Binding to LayoutAnchorableItem Visibility in AvalonDock 2

耗尽温柔 提交于 2019-12-06 13:51:56

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!