Привязка к видимости LayoutAnchorableItem в AvalonDock 2

Я пытаюсь связать Visibility из LayoutAnchorableItem с логическим значением в ViewModel, чтобы я мог программно отображать и скрывать привязку:

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

Однако всякий раз, когда я скрываю привязку, возникает исключение:

Ссылка на объект не установлена ​​на экземпляр.

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)
...

После комментирования привязки к видимости объект привязки скрывается, как и ожидалось.


person clcto    schedule 12.05.2014    source источник


Ответы (1)


tl;dr

Вам нужно добавить ConverterParameter значения Visibility.Hidden в привязку:

<Setter Property="Visibility" Value="{Binding Model.IsVisible, ConverterParameter={x:Static Visibility.Hidden}, Converter={StaticResource btvc}, Mode=TwoWay}"/>

Параметр преобразователя — это Visibility, который возвращается, когда логическое значение равно false, а Hidden означает, что привязка скрыта.

Полный ответ

Если мы посмотрим на LayoutContent.Close(), он помечен комментарием:

Обратите внимание, что обычно якорь только скрыт (не закрыт). По умолчанию, когда пользователь нажимает кнопку X, он только скрывает содержимое.

Так это не должно было называться. Глядя на трассировку стека, это вызывается из:

// LayoutItem class.
protected virtual void OnVisibilityChanged()
{
    if (LayoutElement != null &&
        Visibility == System.Windows.Visibility.Collapsed)
        LayoutElement.Close();
}

Согласно Microsoft, System.Windows.Visibility.Collapsed означает, что элемент не виден и для него не зарезервировано место во время компоновки. Это похоже на то, что происходит с элементами привязки, когда мы нажимаем X, чтобы скрыть их (и это, вероятно, происходит где-то вверху визуального дерева). Но тогда почему в комментариях говорится, что это обычно не требуется для привязок? Если мы посмотрим на 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();
}

ясно, что AvalonDock использует значение Visibility.Hidden, чтобы указать, что объект привязки скрыт. (Это немного смутило меня, поскольку Microsoft заявляет, что Hidden скрывает элемент, но резервирует место в макете, что не соответствует тому, как ведет себя привязка, когда вы его скрываете.) Так почему же видимость Collapsed вместо Hidden? Ответ лежит в 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;
    }

    // ...
}

Если параметр типа Visibility не передается, Visibility.Collapsed используется, когда логическое значение равно false. Мы хотим, чтобы false означало Visibility.Hidden, поэтому мы устанавливаем его как ConverterParameter.

<Setter Property="Visibility" Value="{Binding Model.IsVisible, ConverterParameter={x:Static Visibility.Hidden}, Converter={StaticResource btvc}, Mode=TwoWay}"/>
person clcto    schedule 12.05.2014
comment
Раньше я не замечал этого продолжения моего вопроса. Спасибо, что прояснили ситуацию? Это означает, что я могу вернуться к использованию исходников из основного репозитория. (Для других это был мой связанный с этим вопрос: " title="поставляется ли менеджер докинга со встроенным методом для обработки привязок"> stackoverflow.com/questions/21692918/) - person TimothyP; 21.06.2014
comment
Благодарю вас! Благодарю вас! Благодарю вас! Благодарю вас! Благодарю вас! - person Josh Close; 25.10.2014