C# 已从父级禁用WPF覆盖

C# 已从父级禁用WPF覆盖,c#,wpf,dependency-properties,C#,Wpf,Dependency Properties,我刚刚搜索了一种方法来启用子控件,而父控件具有IsEnabled=false。 到目前为止,我找到的所有答案都表明这是不可能的——必须启用父控件并禁用子控件,但仍应启用的控件除外 但是,通过覆盖App.xaml.cs文件中IsEnabledProperty的元数据,我可以更改此默认行为: protected override void OnStartup(StartupEventArgs e) { UIElement.IsEnabledProperty.OverrideMetadata(

我刚刚搜索了一种方法来启用子控件,而父控件具有
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;
}
启动时受保护的覆盖无效(StartupEventArgs e)
{
UIElement.IsEnabledProperty.OverrideMetadata(typeof(FrameworkElement),
新的UIPropertyMetadata(true,IsEnabledChanged,强制启用));
}
私有void IsEnabledChanged(DependencyObject d、DependencyPropertyChangedEventArgs e)
{
var childrenCount=visualtreeheloper.GetChildrenCount(d);
对于(int i=0;i
现在,您可以在子级上手动设置
IsEnabled
属性,该属性将覆盖父级值


这种方法有什么缺点吗?

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


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

这在我的情况下适用于多次使用的控件,只需稍加修改

在此处放置以帮助任何未来处于类似情况的web搜索者:

  • 将其放置在静态构造函数而不是事件中,否则它会多次尝试对其进行设置,并引发“PropertyMetadata已为类型{type}注册”异常
  • 已更改类型以匹配控件
代码:

确保找到控件的类型名称并将其替换为
[CustomControl]

static[CustomControl]()
{
UIElement.IsEnabledProperty.OverrideMetadata(typeof([CustomControl]),新的UIPropertyMetadata(true,[CustomControl]_IsEnabledChanged,强制启用));
}
私有静态无效[CustomControl]\u IsEnabledChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
{
var childrenCount=visualtreeheloper.GetChildrenCount(d);
对于(int i=0;i
另一个选项是覆盖FrameworkPropertyMetadataOptions以删除Inherits属性。我对FontSize也有类似的问题,而且效果很好:

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

当父级禁用时启用子级=子级不应该是该父级的子级,设计需要重新思考。我认为有必要提出另一个问题,以便我们可以告诉您如何在当前布局中避免这种情况。没有一个单独的布局可以应用此布局,而是整个应用程序的一般要求。根据当前用户的不同,某些内容不应编辑。但是,应该可以在子控件中滚动和导航。因此,应启用选定的子控件(例如树视图)。实现这一点最简单的方法是禁用父控件并启用选定的子控件-否则我们将不得不手动绑定数百个子控件,使用不同的viewmodels-并非每个子控件都知道它是否已启用…通过WPF中的设计,所有子控件都从父控件继承isEnable属性,因此,如果此属性与父属性不匹配,则每个子控件都不允许您更改此属性。我相信有另一种方法可以获得您所需要的相同效果(可能还需要一些设计更改:),但尝试使子对象IsEnable属性不匹配并不是正确的方法。如果您可以发布更多的GUI设计代码,将会更好。我在VS2013中也使用了这一点。我只需要在应用程序的一个窗口中显示该行为。Therfor我将代码放入行为类中。使用这个类,只有在需要时才可以将行为附加到UIElement。工作得很好。谢谢但并非所有框架元素都支持IsReadOnly属性。那么我如何将它应用于窗口的所有子元素呢?这正是我想要的。我从ContentPresenter派生并实现了您的方法,因此我可以用它包装任何类型的视图/控件。