【问题标题】:property value overridden by parent even though set explicitly即使显式设置,也会被父级覆盖的属性值
【发布时间】:2017-02-15 08:55:00
【问题描述】:

当我有一个包含控件B 的控件A 时,会继承一些属性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 sn-p 将其 DataContext 设置为我的 ViewModel。 ViewModel 包含一个属性InDisableMode,它是一个bool。当它是false 时,一切正常:StackPanel 已启用,TextBox 已禁用。

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

注意:我知道我可以在两个控件中将 IsEnabled 数据绑定到 InDisableMode(直接在 TextBox 中,在 StackPanel 中使用补码转换器)。我还没有尝试过这是否可行,因为无论如何我都想用触发器来做到这一点。

编辑:

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

【问题讨论】:

    标签: c# wpf inheritance properties triggers


    【解决方案1】:

    如果您禁用一个控件,它的子控件也会被禁用。因为StackPanel 不是交互式的,所以除了禁用它的交互式子项之外,没有理由禁用它。

    如果您想在禁用父控件 B 的情况下启用控件 A,则不能这样做。如果您想在 B 被禁用时启用 A,则 B 不能是父级。

    对于解决方法,您可以将它们都放在Grid 中,最后定义TextBox,以将TextBox 叠加在StackPanel 之上。然后它将在StackPanel 的区域内,但不会是StackPanel 的子代。

    【讨论】:

      【解决方案2】:

      这是因为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) ,看看会发生什么。

      【讨论】:

      • “发生这种情况是因为 UIElement.IsEnabled 属性通过从其父级继承值来使用值强制。” - 但是如果孩子总是从其父母那里继承价值,为什么可以让父母启用而孩子被禁用?
      • @Kjara 继承和覆盖是两种不同的情况。强制意味着您要覆盖默认行为。 Label.FontSize = 20 将更改其中所有文本块的字体大小,但如果您设置特定字体的字体大小,则会受到尊重。
      猜你喜欢
      • 2015-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-09
      • 1970-01-01
      • 2017-11-30
      • 2022-12-18
      • 1970-01-01
      相关资源
      最近更新 更多