WPF容器将所有子控件变为只读

19
我想要一个WPF容器(面板、用户控件等),该容器公开一个属性,如果设置为只读,则所有子元素都变为只读。这基本上就像将父控件设置为IsEnabled=false,这也会禁用所有子元素。应考虑哪些子元素及其属性是固定的(例如TextBox.ReadOnly,DataGrid.ReadOnly等)。
我尝试创建这样的控件,它基本上遍历可视树的所有子元素(递归)并相应地处理控件。这很好用,但存在一种情况,即进一步更改会影响可视树,以便添加新的子元素。对于ContentControl或ItemsControl,这是正确的。如果在我完成此操作后向可视树添加了子元素,那么它们显然不是只读的。
我一直在寻找一个合适的事件来触发(基本上检测可视树中的新子元素),但无法找到合适的事件。UpdateLayout被触发,但太频繁了,每次都需要遍历可视树。
有解决方法吗?可能还有另一种方法可以通过父元素的绑定递归地将所有(相关的)子元素设置为只读吗?
(不:我不希望将所有子元素的只读属性绑定到全局绑定。关键是拥有单个元素/部分将此传播到所有子元素)

我从未真正退后一步,思考过“新视觉儿童”问题。这确实是一个很好的问题。 - Federico Berasategui
我猜你最好的选择是让ViewModel在集合被修改时或其他情况下引发属性更改通知(NotifyPropertyChange(() => IsReadOnly);),而不是尝试通过Visual Tree路由。 - Federico Berasategui
你需要将它作为一个依赖属性来实现。依赖属性允许继承,这正是你所需要的。IsEnabled 是一个依赖属性,而 IsReadOnly 不是。 - user2880486
1个回答

17

您可以使用提供值继承的附加属性来实现此操作:

public class ReadOnlyPanel
{
    public static readonly DependencyProperty IsReadOnlyProperty =
        DependencyProperty.RegisterAttached(
            "IsReadOnly", typeof(bool), typeof(ReadOnlyPanel),
            new FrameworkPropertyMetadata(false,
                FrameworkPropertyMetadataOptions.Inherits, ReadOnlyPropertyChanged));

    public static bool GetIsReadOnly(DependencyObject o)
    {
        return (bool)o.GetValue(IsReadOnlyProperty);
    }

    public static void SetIsReadOnly(DependencyObject o, bool value)
    {
        o.SetValue(IsReadOnlyProperty, value);
    }

    private static void ReadOnlyPropertyChanged(
        DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        if (o is TextBox)
        {
            ((TextBox)o).IsReadOnly = (bool)e.NewValue;
        }
        // other types here
    }
}

在XAML中,您会这样使用它:

<StackPanel local:ReadOnlyPanel.IsReadOnly="{Binding IsChecked, ElementName=cb}">
    <CheckBox x:Name="cb" Content="ReadOnly"/>
    <TextBox Text="Hello"/>
</StackPanel>

1
我记得最初尝试过这种方法,但在性能方面并没有产生令人满意的结果,不过它确实是一个好的解决方案。 - Federico Berasategui
当然,性能可能会受到影响。来自MSDN的说明:请注意,将属性指定为可继承的确实会有一些性能考虑因素。在该属性没有已建立的本地值或通过样式、模板或数据绑定获取的值的情况下,可继承属性会向逻辑树中的所有子元素提供其分配的属性值。 - Clemens
非常好。但我正在为性能影响而苦恼。我又发布了一篇文章,介绍如何防止属性传播到DataGrid中,因为如果我可以将DataGrid切换为只读,我就不想让单个单元格变为只读。 - user1211286
这是一个非常好的例子.. 但是我想知道我们能否通过代码设置isreadonly。 - keerti_h

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接