从 xaml 中清除 dependencyProperty 值

Posted

技术标签:

【中文标题】从 xaml 中清除 dependencyProperty 值【英文标题】:Clear a dependencyProperty value from the xaml 【发布时间】:2011-12-19 15:18:34 【问题描述】:

我有一个控件,它有一个属性的默认值。当控件第一次获取其 dataContext 集时,它会自动分配此属性。

现在在 xaml 中,我希望可以取消设置此属性。我尝试将其设置为 x:Null 的空字符串,但随后出现错误,因为该属性没有转换器。在我希望禁用该功能的极少数情况下,如何简单地从 xaml 取消分配此属性?

最初设置的代码:

void OmniBox_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)

    if( e.NewValue is BindingObjectBaseExtended )
    
        BindingObjectBaseExtended value = (BindingObjectBaseExtended)e.NewValue;
        this.SetBinding(OmniBox.ContextValidationMessagesProperty, new Binding()  Source = value, Path = new PropertyPath("ValidationMessages") );
    

我想取消设置属性的xaml。

<Style TargetType="ui:OmniBox">
    <Setter Property="ContextValidationMessages" Value="" />
</Style>

请注意,如果我没有在数据上下文更改时自动设置绑定,则默认情况下没有验证消息,我必须在 xaml 中执行以下操作来设置它们:

<Style TargetType="ui:OmniBox">
    <Setter Property="ContextValidationMessages" Value="ValidationMessages" />
</Style>

我正在尝试将上述绑定设置为我的自定义 OmniBox 控件的默认设置,并允许用户取消设置或将其更改为其他内容。

【问题讨论】:

【参考方案1】:

DependencyProperty.UnsetValue 不能在 XAML 中使用。

http://msdn.microsoft.com/en-us/library/system.windows.dependencyproperty.unsetvalue(v=vs.90).ASPX

【讨论】:

【参考方案2】:

就个人而言,我会创建一个单独的依赖属性,例如bool AutoBindValidation,并使其默认为true。如果为 false,则在 DataContext 更改时不要执行任何操作。这是更多的自我记录。根据您的具体操作,您可能根本不想公开ContextValidationMessages

如果您真的想按照您发布的方式进行操作,我不确定为什么将其设置为 x:Null 会导致错误(除非属性类型不可为空)。但是这种方法会有问题,因为DataContextChanged 将在解析 XAML 之后发生。因此用户可以将其设置为x:Null,但随后DataContext 将发生变化,您的代码将设置默认绑定并践踏用户的值。您可以在控件的构造函数中设置绑定,但是如果 DataContext 没有ValidationMessages 属性,您的控件将出现绑定错误。

【讨论】:

+1 在这种情况下,正确的解决方案可能是创建一个新的 AutoBindValidation 依赖属性,默认为 true,如果他们尝试设置 ContextValidationMessages 而不先将其设置为 false,则只提供错误消息。不过,对于我的应用程序,我确实希望可以使用不同的 ValidationMesages 集合覆盖默认的自动绑定。【参考方案3】:

这可能是不可能的,我最好的选择是:

<Setter Property="ContextValidationMessages"
        Value="x:Static DependencyProperty.UnsetValue" />

但这会抛出"Cannot unset setter value"。所以你最好颠倒你的逻辑或以另一种方式保持属性未设置。

【讨论】:

+1 我不知道x:Static DependencyProperty.UnsetValue,即使它对这种情况没有帮助,它也可能对类似的情况有所帮助。 但这不是答案。这应该写成评论。 @Lucenty:这里的答案是:you better inverse your logic or keep the property unset another way。有时所要求的内容是不可能的,或者需要其他方法。 太烦人了,这行不通!这就是 css 程序员的方法!【参考方案4】:

我认为 xaml 本身没有任何支持的方式来执行此操作。在您的代码中,您要在 ContextValidationMessagesProperty 上设置一个本地值。您包含的样式设置器将具有较低的dependency property precedence,即使对它们进行了评估,它们也会根据指定的值设置一个值 - 不清除它。也许您可以在 OmniBox 的默认样式中使用 Setter 来设置该属性,而不是在代码中设置绑定 - 例如

<Setter Property="ContextValidationMessages" Value="Binding ValidationMessages" />

如果您必须有条件地设置绑定,那么您可以创建一个自定义 IValueConverter 来检查指定类型(作为参数传递)。例如

public class IsAssignableFromConverter : IValueConverter

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    
        Type typeParameter = parameter as Type;

        if (typeParameter == null)
            return DependencyProperty.UnsetValue;

        return value != null && typeParameter.IsAssignableFrom(value.GetType());
    

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    
        return DependencyProperty.UnsetValue;
    

那么你可以这样使用它:

    <local:IsAssignableFromConverter x:Key="isAssignableConverter" />
    <Style TargetType="ui:OmniBox">
        <Style.Triggers>
            <DataTrigger Binding="Binding Converter=StaticResource isAssignableConverter, ConverterParameter=x:Type ui:BindingObjectBaseExtended" Value="True">
                <Setter Property="ContextValidationMessages" Value="Binding ValidationMessages" />
            </DataTrigger>
        </Style.Triggers>
    </Style>

如果您不希望应用此属性,您可以将 OmniBox 实例的样式设置为新样式,并确保为set the OverridesDefaultStyle property to true。

我想另一种选择是创建另一个依赖属性,它将在 ContextValidationMessages 属性上调用 ClearValue,但这似乎是一个维护问题。

【讨论】:

问题是我确实需要先检查 dataContext 的类型,然后才能尝试设置 ContextValidationMessages。我只希望它为从 BindingObjectBaseExtended 类继承的绑定对象自动设置,否则用户肯定有责任设置它。我不能在 xaml 中进行那种类型检查。 我更新了答案以使用自定义 IValueConverter,如果 DataContext 是特定类型(或派生的),您可以在样式触发器中使用它来有条件地应用设置器。【参考方案5】:

在某些情况下,您可以使用RelativeSource 将其“重置”为父控件的默认值。例如,我正在使用 DataGrid,这对我重置为“默认”很有用。

这是数据网格单元格内的文本块。

<TextBlock Text="Binding ServiceName">

    <TextBlock.Style>
        <Style>

            <Style.Triggers>

                <!-- Change text color to purple for FedEx -->
                <Trigger Property="TextBlock.Text" Value="FedEx">
                    <Setter Property="TextBlock.Foreground" Value="Purple"/>
                </Trigger>

                <!-- Reset if the cell is selected, since purple on blue is illegible -->
                <DataTrigger Binding="Binding IsSelected, RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type DataGridCell" Value="True">
                    <Setter Property="TextBlock.Foreground" Value="Binding Foreground, RelativeSource=RelativeSource FindAncestor, AncestorType=x:Type DataGridCell"/>
                </DataTrigger>

            </Style.Triggers>
        </Style>
    </TextBlock.Style>

</TextBlock>

这似乎足够聪明,即使在窗口处于非活动状态时也能继承正确的颜色。

【讨论】:

以上是关于从 xaml 中清除 dependencyProperty 值的主要内容,如果未能解决你的问题,请参考以下文章

如何从页面中的 App.xaml 获取颜色值

我是不是可以从 XAML 构建模板中访问特定的 MSBuild 参数?

WPF 中.XAML文件如何读取资源文件?

从其他 Xaml 文件绑定到用户控件中的元素

从 XAML 到 C# 的 WPF 故事板

使用 C# 如何从 xaml 返回切换开关的值