绑定AvalonDock LayoutAnchorable IsVisible属性 [英] Binding an AvalonDock LayoutAnchorable IsVisible property

查看:56
本文介绍了绑定AvalonDock LayoutAnchorable IsVisible属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图将AvalonDock LayoutAnchorables绑定到WPF中的相应菜单项.如果在菜单中选中,则可锚定项应可见.如果未在菜单中选中,则应隐藏可锚定对象.

I am trying to bind AvalonDock LayoutAnchorables to their respective menu items in WPF. If checked in the menu the anchorable should be visible. If not checked in the menu, the anchorable should be hidden.

IsCheckedIsVisible都是布尔值,因此我不希望需要转换器.我可以将LayoutAnchorable IsVisible属性设置为TrueFalse,并且行为在设计视图中是预期的.

Both IsChecked and IsVisible are boolean so I wouldn't expect a converter to be required. I can set the LayoutAnchorable IsVisible property to True or False, and behavior is as expected in the design view.

但是,如果尝试按以下方式实现绑定,则会收到错误消息

However, if trying to implement binding as below I get the error

不能在类型的"IsVisible"属性上设置"Binding" "LayoutAnchorable".只能在 DependencyObject的DependencyProperty.

'Binding' cannot be set on the 'IsVisible' property of type 'LayoutAnchorable'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.

问题在这里:

<dock:LayoutAnchorable ContentId="content1" IsVisible="{Binding IsChecked, ElementName=mnuPane1}" x:Name="anchorable1" IsSelected="True">

我该怎么做?

<Window x:Class="TestAvalonBinding.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:dock="http://schemas.xceed.com/wpf/xaml/avalondock"
    mc:Ignorable="d"
    Title="MainWindow"
    Height="450"
    Width="800">
<Grid>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <!-- Menu -->
    <Menu Height="18" HorizontalAlignment="Stretch" Name="menu1" VerticalAlignment="Top" Grid.Row="0">
        <MenuItem Header="File">
            <MenuItem Header="_Foo1" Name="mnuPane1" IsCheckable="True">
            </MenuItem>
            <MenuItem Header="Foo2" Name="mnuPane2" IsCheckable="True">
            </MenuItem>
        </MenuItem>
    </Menu>

    <!-- AvalonDock -->
    <dock:DockingManager x:Name="Dockman" DockPanel.Dock="Left" Grid.Row="1" >

        <dock:LayoutRoot x:Name="_layoutRoot">
            <dock:LayoutPanel Orientation="Horizontal">
                <dock:LayoutAnchorablePaneGroup Orientation="Vertical">
                    <dock:LayoutAnchorablePane FloatingWidth="150" FloatingHeight="150" FloatingLeft="100" FloatingTop="300">
                        <dock:LayoutAnchorable ContentId="content1" IsVisible="{Binding IsChecked, ElementName=mnuPane1}" x:Name="anchorable1" IsSelected="True">
                            <GroupBox Header="Foo1"/>
                        </dock:LayoutAnchorable>
                    </dock:LayoutAnchorablePane>
                    <dock:LayoutAnchorablePane FloatingWidth="150" FloatingHeight="150" FloatingLeft="100" FloatingTop="300">
                        <dock:LayoutAnchorable ContentId="content2" x:Name="anchorable2" IsSelected="True">
                            <GroupBox Header="Foo2"/>
                        </dock:LayoutAnchorable>
                    </dock:LayoutAnchorablePane>
                </dock:LayoutAnchorablePaneGroup>
            </dock:LayoutPanel>
        </dock:LayoutRoot>
    </dock:DockingManager>
    
</Grid>
</Window>

更新:

我对BionicCode的回答的实现.我剩下的问题是,如果我关闭窗格,则菜单项保持选中状态.

My implementation of BionicCode's answer. My remaining issue is that if I close a pane, the menu item remains checked.

XAML

<Grid>

    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <!-- Menu -->
    <Menu Height="18" HorizontalAlignment="Stretch" Name="menu1" VerticalAlignment="Top" Grid.Row="0">
        <MenuItem Header="File">
            <MenuItem Header="_Foo1" Name="mnuPane1" IsCheckable="True" IsChecked="{Binding RelativeSource={RelativeSource AncestorType=local:MainWindow}, Path=IsAnchorable1Visible}"/>
            <MenuItem Header="Foo2" Name="mnuPane2" IsCheckable="True" IsChecked="{Binding RelativeSource={RelativeSource AncestorType=local:MainWindow}, Path=IsAnchorable2Visible}"/>
        </MenuItem>
    </Menu>

    <!-- AvalonDock -->
    <dock:DockingManager x:Name="Dockman" DockPanel.Dock="Left" Grid.Row="1" >
        <dock:LayoutRoot x:Name="_layoutRoot">
            <dock:LayoutPanel Orientation="Horizontal">
                <dock:LayoutAnchorablePaneGroup Orientation="Vertical">
                    <dock:LayoutAnchorablePane FloatingWidth="150" FloatingHeight="150" FloatingLeft="100" FloatingTop="300">
                        <dock:LayoutAnchorable ContentId="content1" x:Name="anchorable1" IsSelected="True" >
                            <GroupBox Header="Foo1"/>
                        </dock:LayoutAnchorable>
                    </dock:LayoutAnchorablePane>
                    <dock:LayoutAnchorablePane FloatingWidth="150" FloatingHeight="150" FloatingLeft="100" FloatingTop="300">
                        <dock:LayoutAnchorable ContentId="content2" x:Name="anchorable2" IsSelected="True" >
                            <GroupBox Header="Foo2"/>
                        </dock:LayoutAnchorable>
                    </dock:LayoutAnchorablePane>
                </dock:LayoutAnchorablePaneGroup>
            </dock:LayoutPanel>
        </dock:LayoutRoot>
    </dock:DockingManager>

</Grid>

背后的代码

partial class MainWindow : Window
{
    public static readonly DependencyProperty IsAnchorable1VisibleProperty = DependencyProperty.Register(
      "IsAnchorable1Visible",
      typeof(bool),
      typeof(MainWindow),
      new PropertyMetadata(default(bool), MainWindow.OnIsAnchorable1VisibleChanged));

    public static readonly DependencyProperty IsAnchorable2VisibleProperty = DependencyProperty.Register(
      "IsAnchorable2Visible",
      typeof(bool),
      typeof(MainWindow),
      new PropertyMetadata(default(bool), MainWindow.OnIsAnchorable2VisibleChanged));

    public bool IsAnchorable1Visible
    {
        get => (bool)GetValue(MainWindow.IsAnchorable1VisibleProperty);
        set => SetValue(MainWindow.IsAnchorable1VisibleProperty, value);
    }
    public bool IsAnchorable2Visible
    {
        get => (bool)GetValue(MainWindow.IsAnchorable2VisibleProperty);
        set => SetValue(MainWindow.IsAnchorable2VisibleProperty, value);
    }

    public MainWindow()
    {
        InitializeComponent();
        this.IsAnchorable1Visible = true;
        this.IsAnchorable2Visible = true;
    }

    private static void OnIsAnchorable1VisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as MainWindow).anchorable1.IsVisible = (bool)e.NewValue;
    }
    private static void OnIsAnchorable2VisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as MainWindow).anchorable2.IsVisible = (bool)e.NewValue;
    }
}

推荐答案

AvalonDock XAML布局元素既不是控件也不是UIElement的派生.它们充当普通模型(尽管它们扩展了DependencyObject).
LayoutAnchorable的属性未实现为DependencyProperty,而是实现了INotifyPropertyChanged(如前所述,布局元素用作控件的视图模型).因此,他们不支持数据出价(作为绑定目标).

The AvalonDock XAML layout elements are neither controls nor derived of UIElement. They serve as plain models (although they extend DependencyObject).
The properties of LayoutAnchorable are not implemented as DependencyProperty, but instead implement INotifyPropertyChanged (as said before, the layout elements serve as the control's view model). Hence they don't support data biding (as binding target).

这些XAML布局元素中的每一个都有一个对应的控件,该控件实际上将以layout元素呈现为DataContext.名称等于带有后缀 Control 的布局元素的名称.如果要将这些控件或项目容器(例如LayoutAnchorableItem)连接到视图模型,则必须创建一个以该容器为目标的Style.下一个缺陷是此容器的DataContext不是控件要显示的数据模型,而是控件的内部模型.要访问您的视图模型,您需要访问例如LayoutAnchorableControl.LayoutItem.Model(因为LayoutAnchorableControl.DataContextLayoutAnchorable).

Each of those XAML layout elements has a corresponding control which will be actually rendered with the layout element as DataContext. The names equal the layout element's name with the Control suffix attached. If you want to connect those controls or item containers e.g., LayoutAnchorableItem to your view model, you'd have to create a Style that targets this container. The next flaw is that the DataContext of this containers is not your data model that the control is intended to display, but the control's internal model. To get to your view model you would need to access e.g. LayoutAnchorableControl.LayoutItem.Model (because the LayoutAnchorableControl.DataContext is the LayoutAnchorable).

作者显然很迷失,因为他们太渴望使用MVVM实现 control 本身(如他们的文档所述),而忘记了以MVVM客户端 application 为目标.他们打破了常见的WPF模式.在外部看起来不错,但在内部看起来不太好.

The authors obviously got lost while being too eager to implement the control itself using MVVM (as stated in their docs) and forget to target the MVVM client application. They broke the common WPF pattern. Looks good on the outside, but not so good on the inside.

要解决您的问题,必须在视图上引入一个中间依赖项属性.然后,已注册属性更改的回调将委派可见性以切换可定位对象的可见性.
还需要注意的是,AvalonDock的作者没有使用UIElement.Visibility来处理可见性.他们引入了独立于框架属性的自定义可见性逻辑.

To solve your problem, you have to introduce an intermediate dependency property on your view. A registered property changed callback would then delegate the visibility to toggle the visibility of the anchorable.
It's also important to note that the authors of AvalonDock didn't use the UIElement.Visibility to handle visibility. They introduced a custom visibility logic independent of the framework property.

如前所述,始终存在纯模型驱动的方法,您可以通过提供ILayoutUpdateStrategy实现来布局初始视图.然后,您可以定义样式以连接视图和视图模型.使用XAML布局元素对视图进行硬编码会在更高级的场景中带来某些不便.

As mentioned before, there is always the pure model driven approach, where you layout the initial view by providing a ILayoutUpdateStrategy implementation. You then define styles to wire up view and view models. Hardcoding the view using the XAML layout elements leads to certain inconvenience in more advanced scenarios.

LayoutAnchorable公开Show()Close()方法或IsVisible属性以处理可见性.您还可以在访问LayoutAnchorableControl.LayoutItem时绑定到命令(例如,从ControlTemplate内部访问),该命令将返回LayoutAnchorableItem.该LayoutAnchorableItem公开了HideCommand.

LayoutAnchorable exposes a Show() and Close() method or the IsVisible property to handle visibility. You can also bind to a command when accessing LayoutAnchorableControl.LayoutItem (e.g. from within a ControlTemplate), which returns a LayoutAnchorableItem. This LayoutAnchorableItem exposes a HideCommand.

MainWindow.xaml

<Window>
  <Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <!-- Menu -->
    <Menu Grid.Row="0">
      <MenuItem Header="File">
        <MenuItem Header="_Foo1" 
                  IsCheckable="True"
                  IsChecked="{Binding RelativeSource={RelativeSource AncestorType=MainWindow}, Path=IsAnchorable1Visible}" />
      </MenuItem>
    </Menu>

    <!-- AvalonDock -->
    <dock:DockingManager Grid.Row="1" >
      <dock:LayoutRoot>
        <dock:LayoutPanel>
          <dock:LayoutAnchorablePaneGroup>
            <dock:LayoutAnchorablePane>
              <dock:LayoutAnchorable x:Name="Anchorable1"
                                     Hidden="Anchorable1_OnHidden">
                <GroupBox Header="Foo1" />
              </dock:LayoutAnchorable>
            </dock:LayoutAnchorablePane>
          </dock:LayoutAnchorablePaneGroup>
        </dock:LayoutPanel>
      </dock:LayoutRoot>
    </dock:DockingManager>    
  </Grid>
</Window>

MainWindow.xaml.cs

partial class MainWindow : Window
{
  public static readonly DependencyProperty IsAnchorable1VisibleProperty = DependencyProperty.Register(
    "IsAnchorable1Visible",
    typeof(bool),
    typeof(MainWindow),
    new PropertyMetadata(default(bool), MainWindow.OnIsAnchorable1VisibleChanged));

  public bool IsAnchorable1Visible
  {
    get => (bool) GetValue(MainWindow.IsAnchorable1VisibleProperty);
    set => SetValue(MainWindow.IsAnchorable1VisibleProperty, value);
  }

  public MainWindow()
  {
    InitializeComponent();
    this.IsAnchorable1Visible = true;
  }

  private static void OnIsAnchorable1VisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  { 
    (d as MainWindow).Anchorable1.IsVisible = (bool) e.NewValue;
  }

  private void Anchorable1_OnHidden(object sender, EventArgs e) => this.IsAnchorable1Visible = false;
}

这篇关于绑定AvalonDock LayoutAnchorable IsVisible属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆