首页 > 解决方案 > 绑定 AvalonDock LayoutAnchorable IsVisible 属性

问题描述

我正在尝试将 AvalonDock 绑定LayoutAnchorables到 WPF 中它们各自的菜单项。如果在菜单中选中,anchorable 应该是可见的。如果未在菜单中选中,则应该隐藏可锚定对象。

两者IsCheckedIsVisible都是布尔值,所以我不希望需要转换器。我可以将LayoutAnchorable IsVisible属性设置为Trueor False,并且行为与设计视图中的预期一致。

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

不能在“LayoutAnchorable”类型的“IsVisible”属性上设置“绑定”。只能在 DependencyObject 的 DependencyProperty 上设置“绑定”。

问题在这里:

<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 答案的实现。我剩下的问题是,如果我关闭一个窗格,菜单项仍然处于选中状态。

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;
    }
}

标签: c#wpfbinding

解决方案


您的绑定有两个主要问题。

  1. IsVisible属性不是一个DependencyProperty,而只是一个 CLR 属性,所以你不能绑定它
  2. ALayoutAnochorable不是可视树的一部分,因此ElementName绑定RelativeSource不起作用,您将在输出窗口中看到相应的绑定错误

我不确定是否有特定的设计选择或限制使该IsVisible属性成为依赖属性,但您可以通过创建附加属性来解决此问题。此属性可以绑定并在更改时设置 CLRIsVisible属性LayoutAnchorable

public class LayoutAnchorableProperties
{
   public static readonly DependencyProperty IsVisibleProperty = DependencyProperty.RegisterAttached(
      "IsVisible", typeof(bool), typeof(LayoutAnchorableProperties), new PropertyMetadata(true, OnIsVisibleChanged));

   public static bool GetIsVisible(DependencyObject dependencyObject)
   {
      return (bool)dependencyObject.GetValue(IsVisibleProperty);
   }

   public static void SetIsVisible(DependencyObject dependencyObject, bool value)
   {
      dependencyObject.SetValue(IsVisibleProperty, value);
   }

   private static void OnIsVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
   {
      if (d is LayoutAnchorable layoutAnchorable)
         layoutAnchorable.IsVisible = (bool)e.NewValue;
   }
}

您可以在 XAML 中绑定此属性,但正如所说,这将不起作用,因为它LayoutAnchorable不在可视树中。列也会出现同样的问题DataGrid。在这篇相关文章中,您可以找到BindingProxy我们将使用的类的解决方法。请将此类复制到您的项目中。

在您的DockingManager.Resources. 它用于访问菜单项。

<dock:DockingManager x:Name="Dockman" DockPanel.Dock="Left" Grid.Row="1">
   <dock:DockingManager.Resources>
      <local:BindingProxy x:Key="mnuPane1Proxy" Data="{Binding ElementName=mnuPane1}"/>
   </dock:DockingManager.Resources>
   <!-- ...other XAML code. -->
</dock:DockingManager>

删除旧IsVisible绑定。使用 . 将绑定添加到附加属性mnuPane1Proxy

<xcad:LayoutAnchorable ContentId="content1"
                       x:Name="anchorable1"
                       IsSelected="True"
                       local:LayoutAnchorableProperties.IsVisible="{Binding Data.IsChecked, Source={StaticResource mnuPane1Proxy}}">

最后,将IsChecked菜单项中的默认状态设置为true,因为这是默认状态,IsVisible并且由于在附加属性中设置默认值,绑定不会在初始化时更新,这是为了防止InvalidOperationException由于控件而引发的没有完全初始化。

<MenuItem Header="_Foo1" Name="mnuPane1" IsCheckable="True" IsChecked="True">

推荐阅读