获取DependencyProperties源(binding,const,...)并替换为wrapper

时间:2013-11-18 13:18:36

标签: c# wpf

我试图通过数据绑定和附加属性将面板中的整个控件集设置为只读(例如,如果用户没有编辑权限)。 (我知道将面板设置为禁用也会禁用其子节点,但这太多了,因为它也会禁用超链接,列表等。)

基本上,属性更改事件处理程序迭代可视树并查找所有TextBox子项,然后将其IsReadOnly属性设置为true或false。 这有效,但不包括TextBox已经具有IsReadOnly设置的情况 - const或binding。例如,如果TextBox应始终是只读的,则附加属性不应将其更改为true。此外,如果TextBox具有在某些情况下将TextBox限制为只读的绑定,则附加属性不应盲目地设置为true或false,而是组合设置,即如果附加属性AND文本框绑定指示没有只读,则它是可编辑的,否则它是只读的。

如何做到这一点?这将需要以某种方式获取当前的IsReadOnly设置(绑定,标记,常量值,...)并将其替换为执行AND组合的包装器。 如何获取依赖项属性的当前设置/值源?我看了下面的内容,但看不出它会如何解决我的问题:

        TextBox1.GetValue(TextBoxBase.IsReadOnlyProperty);
        DependencyPropertyHelper.GetValueSource(TextBox1, TextBoxBase.IsReadOnlyProperty);
        TextBox1.GetBindingExpression(TextBoxBase.IsReadOnlyProperty);

任何帮助都将不胜感激。

Ĵ.-

编辑:我正在寻找类似

的内容
(pseudo-code) 
TextBox1.IsReadOnly := OR(TextBox1.IsReadOnly, GlobalIsReadOnly) 

现在将TextBox1.IsReadOnly设置为true,如果设置了GlobalIsReadOnly标志,或者TextBox1.IsReadOnly值指示只读,无论是绑定,标记还是常量。

3 个答案:

答案 0 :(得分:3)

您可以使用DependencyPropertyDescriptor来挂钩您的IsReadonly属性更改处理程序(适用于所有对象)。

(注意:添加到DependencyPropertyDescriptor的处理程序是一个gcroot ...请记住以避免内存泄漏)

这个钩子会尝试获取你的自定义附加属性,如果找到它并设置为'readonly forced',如果它的值被更改,则将你的IsReadOnly属性重新设置为false(但是存储一个标志,可能是另一个附加的属性,知道以后是否必须恢复为只读。

但是,您的逻辑将覆盖IsReadonly上的任何绑定。但是,使用GetBindingExpression和存储/恢复在IsReadonly属性上设置的绑定表达式,可以使用绑定表达式(而不仅仅是属性的值)应用相同的逻辑。

专业人员:实施后不再需要其他代码。

缺点: DependencyPropertyDescriptor.AddValueChanged“隐藏”逻辑......因为不会发现这个IsReadonly属性将被绑定到你将要编写的更多xaml中的内容。

*编辑:其他解决方案*

使用多重绑定,这应该工作(未测试)。 但是,这有一些要求:

  • 绑定/值必须1不得修改
  • 在执行此

    之前,必须初始化绑定
        var readonlyGlobalBinding = new Binding
            {
                Source = myRoot, // to fill
                Path = new PropertyPath(IsGlobalReadOnlyProperty)
            };
        var be = box.GetBindingExpression(TextBoxBase.IsReadOnlyProperty);
        if (be != null)
        {
            var mb = new MultiBinding();
            mb.Bindings.Add(be.ParentBinding);
            mb.Bindings.Add(readonlyGlobalBinding);
            mb.Converter = new OrConverter();
            box.SetBinding(TextBoxBase.IsReadOnlyProperty, mb);
        }else if(!box.IsReadOnly)
            box.SetBinding(TextBoxBase.IsReadOnlyProperty, readonlyGlobalBinding);
    

使用班级

        class OrConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            return values.OfType<bool>().Aggregate(false, (a, b) => a || b);
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new InvalidOperationException();
        }
    }

答案 1 :(得分:2)

这不完全是你所追求的,但我会从不同的角度来解决这个问题。我基本上将bool IsReadOnly属性添加到我的视图模型中,Bind将其添加到UI控件上的相关IsReadOnly属性,然后将其设置为true false 1}}依赖于一些UI交互:

public bool IsReadOnly { get; set; } // Implement INotifyPropertyChanged here

...

<TextBox Grid.Row="0" IsReadOnly="{Binding IsReadOnly}" ... />
...
<TextBox Grid.Row="3" IsReadOnly="{Binding IsReadOnly}" ... />
<TextBox Grid.Row="4" ... /> <!--Never readonly-->

...

 IsReadOnly = true;

答案 2 :(得分:0)

我在类似方案中使用的是SetCurrentValueInvalidateProperty

  

此方法由以编程方式设置的组件使用   在不禁用应用程序的情况下,其自身属性之一的值   宣布使用该财产。 SetCurrentValue方法改变了   有效的属性值,但现有的触发器,数据绑定,   和风格将继续有效。

因此,您可以使用SetCurrentValue将IsReadOnly设置为true,然后调用InvalidateProperty将其重置为“声明的使用”(可能是true或false)。

但是我不确定这是你想要的。听起来您希望附加属性在其活动的整个时间内具有优先权。使用此方法,如果绑定在 SetCurrent和Invalidate之间的某个时间更新,则仍将应用覆盖附加属性。

另一半解决方案是添加触发器。如果在TextBox样式的Triggers集合的末尾声明了此触发器,则它将优先:

<DataTrigger Binding="{Binding GlobalIsReadonly}" Value="True">
    <Setter Property="IsReadOnly" Value="True" />
</DataTrigger>

问题是它只优先于其他setter(样式或触发器),而不是直接设置属性。在分配样式后也无法修改样式,因此要以编程方式添加样式,您需要复制旧样式并重新分配。

不幸的是,我认为唯一完整的解决方案是收听所有文本框中的属性更改并强制进行其他更改。