c# - 绑定(bind) AvalonDock LayoutAnchorable IsVisible 属性

标签 c# wpf binding

我正在尝试将 AvalonDock LayoutAnchorables 绑定(bind)到 WPF 中它们各自的菜单项。如果在菜单中选中, anchor 应该是可见的。如果未在菜单中选中,应隐藏 anchor 。

IsCheckedIsVisible 都是 bool 值,所以我认为不需要转换器。我可以将 LayoutAnchorable IsVisible 属性设置为 TrueFalse,并且行为符合设计 View 中的预期。

但是,如果尝试按如下方式实现绑定(bind),则会出现错误

'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 答案的实现。我剩下的问题是,如果我关闭一个 Pane ,菜单项将保持选中状态。

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(如前所述,布局元素充当控件的 View 模型) .因此,它们不支持数据出价(作为绑定(bind)目标)。

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

作者显然迷失了方向,因为他们太急于使用 MVVM 实现控制本身(如他们的文档中所述),而忘记以 MVVM 客户端应用程序为目标。他们打破了常见的 WPF 模式。外面看起来不错,但里面不太好。

要解决您的问题,您必须在 View 中引入一个中间依赖属性。注册的属性更改回调将委托(delegate)可见性以切换可锚定的可见性。
同样重要的是要注意 AvalonDock 的作者没有使用 UIElement.Visibility 来处理可见性。他们引入了独立于框架属性的自定义可见性逻辑。

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

LayoutAnchorable 公开了一个 Show()Close() 方法或 IsVisible 属性来处理可见性。您还可以在访问 LayoutAnchorableControl.LayoutItem 时绑定(bind)到命令(例如,从 ControlTemplate 中),这会返回一个 LayoutAnchorableItem。此 LayoutAnchorableItem 公开了一个 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;
}

关于c# - 绑定(bind) AvalonDock LayoutAnchorable IsVisible 属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63645164/

相关文章:

ios - Xamarin - 构建绑定(bind)库 Messaging.g.cs 时出错

c# - 从 .NET DLL 文件生成 PDB?

c# - 动态读取文本文件的更改 C#

c# - WPF 组合框作为 System.Windows.Media.Colors

c# - 单击子元素时如何阻止父元素的事件

javascript - Angularjs在js中绑定(bind)动态表单

c# - 如何在 xamarin 中添加 System.Drawing?

c# - 为什么 LINQ 返回的列值不是预期的?

C# MVVM 如何动态创建 View

c# - WPF组合框怪异问题