Error Changing View Layout

I have this method to change the layout of a created View, where I remove a child item from a ConfigItem (a group of Views) and then proceed to create a new View with a configured layout and associated cameras.

In the 2023R3 version it was working, but I’ve tested in the 2024R1 version and I get the following error in the MIP logs:

Debug: MipNotifyWindowClose::MessageId.SmartClient.SelectedViewChangedIndication ():Unable to locate window info by WindowId., ffb3358c-1de1-4a5f-ae42-bbb233fac729

Is there an update on the methods tied to creating/removing Views and InsertBuiltInViewItem or MultiWindowCommand, that I should change?

I would like to see if I can reproduce your observation here in the Milestone test lab. I have some questions.

Where in the Smart Client (what class) is the code running? Is the view open while be changed? If yes, is it open in the Main view, floating view or something else?

Could you give me a minimum implementation plugin that reproduce the issue? Alternatively share a snippet of code with instruction on where and how the code can be used?

Sure! I should have put some code implementation when I opened :sweat_smile:

Here are some clarifications and some snippets to help you reproduce:

  • The code is running in the BackgroundPlugin;
  • The View I want to close is in a Secondary Display in a fullscreen window;
  • The procedure is that the previous View is open in that display, I send a command that closes it/creates a copy of it with a different layout/opens it in the same display;

Here is the code I’m using to open it:

// Handles the background window change event
internal static object Handler(Message message, FQID dest, FQID source)
{
    // Changing the view layout requires us to get the state of the windows
    WindowData windowData = message.Data;
 
    // Check if the request is to set the window
    if (windowData.Request == SetWindow)
    {
        try
        {
            // Fetch the view object based on ViewGroupObjectId and ViewObjectId
            ViewAndLayoutItem viewObject = GetViewObject(windowData.ViewGroupObjectId, windowData.ViewObjectId);
 
            // Reuse the same view name
            Item previousView = viewObject;
 
            // Collect the cameras for the View
            Dictionary<int, string> collectedCameras = !string.IsNullOrEmpty(windowData.CameraGroup)
                ? JsonConvert.DeserializeObject<Dictionary<int, string>>(windowData.CameraGroup)
                : CollectPreviousViewCameras(previousView);
 
            ConfigItem group = (ConfigItem)Configuration.Instance.GetItemsByKind(Kind.View).First(item => item.FQID.ObjectId == windowData.ViewGroupObjectId).GetChildren()[0];
 
            // Update the View
            Task.Run(async () =>
            {
                await Task.Delay(1000);
                await Application.Current.Dispatcher.InvokeAsync(() => group.RemoveChild(viewObject));
                UpdateView(group, collectedCameras, previousView, windowData.NewLayout, windowValue);
            });
        }
        catch (Exception ex)
        {
            LogError(ex);
        }
    }
 
    return null;
}
 
private static async void UpdateView(ConfigItem groupItem, Dictionary<int, string> cameras, Item previousView, Guid newLayout, int windowId)
{
    Action updateView = () =>
    {
        // Rebuild the view with the new layout and add cameras
        ViewAndLayoutItem newViewAndLayoutItem = (ViewAndLayoutItem)groupItem.AddChild(previousView.Name, Kind.View, FolderType.No);
        // Create layout from the layout ObjectId
		newViewAndLayoutItem.Layout = ParseViewLayoutXml(newLayout);
 
        foreach (var indexCamera in cameras)
        {
            // InsertBuiltinViewItem method
            AddCameraToView(indexCamera, newViewAndLayoutItem);
        }
 
        // Save the View
        newViewAndLayoutItem.Save();
    };
 
    await Application.Current.Dispatcher.InvokeAsync(updateView);
    OpenWindow(groupItem, previousView, windowId, newLayout);
}
 
private static async void OpenWindow(ConfigItem groupItem, Item previousView, int screen, Guid newLayout)
{
    try
    {
        // Find the created view and open it in a new window
        Item createdView = groupItem.GetChildren().Find(item => item.Name == previousView.Name);
        Action createWindow = () => 
        {
            MultiWindowCommandData nextWindowData = new MultiWindowCommandData()
            {
// screenId is zero sets the view in MainWindow or else open Fullscreen
                MultiWindowCommand = screen == 0 ? MultiWindowCommand.SetViewInWindow : MultiWindowCommand.OpenFullScreenWindow,
                Screen = Configuration.Instance.GetItemsByKind(Kind.Screen).First(item => item.FQID.ObjectIdString == screen.ToString()).FQID,
                Window = Configuration.Instance.GetItemsByKind(Kind.Window).First(item => item.FQID.ObjectId == item.FQID.Kind).FQID,
                View = createdView.FQID,
            };
            EnvironmentManager.Instance.SendMessage(new Message(MessageId.SmartClient.MultiWindowCommand, nextWindowData), null, null);
        };
        await Application.Current.Dispatcher.InvokeAsync(createWindow);
    }
    catch (Exception ex)
    {
        LogError(ex);
    }
}

My effort to try to reproduce did not succeed. I have some questions.

My guess is that you have a RegisterReceiver that trigger the Handler. One guess could be -

_msgRef = EnvironmentManager.Instance.RegisterReceiver(Handler, new MessageIdFilter(VideoOS.Platform.Messaging.MessageId.SmartClient.SelectedViewChangedIndication));

Please give me the details on what you use.

Your code starts by using a WindowData type, I haven’t been able to guess how that might be defined. Can you please look at your code and either detail methods so I can use the code or alternatively make a simpler code that would still show the issue (if possible).