问题
I am attempting to bind the Visibility
of LayoutAnchorableItem
to a boolean in the ViewModel so that I can programmatically show and hide the anchorable:
<UserControl.Resources>
<avalon:BoolToVisibilityConverter x:Key="btvc"/>
</UserControl.Resources>
<avalon:DockingManager>
<avalon:DockingManager.LayoutItemContainerStyleSelector>
<ws:WorkspaceStyleSelector>
<ws:WorkspaceStyleSelector.AnchorableStyle>
<Style TargetType="{x:Type avalon:LayoutAnchorableItem}">
<!-- ... -->
<Setter Property="Visibility" Value="{Binding Model.IsVisible, Converter={StaticResource btvc}, Mode=TwoWay}"/>
</Style>
</ws:WorkspaceStyleSelector.AnchorableStyle>
</ws:WorkspaceStyleSelector>
</avalon:DockingManager.LayoutItemContainerStyleSelector>
<!-- ... -->
</avalon:DockingManager>
However, whenever I hide an anchorable, an exception is thrown:
Object reference is not set to an instance.
at Xceed.Wpf.AvalonDock.Layout.LayoutContent.Close() in ...\Xceed.Wpf.AvalonDock\Layout\LayoutContent.cs:line 346
at Xceed.Wpf.AvalonDock.Controls.LayoutItem.OnVisibilityChanged() in ...\Xceed.Wpf.AvalonDock\Controls\LayoutItem.cs:line 310
at Xceed.Wpf.AvalonDock.Controls.LayoutAnchorableItem.OnVisibilityChanged() in ...\Xceed.Wpf.AvalonDock\Controls\LayoutAnchorableItem.cs:line 299
at Xceed.Wpf.AvalonDock.Controls.LayoutItem.OnVisibilityChanged(DependencyObject s, DependencyPropertyChangedEventArgs e) in ...\Xceed.Wpf.AvalonDock\Controls\LayoutItem.cs:line 303
at Xceed.Wpf.AvalonDock.Controls.LayoutItem.<.cctor>b__1(DependencyObject s, DependencyPropertyChangedEventArgs e) in ...\Xceed.Wpf.AvalonDock\Controls\LayoutItem.cs:line 37
at System.Windows.PropertyChangedCallback.Invoke(DependencyObject d, DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
...
After commenting out the binding to visibility, the anchorable is hidden as expected.
回答1:
tl;dr
You need to add a ConverterParameter
of value Visibility.Hidden
to the Binding:
<Setter Property="Visibility" Value="{Binding Model.IsVisible, ConverterParameter={x:Static Visibility.Hidden}, Converter={StaticResource btvc}, Mode=TwoWay}"/>
The converter parameter is the Visibility
that is returned when the boolean is false
, and Hidden
means the anchorable is hidden.
Full Answer
If we look at LayoutContent.Close()
, it is marked with the comment:
Please note that usually the anchorable is only hidden (not closed). By default when user click the X button it only hides the content.
So this should not have been called. Looking at the stacktrace, this is called from:
// LayoutItem class.
protected virtual void OnVisibilityChanged()
{
if (LayoutElement != null &&
Visibility == System.Windows.Visibility.Collapsed)
LayoutElement.Close();
}
According to Microsoft, System.Windows.Visibility.Collapsed
means that the item is not visible and space is not reserved for it during layout. This sounds like what is happening with the anchorables when we click the X
to hide them (and this is probably happening somewhere up the visual tree). But then why do the comments say that this is not normally called for anchorables? If we look at LayoutAnchorableItem.OnVisibilityChanged()
:
protected override void OnVisibilityChanged()
{
if (_anchorable != null && _anchorable.Root != null)
{
if (_visibilityReentrantFlag.CanEnter)
{
using (_visibilityReentrantFlag.Enter())
{
if (Visibility == System.Windows.Visibility.Hidden)
_anchorable.Hide(false);
else if (Visibility == System.Windows.Visibility.Visible)
_anchorable.Show();
}
}
}
base.OnVisibilityChanged();
}
it is clear that AvalonDock uses the Visibility.Hidden
value to indicate that the anchorable is hidden. (This was a little confusing to me since Microsoft states that Hidden
hides the element but reserves space in the layout which is not how the anchorable behaves when you hide it.) So why is the Visibility Collapsed
instead of Hidden
? The answer lies in the BoolToVisibilityConverter
.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool && targetType == typeof(Visibility))
{
bool val = (bool)value;
if (val)
return Visibility.Visible;
else
if (parameter != null && parameter is Visibility)
return parameter;
else
return Visibility.Collapsed;
}
// ...
}
Unless a parameter of type Visibility
is passed, Visibility.Collapsed
is used when the boolean is false. We want false to mean Visibility.Hidden
so we set that as the ConverterParameter
<Setter Property="Visibility" Value="{Binding Model.IsVisible, ConverterParameter={x:Static Visibility.Hidden}, Converter={StaticResource btvc}, Mode=TwoWay}"/>
来源:https://stackoverflow.com/questions/23617707/binding-to-layoutanchorableitem-visibility-in-avalondock-2