根据祖先类型的存在来设置样式

时间:2013-10-28 21:45:53

标签: wpf xaml findancestor

我有两组文本块,其中一些是在itemcontrol中,其中一些不是, 我想创建一个样式(仅基于类型),如果其祖先是ItemControl,则设置文本块的背景。
我可以通过以下代码来实现,但问题是在日志(和输出窗口)上将显示数据替换错误消息,因为没有Itemcontrol作为ancestore的文本块。有没有更好的方法来执行此任务并避免此错误消息?

<Grid>
    <Grid.Resources>
        <local:HasAncestorConverter x:Key="HasAncestorConverter" />
        <Style TargetType="TextBlock">

            <Style.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Converter={StaticResource HasAncestorConverter}}" Value="True">
                    <Setter Property="Background"
                            Value="{Binding Tag,
                            RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />

                </DataTrigger>
            </Style.Triggers>

        </Style>
    </Grid.Resources>
    <StackPanel>
        <TextBlock Text="Out of ItemControl" />
        <ItemsControl Tag="Blue" >
            <TextBlock Text="Inside of ItemControl" />
        </ItemsControl>
    </StackPanel>

</Grid>    

转换器:

class HasAncestorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value != null;
    }

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

错误讯息:

  

System.Windows.Data错误:4:无法找到与引用'RelativeSource FindAncestor绑定的源,AncestorType ='System.Windows.Controls.ItemsControl',AncestorLevel ='1''。 BindingExpression:路径=;的DataItem = NULL; target元素是'TextBlock'(Name =''); target属性是'NoTarget'(类型'Object')

4 个答案:

答案 0 :(得分:2)

我认为@Xameli解决方案正是您实际需要的... ... 但如果您只是必须以某种风格进行,那么您可以使用VisualTreeHelper来实现它:

<Style.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Converter={StaticResource HasAncestorConverter}}" Value="True">
                <Setter Property="Background"
                        Value="{Binding Tag,RelativeSource={RelativeSource Self}}" />

            </DataTrigger>
        </Style.Triggers>

转换器:

class HasAncestorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        //you probably will have to look a few levels up
        var parent = VisualTreeHelper.GetParent(value) as ItemsControl;
        return item != null; 
    }

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

答案 1 :(得分:2)

根据@ makc的回应,我用这种方式解决了问题:

 <Grid>
    <Grid.Resources>
        <local:HasAncestorConverter x:Key="HasAncestorConverter" />
        <Style TargetType="TextBlock">
            <Style.Triggers>
                <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, 
                                        Converter={StaticResource HasAncestorConverter},
                                        ConverterParameter={x:Type ItemsControl}}"
                             Value="True">
                    <Setter Property="Background"
                            Value="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Resources>
    <StackPanel>
        <TextBlock Text="Out of ItemsControl" />
        <ItemsControl Tag="Blue">
            <TextBlock Text="Inside of ItemsControl" />
        </ItemsControl>
    </StackPanel>
</Grid>  

转换器:

class HasAncestorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter
        , System.Globalization.CultureInfo culture)
    {
        object parent = null;
        if (value != null && parameter != null &&
            parameter is Type && value is DependencyObject)
        {
            var control = value as DependencyObject;
            Type t = parameter as Type;
            parent = ParentFinder.FindParent(control, t);
        }
        return parent != null;
    }

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

用于查找特定类型父级的助手类:
注意:此帮助程序在逻辑树或可视树中查找任何类型的父级。例如,在我的情况下,ItemsControl是逻辑树中的父项,它可以是祖父项。

class ParentFinder
{
    public static object FindParent(DependencyObject child, Type parentType)
    {
        object parent = null;
        var logicalParent = LogicalTreeHelper.GetParent(child);
        var visualParent = VisualTreeHelper.GetParent(child);

        if (!(logicalParent == null && visualParent == null))
        {
            if (logicalParent != null && logicalParent.GetType() == parentType)
                parent = logicalParent;
            else if (visualParent != null && visualParent.GetType() == parentType)
                parent = visualParent;
            else
            {
                if (visualParent != null)
                    parent = FindParent(visualParent, parentType);
                if (parent == null && logicalParent != null)
                    parent = FindParent(logicalParent, parentType);
            }
        }
        return parent;
    }
}

答案 2 :(得分:1)

DataTemplate中的项目使用ItemsControl

<ItemsControl ....
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding }"
                       Background="{Binding Tag,
                                            RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <sys:String>Inside of ItemControl</String>
</ItemsControl>

如果您需要其他设置器,请保留您拥有的样式,只需移除触发器。

答案 3 :(得分:0)

您可以使用FallbackValue或TargetNullValue

检查此链接:

http://dontcodetired.com/blog/post/FallbackValue-TargetNullValue-StringFormat-in-Silverlight-4.aspx