如何处理可能存在或不存在的源的RelativeSource绑定?

时间:2015-06-29 18:33:41

标签: wpf xaml binding wpf-controls

我们有一个可能会或可能不会在弹出控件中托管的控件。在它的情况下,我们想要使用RelativeSource和OneWayToSource绑定在弹出窗口上设置属性。如果它没有托管在弹出窗口中,我们希望绑定基本上被忽略/什么都不做。

我唯一想到的就是使用自定义转换器绑定到self,该转换器在内部遍历可视树寻找弹出窗口。如果找到了,那就去做吧。如果没有,什么也不做。但我想知道它是否可以完全用XAML绑定语法完成。

2 个答案:

答案 0 :(得分:0)

请尝试以下代码:

   <Border>
        <TextBlock>
            <TextBlock.Style>
                <Style TargetType="TextBlock">
                    <Setter Property="Text" Value="Exists"/>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=StackPanel}}" Value="{x:Null}">
                            <Setter Property="Text" Value="No stackpanel"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </TextBlock.Style>
        </TextBlock>
    </Border>

它将同时显示&#34; No stackpanel&#34;和输出窗口中的错误。如果你把StackPanel放在Border&#34; Exists&#34;将显示。在满足条件时,在DataTrigger中设置您想要的任何内容。

如果您想避免收到错误:

  <Window.Resources>
    <local:IsParentTypePresentToBoolConverter x:Key="IsParentTypePresentToBoolConverter"/>
</Window.Resources>

<Border>
    <TextBlock>
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Setter Property="Text" Value="No StackPanel"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, 
                        Converter={StaticResource IsParentTypePresentToBoolConverter}}" Value="True">
                        <Setter Property="Text" Value="Stackpanel exists as parent"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
</Border>

转换器,它检测这种类型是否作为父类存在:

class IsParentTypePresentToBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var isPresent  = FindParent<StackPanel>((DependencyObject) value);
        return isPresent != null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    private T FindParent<T>(DependencyObject child) where T : DependencyObject
    {
        var parentObject = VisualTreeHelper.GetParent(child);
        if (parentObject == null) return null;
        var parent = parentObject as T;
        return parent?? FindParent<T>(parentObject);
    }
}

在这里你有更多的通用等价物,你可以使用反射来找到父类型。

class IsParentTypePresentToBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var method = GetType().GetMethod("FindParent").MakeGenericMethod(new Type[1] { (Type)parameter });
        var foundObject = method.Invoke(this, new object[] { (DependencyObject)value });
        return foundObject != null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public T FindParent<T>(DependencyObject child) where T : DependencyObject
    {
        var parentObject = VisualTreeHelper.GetParent(child);
        if (parentObject == null) return null;
        var parent = parentObject as T;
        return parent ?? FindParent<T>(parentObject);
    }
}

XAML中唯一的区别是您指示搜索对象类型。

 <Border>
    <TextBlock>
        <TextBlock.Style>
            <Style TargetType="TextBlock">
                <Setter Property="Text" Value="No StackPanel"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, 
                        Converter={StaticResource IsParentTypePresentToBoolConverter},
                        ConverterParameter={x:Type Border}}" Value="True">
                        <Setter Property="Text" Value="Stackpanel exists as parent"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBlock.Style>
    </TextBlock>
</Border>

答案 1 :(得分:0)

  

如果它没有托管在弹出窗口中,我们希望绑定基本上被忽略/什么都不做。

由于你有一个控件,你可以创建一个布尔依赖属性,一个标志,它可以触发两个隐藏控件中的一个,这些控件由于设置布尔值的方式而以特定方式运行。

我称之为标准方式,因为控件不需要了解消费者的任何信息,消费者指定状态。

  

使用RelativeSource和OneWayToSource绑定在弹出窗口上设置属性。

与上面类似,使用两个不同的隐藏控件,然后使用样式查找特定窗口和特定属性。然后根据找到的内容隐藏或显示控件:

<Setter Property="IsEnabled" Value="False" />
<Style.Triggers>
    <DataTrigger Binding="{Binding Path=IsPopup, 
                         RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Page}}}"
                 Value="True">
        <Setter Property="IsEnabled"
                Value="True" />
    </DataTrigger>
</Style.Triggers>