wpf 如何自动将布局加载到我的 AvalonDock 实例中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14585556/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-13 07:16:55  来源:igfitidea点击:

How do I automatically load a layout into my AvalonDock instance?

c#wpfmvvmavalondock

提问by Nick Strupat

I have integrated AvalonDock 2.0 into my application. I've bound the document and anchor-able sources to my view-model which are rendered with the proper user controls via DataTemplates.

我已将 AvalonDock 2.0 集成到我的应用程序中。我已将文档和可定位源绑定到我的视图模型,这些视图模型通过DataTemplates使用适当的用户控件呈现。

I can load and save layouts with the XmlLayoutSerializer. I need to support loading predefined layouts on demand (via Buttons and ICommands). That works as well.

我可以使用XmlLayoutSerializer. 我需要支持按需加载预定义的布局(通过Buttons 和ICommands)。这也有效。

The thing I can't get working is loading a serialized layout automatically when the DockingManageris done loading the view-models and their views (user controls).

我无法工作的是在DockingManager完成加载视图模型及其视图(用户控件)后自动加载序列化布局。

I've tried loading my layout on DockingManager.DataContextChangedbut I think it fires too early because the layout loads with the documents in the hidden section and duplicate documents in the visible sections. The rendered panes do not reflect the loaded layout and when the layout is saved again the duplicates are accumulated in the hidden section.

我试过加载我的布局,DockingManager.DataContextChanged但我认为它触发得太早了,因为布局加载了隐藏部分中的文档和可见部分中的重复文档。渲染的窗格不反映加载的布局,当再次保存布局时,重复项会累积在隐藏部分中。

<ad:DockingManager Name="DockingManager"
                   DataContext="{Binding Project}"
                   DataContextChanged="DockingManager_OnDataContextChanged"
                   ActiveContent="{Binding Active}"
                   AnchorablesSource="{Binding Anchorables}"
                   DocumentsSource="{Binding Documents}">
    <ad:DockingManager.Theme>
        <ad:AeroTheme/>
    </ad:DockingManager.Theme>

    <ad:DockingManager.LayoutItemTemplateSelector>
        <views:PanesTemplateSelector>
            <views:PanesTemplateSelector.ChartTemplate>
                <DataTemplate>
                    <views:Chart/>
                </DataTemplate>
            </views:PanesTemplateSelector.ChartTemplate>
            <views:PanesTemplateSelector.WorkspaceTemplate>
                <DataTemplate>
                    <Views:Workspace/>
                </DataTemplate>
            </views:PanesTemplateSelector.WorkspaceTemplate>
            ...
        </views:PanesTemplateSelector>
    </ad:DockingManager.LayoutItemTemplateSelector>

    <ad:DockingManager.LayoutItemContainerStyle>
        <Style TargetType="{x:Type ad:LayoutItem}">
            <Setter Property="Title" Value="{Binding Model.DisplayText}"/>
            <Setter Property="ContentId" Value="{Binding Model.ContentId}"/>
        </Style>
    </ad:DockingManager.LayoutItemContainerStyle>

    <ad:LayoutRoot>
        <!--<ad:LayoutPanel>
            <ad:LayoutDocumentPane/>
            <ad:LayoutAnchorablePane/>
        </ad:LayoutPanel>-->
    </ad:LayoutRoot>
</ad:DockingManager>

... and the code-behind ...

......和代码隐藏......

private void SaveLayout() {
    if (this.DataContext == null)
        return;
    var xmlLayoutSerializer = new XmlLayoutSerializer(this.DockingManager);
    var stringBuilder = new StringBuilder();
    using (var textWriter = new StringWriter(stringBuilder))
        xmlLayoutSerializer.Serialize(textWriter);
    var serialized = stringBuilder.ToString();
    (this.DataContext as dynamic).XmlSerializedAndEscapedLayout = HttpUtility.HtmlEncode(serialized);
}
private void LoadLayout()
{
    if (DataContext == null)
        return;
    var encoded = (DataContext as dynamic).XmlSerializedAndEscapedLayout;
    var window = Window.GetWindow(this);
    window.Closing += (sender, args) => SaveLayout();
    if (String.IsNullOrWhiteSpace(encoded))
        return;
    var serialized = HttpUtility.HtmlDecode(encoded);
    var xmlLayoutSerializer = new XmlLayoutSerializer(DockingManager);
    using (var stringReader = new StringReader(serialized))
        xmlLayoutSerializer.Deserialize(stringReader);
}
private void DockingManager_OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue != null) // A type check here would be best, I know.
        LoadLayout();
}

... And the view-model ...

......还有视图模型......

public class ProjectViewModel
{
    public IModel Model { get; private set; }
    public String SerializedLayout { get; set; }
    public ViewModelBase Active { get; set; }
    private readonly ObservableCollection<ViewModelBase> documents;
    public ReadOnlyObservableCollection<ViewModelBase> Documents { get; private set; }
    private readonly ObservableCollection<ViewModelBase> anchorables;
    public ReadOnlyObservableCollection<ViewModelBase> Anchorables { get; private set; }

    public ProjectViewModel(String filePath, String serializedLayout)
    {
        SerializedLayout = serializedLayout;
        using (var fileStream = new FileStream(filePath, FileMode.Open))
        {
            IModelRepository modelRepository = Ioc.DependencyInjectionContainer.DefaultContainer.Resolve<IModelRepository>();
            Model = modelRepository.Load(fileStream);
        }
        documents = new ObservableCollection<ViewModelBase>();
        anchorables = new ObservableCollection<ViewModelBase>();
        documents.Add(new Workspace());
        anchorables.Add(new RiskLimitsViewModel(Model.RiskLimits));
        ...
        Documents = new ReadOnlyObservableCollection<ViewModelBase>(documents);
        Anchorables = new ReadOnlyObservableCollection<ViewModelBase>(anchorables);
    }
}

Any insight would be greatly appreciated. Thanks!

任何见解将不胜感激。谢谢!

回答by Nick Strupat

I had to add this in the XAML...

我不得不在 XAML 中添加它...

...
</ad:DockingManager.Theme>
<ad:DockingManager.LayoutUpdateStrategy>
    <views:LayoutUpdateStrategy/>
</ad:DockingManager.LayoutUpdateStrategy>

<ad:DockingManager.LayoutItemTemplateSelector>
...

...and this in the code-behind...

...这在代码隐藏...

class LayoutUpdateStrategy : ILayoutUpdateStrategy
{
    private bool BeforeInsertContent(LayoutRoot layout, LayoutContent anchorableToShow)
    {
        var viewModel = (ViewModelBase) anchorableToShow.Content;
        var layoutContent = layout.Descendents().OfType<LayoutContent>().FirstOrDefault(x => x.ContentId == viewModel.ContentId);
        if (layoutContent == null)
            return false;
        layoutContent.Content = anchorableToShow.Content;
        // Add layoutContent to it's previous container
        var layoutContainer = layoutContent.GetType().GetProperty("PreviousContainer", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(layoutContent, null) as ILayoutContainer;
        if (layoutContainer is LayoutAnchorablePane)
            (layoutContainer as LayoutAnchorablePane).Children.Add(layoutContent as LayoutAnchorable);
        else if (layoutContainer is LayoutDocumentPane)
            (layoutContainer as LayoutDocumentPane).Children.Add(layoutContent);
        else
            throw new NotSupportedException();
        return true;
    }
    public bool BeforeInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableToShow, ILayoutContainer destinationContainer)
    {
        return BeforeInsertContent(layout, anchorableToShow);
    }
    public void AfterInsertAnchorable(LayoutRoot layout, LayoutAnchorable anchorableShown) {}
    public bool BeforeInsertDocument(LayoutRoot layout, LayoutDocument anchorableToShow, ILayoutContainer destinationContainer)
    {
        return BeforeInsertContent(layout, anchorableToShow);
    }
    public void AfterInsertDocument(LayoutRoot layout, LayoutDocument anchorableShown) {}
}

回答by user2314510

I used this :

我用过这个:

XAML:

XAML:

<avalonDock:DockingManager 
x:Name="dockManager" 
AllowMixedOrientation="True" 
DocumentClosing="dockManager_DocumentClosing" 
AnchorablesSource="{Binding ListUserPanelAnchorable}"
DocumentsSource="{Binding ListUserPanel}"
Theme="{Binding avalondockTheme}">

Model :

模型 :

public ObservableCollection<UserPanel> _ListUserPanelAnchorable;
ReadOnlyObservableCollection<UserPanel> _readonyFiles = null;
public ReadOnlyObservableCollection<UserPanel> ListUserPanelAnchorable
{
    get
    {
        if (_readonyFiles == null)
            _readonyFiles = new ReadOnlyObservableCollection<UserPanel>(_ListUserPanelAnchorable);

        return _readonyFiles;
    }
}

xaml.cs:

xaml.cs:

private void loadLayout()
{
    //dockPanelModel.ListUserPanel.Clear();
    //dockPanelModel.ListUserPanelAnchorable.Clear();
    var serializer = new XmlLayoutSerializer(dockManager);
    serializer = new XmlLayoutSerializer(dockManager);
    serializer.LayoutSerializationCallback += (s, args) =>
    {
        args.Content = userPanel;
                dockPanelModel._ListUserPanelAnchorable.Add(userPanel);
    }
}

public AvaladonDockPanel()
{
    InitializeComponent();
    this.Loaded += AvaladonDockPanel_Loaded;                
}       

void AvaladonDockPanel_Loaded(object sender, RoutedEventArgs e)
{
    loadLayout();          
}

userpanel:

用户面板:

public class UserPanel
{
    public string Title { get; set; }
    public string ToolTip { get; set; }
    public string IconSource { get; set; }

    private Guid _contentGuid = Guid.NewGuid();
    public Guid ContentId
    {
        get { return _contentGuid; }
        set { _contentGuid = value; }
    }


    private UserControl _UserInterface;
    public UserControl UserInterface { get { return _UserInterface; } set { _UserInterface = value; NotifyPropertyChanged("UserInterface"); } }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            Debug.WriteLine(string.Format("PropertyChanged-> {0}", propertyName));
        }
    }
}

binding avalon to userpanel:

将 Avalon 绑定到用户面板:

<avalonDock:DockingManager.LayoutItemContainerStyle>
    <Style TargetType="{x:Type avalonDock:LayoutItem}">
        <Setter Property="Title" Value="{Binding Model.Title}"/>
        <Setter Property="ToolTip" Value="{Binding Model.ToolTip}"/>
        <Setter Property="IconSource" Value="{Binding Model.IconSource}" />
        <Setter Property="ContentId" Value="{Binding Model.ContentId}"/>
    </Style>
</avalonDock:DockingManager.LayoutItemContainerStyle>
<avalonDock:DockingManager.LayoutItemTemplate >
    <DataTemplate >
        <ContentPresenter Content="{Binding UserInterface}"  />
    </DataTemplate>
</avalonDock:DockingManager.LayoutItemTemplate>