即使明确设置

时间:2016-10-06 14:39:01

标签: c# wpf inheritance properties triggers

如果我有一个包含控件A的控件B,则会继承属性Prop。这意味着,如果B.Prop未明确设置,A.Prop将自动获取B.Prop 的值。据我所知,IsEnabled就是这样的财产。

现在我的情况是我明确地设置了B.IsEnabled的值,但它仍被A.IsEnabled的值覆盖。 为什么会这样,我该如何纠正?

在这种情况下,A是一个StackPanel,B是一个TextBox:

<StackPanel>
    <StackPanel.Style>
        <Style TargetType="StackPanel">
            <Setter Property="IsEnabled" Value="True"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding InDisableMode}" Value="True">
                    <Setter Property="IsEnabled" Value="False"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </StackPanel.Style>
    <TextBox Text="some text">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Setter Property="IsEnabled" Value="False"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding InDisableMode}" Value="True">
                        <Setter Property="IsEnabled" Value="True"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>
</StackPanel>

上面的XAML代码段将其DataContext设置为我的ViewModel。 ViewModel包含属性InDisableMode,即bool。当它为false时,一切都符合预期:启用了StackPanel并禁用了TextBox。

但是当InDisableModetrue时,StackPanel和TextBox都被禁用,尽管两个触发器都应该触发!

注意:我知道我可以在两个控件中将IsEnabled数据绑定到InDisableMode(在TextBox中直接使用补充转换器在StackPanel中)。我还没有尝试过,因为我想用触发器来做这件事。

编辑:

禁用StackPanel的目的是轻松禁用所有子代(除了我想要启用的TextBox)。如何在不改变父子关系或创建新控件的情况下解决此任务的任何其他想法?目前,我看到的唯一方法是逐个禁用除TextBox之外的所有子项......

2 个答案:

答案 0 :(得分:1)

如果禁用控件,则会禁用其子控件。由于StackPanel不是交互式的,因此除了禁用其交互式子项外,没有理由禁用它。

如果要在禁用其父B的情况下启用控件A,则无法执行此操作。如果要在禁用B时启用A,则B不能是父级。

要获得变通方法,您可以将它们放在Grid中,最后定义TextBox,将TextBox叠加在StackPanel之上。然后它将在StackPanel区域内,但它不会成为StackPanel的孩子。

答案 1 :(得分:1)

这是因为UIElement.IsEnabled属性通过从其父级继承值来使用值强制。这是通过使用CoerceValueCallback来实现的。价值强制在Dependency Property Setting Precedence List.

中排名第一

因此,要覆盖此行为,我们有两个选项。首先,使用AddOwner()将我们的类型注册为IsEnabled属性的新所有者。其次,使用OverrideMetadata()覆盖元数据。仅当您直接从UIElement继承时,第二种方法才有效。

因此,假设我们希望我们的Button行为不同,我们应该创建一个新的Button,如下所示:

public class CButton : Button
{
    public static readonly DependencyProperty IsEnabled;

    static CButton()
    {
        IsEnabled = UIElement.IsEnabledProperty.AddOwner(typeof(CButton), 
                new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None, 
                    UIElement.IsEnabledProperty.DefaultMetadata.PropertyChangedCallback,
                    new CoerceValueCallback(IsEnabledCoerceCallback)));
    }

    private static object IsEnabledCoerceCallback(DependencyObject d, object baseValue)
    {            
        return (bool) baseValue;
    }
}

在这里,我们将从IsEnabledCoerceCallback返回指定值。在返回之前,您还可以介绍行为:如果用户没有为IsEnabled提供任何值,则使用来自父项的继承值,否则使用CButton.IsEnabled用户指定的值。

在旁注中,尝试设置null代替new CoerceValueCallback(IsEnabledCoerceCallback),看看会发生什么。