Binding to LayoutAnchorableItem Visibility in AvalonDock 2
avalondock, c#, wpf
Solution
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}"/>
Problem
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.