Avalondock — отложенное восстановление макета для некоторых панелей.

Я использую макет загрузки/сохранения, аналогичный описанному в КодПроект. Перехват события LayoutSerializationCallback и попытка найти соответствующий viewModel для LayoutItem

private void LayoutSerializer_LayoutSerializationCallback(object sender, LayoutSerializationCallbackEventArgs e)
        {
            // This can happen if the previous session was loading a file
            // but was unable to initialize the view ...
            if (string.IsNullOrWhiteSpace(e.Model.ContentId) || (e.Content = ReloadItem(e.Model)) == null)
            {
                e.Cancel = true;
                return;
            }
        }

private object ReloadItem(object item)
{
    object ret = null;

    switch (item)
    {
        case LayoutAnchorable anchorable:
            //list of tools windows
            ret = Manager.Tools.FirstOrDefault(i => i.ContentId == anchorable.ContentId);
            if(ret == null && anchorable.ContentId.StartsWith(MapPanel.MapPanelPrefix))
            {

                MapPanels.Add(anchorable);
            }
            break;
        case LayoutDocument document:
            // list of restored documents
            ret = Manager.Documents.FirstOrDefault(i => i.ContentId == document.ContentId);
            break;
        default:
            throw new NotImplementedException("Not implemented type of AD item ");
    }

    return ret;

}

Это отлично работает, когда у меня есть все ViewModels, доступные при десериализации/восстановлении макета.

Но я думаю о чем-то вроде отложенного восстановления макета. В моем случае у меня есть некоторые документы и несколько панелей, доступных в начале. Но могут быть некоторые панели (назовем их MapPanel), которые загружаются позже (модели представления загружаются где-то в будущем). И я не могу понять, как восстановить расположение этих панелей.

Для этого случая у меня есть List MapPanels для хранения привязок, которые загружаются при загрузке макета avalondock, и пытаюсь восстановить их в BeforeInsertAnchorable в ILayoutUpdateStrategy. Но когда я отлаживаю его, у сохраненного LayoutAnchorable есть разные родители, которые его сохранили. Поэтому я предполагаю, что после отмены (e.Cancel = true) в LayoutSerializationCallback каким-то образом модифицируется не восстановленная привязка.

public bool BeforeInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableToShow, ILayoutContainer destinationContainer)
        {
            if (anchorableToShow.Content is ToolPanel tool)
            {

                if(tool is MapPanel)
                {
                    anchorableToShow = LayoutSaveLoadUtil.Instance.MapPanels.FirstOrDefault(mp => mp.ContentId == anchorableToShow.ContentId);
                }

                var destPane = destinationContainer as LayoutAnchorablePane;
                if (destinationContainer != null && destinationContainer.FindParent<LayoutFloatingWindow>() != null)
                    return false;


                var dockLeftPane = layout.Descendents().OfType<LayoutAnchorablePane>().FirstOrDefault(d => d.Name == tool.PreferredLocation + "Pane");
                if (dockLeftPane != null)
                {
                    dockLeftPane.Children.Add(anchorableToShow);
                    return true;
                }

                return false;
            }

            return false;

        }

Поэтому мне любопытно, каков правильный подход для достижения этого. Я также думал о восстановлении макета (снова) после загрузки MapPanel, но я не знаю, как пропустить все остальные элементы макета. Итак, есть ли возможность восстановить одну позицию Anchorable, плавающий родитель, стыковку, размер и т. Д.?


person Luboš Suk    schedule 25.02.2020    source источник


Ответы (1)


Так что я, вероятно, понял решение.

У меня есть несколько панелей (назовем их MapPanel), и их содержимое не загружается при восстановлении макета из XML. В моем случае у меня есть приложение, в котором есть несколько документов и вкладок, и, кроме того, пользователь может загружать дополнительные данные для отображения карт.

И мне нужно было восстановить макет этих карт, когда пользователь их загружает. (нажмите кнопку, выберите, где загрузить карты и т. д.)

У меня есть статический класс с именем LayoutSaveLoadUtil (как описано в codeproject), когда у меня есть дополнительный список MapPanelsStorage типа LayoutAnchorable. В этот список я сохраняю все макеты, в которых отсутствует содержимое при восстановлении макета, а ContentId имеет определенный префикс. Это говорит мне, что это MapPanel. Затем я создаю фиктивный контент, назначаю его этой панели и устанавливаю для его видимости значение false (поэтому панель невидима).

(это вызывается в layoutSerializationCallback)

private object ReloadItem(object item)
{
    object ret = null;

    switch (item)
    {
        case LayoutAnchorable anchorable:
            //list of tools windows
            ret = Manager.Tools.FirstOrDefault(i => i.ContentId == anchorable.ContentId);
            if(ret == null && anchorable.ContentId.StartsWith(MapPanel.MapPanelPrefix))
            {
                //when layoutAnchorable is MapPanel (has MapPanel prefix) and its content is loaded yet
                //store its layout into list to restore it later (when content is loaded)
                MapPanelsStorage.Add(anchorable);
                //Set anchorable visibility to false 
                anchorable.IsVisible = false;
                //return dummy model for avalondock layout serialization
                ret = new EmptyMapViewModel();
            }
            break;
        case LayoutDocument document:
            // list of restored documents
            ret = Manager.Documents.FirstOrDefault(i => i.ContentId == document.ContentId);
            break;
        default:
            throw new NotImplementedException("Not implemented type of AD item ");
    }

    return ret;

}

Затем в BeforeInsertAnchorable (вызывается при добавлении новой панели в макет) я проверяю, является ли содержимое панели MapPanel, ищу сохраненный макет, пытаюсь найти родителя (LayoutAnchorablePane/LayoutDocumentPane), а затем добавляю его вместо панели DummyHidden и просто удаляю его из хранилища. .

//hacky hacky to restore map panel layout when map is opened after layout is loaded
//in layout deserialization, deserialize layout for MapPanels that hasnt DataModels yet with DummyModel to preserve their layout
if (anchorableToShow.Content is MapPanel mappanel)
{
    var storedMapsLayout = LayoutSaveLoadUtil.Instance.MapPanelsStorage;

    //check if Map panel has stored layout from previous layout deserialization
    var matchingAnchorable = storedMapsLayout.FirstOrDefault(m => m.ContentId == mappanel.ContentId);
    if (matchingAnchorable != null)
    {
        //make preserved layout visible, so its parent and etc is restored. Without this, correct parent LayoutGroup isnt found
        matchingAnchorable.IsVisible = true;

        LayoutAnchorablePane matchingAnchorablePane;
        LayoutDocumentPane matchingDocumentPane;

        //find parent layoutGroup. This can be LayoutAnchorablePane or LayoutDocumentPane
        if ((matchingAnchorablePane = matchingAnchorable.FindParent<LayoutAnchorablePane>()) != null)
        {
            //add new panel into layoutGroup with correct layout
            matchingAnchorablePane.Children.Add(anchorableToShow);
            //remove old dummy panel
            matchingAnchorablePane.RemoveChild(matchingAnchorable);
            //remove restored layout from storage
            storedMapsLayout.Remove(matchingAnchorable);
            return true;
        }
        else if ((matchingDocumentPane = matchingAnchorable.FindParent<LayoutDocumentPane>()) != null)
        {
            //add new panel into layoutGroup with correct layout
            matchingDocumentPane.Children.Add(anchorableToShow);
            //remove old dummy panel
            matchingDocumentPane.RemoveChild(matchingAnchorable);
            //remove restored layout from storage
            storedMapsLayout.Remove(matchingAnchorable);
            return true;
        }
        else
        {
            matchingAnchorable.IsVisible = false;
        }
    }
}
person Luboš Suk    schedule 05.03.2020