我正在尝试将 AvalonDock LayoutAnchorables
绑定(bind)到 WPF 中它们各自的菜单项。如果在菜单中选中, anchor 应该是可见的。如果未在菜单中选中,应隐藏 anchor 。
IsChecked
和 IsVisible
都是 bool 值,所以我认为不需要转换器。我可以将 LayoutAnchorable
IsVisible
属性设置为 True
或 False
,并且行为符合设计 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.DataContext
是 LayoutAnchorable
)。
作者显然迷失了方向,因为他们太急于使用 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/