WPF从Parent中覆盖IsEnabled

时间:2014-03-07 11:32:54

标签: c# wpf dependency-properties

我刚刚搜索了一种在父控件具有IsEnabled = false时启用子控件的方法。 到目前为止我发现的所有答案都说不可能 - 必须启用父级并禁用子控件,但应该仍然启用它们。

但是,通过覆盖App.xaml.cs文件中IsEnabledProperty的元数据,我能够更改此默认行为:

protected override void OnStartup(StartupEventArgs e)
{
    UIElement.IsEnabledProperty.OverrideMetadata(typeof(FrameworkElement),
             new UIPropertyMetadata(true,IsEnabledChanged, CoerceIsEnabled));
}

private void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var childrenCount = VisualTreeHelper.GetChildrenCount(d);
    for (int i = 0; i < childrenCount; ++i)
    {
        var child = VisualTreeHelper.GetChild(d, i);
        child.CoerceValue(UIElement.IsEnabledProperty);
    }
}
private object CoerceIsEnabled(DependencyObject d, object basevalue)
{
    var parent = VisualTreeHelper.GetParent(d) as FrameworkElement;
    if (parent != null && parent.IsEnabled == false)
    {
        if (d.ReadLocalValue(UIElement.IsEnabledProperty) == DependencyProperty.UnsetValue)
        {
            return false;
        }
    }
    return basevalue;
}

现在,您可以在子项上手动设置IsEnabled属性,该属性将覆盖父值。

这种方法有什么缺点吗?

4 个答案:

答案 0 :(得分:4)

这适用于我使用控件的情况多次使用稍作修改。

放置此处以帮助处于类似情况的未来网络搜索者:

  • 将它放在一个静态构造函数而不是一个事件中,否则它会尝试多次设置它并抛出一个&#34; PropertyMetadata已经注册了类型&#39; {type}&#39;。&#39;。 ;异常。
  • 更改了与控件匹配的类型

代码:

确保使用控件的类型名称查找并替换[CustomControl]

static [CustomControl]()
{
    UIElement.IsEnabledProperty.OverrideMetadata(typeof([CustomControl]), new UIPropertyMetadata(true, [CustomControl]_IsEnabledChanged, CoerceIsEnabled));
}

private static void [CustomControl]_IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var childrenCount = VisualTreeHelper.GetChildrenCount(d);
    for (int i = 0; i < childrenCount; ++i)
    {
        var child = VisualTreeHelper.GetChild(d, i);
        child.CoerceValue(UIElement.IsEnabledProperty);
    }
}

private static object CoerceIsEnabled(DependencyObject d, object basevalue)
{
    var parent = VisualTreeHelper.GetParent(d) as FrameworkElement;
    if (parent != null && parent.IsEnabled == false)
    {
        if (d.ReadLocalValue(UIElement.IsEnabledProperty) == DependencyProperty.UnsetValue)
        {
            return false;
        }
    }
    return basevalue;
}

答案 1 :(得分:0)

缺点是至少,你打破了基本概念,并且IsEnabled不用于预期的范围。这种解决方法还使维护变得更复杂(开发人员必须首先理解,为什么它的工作方式不同)。

正如评论中所建议的,我会说,重新设计这个窗口会有所帮助。特别是,如果我只想禁止表单中的编辑(数据修改),我会使用其他属性,如IsReadOnly。

答案 2 :(得分:0)

另一种选择是覆盖FrameworkPropertyMetadataOptions以删除Inherits属性。我在FontSize上遇到了类似的问题,这很有效:

FontSizeProperty.OverrideMetadata(
   typeof(YourControl), 
   new FrameworkPropertyMetadata(8.0, 
      FrameworkPropertyMetadataOptions.None, changed));

答案 3 :(得分:0)

问题中提出的解决方案在某些情况下可能没问题。但是,更改应用程序中所有 UIElement 的默认框架行为可能会引入兼容性问题,并且将来可能难以了解行为发生更改的位置/原因。

另一种方法是保留框架的默认行为,并仅在需要时在特定位置手动覆盖该行为。实现此目的的一种方法是创建一个简单的包装元素,该元素会中断 IsEnabled 与父元素的继承链。

框架的默认强制回调检查父 IsEnabled 值并继承它。这个控件设置了一个新的强制回调,直接返回值而不检查继承。

public class ResetIsEnabled : ContentControl
{
    static ResetIsEnabled()
    {
        IsEnabledProperty.OverrideMetadata(
            typeof(ResetIsEnabled),
            new UIPropertyMetadata(
                defaultValue: true,
                propertyChangedCallback: (_, __) => { },
                coerceValueCallback: (_, x) => x));
    }
}

可以这样使用

<ParentControl IsEnabled="False">
  <!-- Any elements here will have IsEnabled set to false, inherited from the parent -->
  <ResetIsEnabled>
    <!-- Any child elements here will have IsEnabled set to true (the default value) -->
  </ResetIsEnabled>
</ParentControl>