5

下面的代码是我目前的解决方案。

如果您需要几分钟才能弄清楚这段代码在做什么,我听到了。

如果有的话,这是一个丑陋的混乱。我会杀了看到另一种选择,(但不要让这阻止你回应...... :-)。哦,天哪,我什至(基本上)通过删除转换器代码来缩写它,当我看到这段代码时,我仍然感到阅读障碍。

我试图模仿的一个很好的例子是 FrameworkElement.ActualWidth 属性。您知道如何计算和重新分配 ActualWidth 属性,无论何时 Width 属性更改,或何时重绘控件,或其他任何时候?------

从开发人员的角度来看,它看起来就像是数据绑定在努力工作。
但 ActualWidth 是一个只读的依赖属性。微软真的必须通过这个巨大的代码垃圾洞才能使其工作吗?或者有没有更简单的方法来利用数据绑定系统的现有功能?

public class foo : FrameworkElement
{
    [ValueConversion(typeof(string), typeof(int))]
    public class fooConverter : IValueConverter
    {   public object Convert(  object value, Type targetType,
                                object parameter, CultureInfo culture)
        { ... }
        public object ConvertBack(  object value, Type targetType,
                                    object parameter, CultureInfo culture)
        { ... }
    }

    private static readonly fooConverter fooConv = new fooConverter();



    private static readonly DependencyPropertyKey ReadOnlyIntPropertyKey =
        DependencyProperty.RegisterReadOnly( "ReadOnlyInt", typeof(int),
                                             typeof(foo), null);
    public int ReadOnlyInt
    {   get { return (int)GetValue(ReadOnlyIntPropertyKey.DependencyProperty); }
    }



    public static readonly DependencyProperty ReadWriteStrProperty =
        DependencyProperty.Register( "ReadWriteStr", typeof(string), typeof(foo),
                                     new PropertyMetadata(ReadWriteStr_Changed));
    public string ReadWriteStr
    {   get { return (string)GetValue(ReadWriteStrProperty); }
        set { SetValue(ReadWriteStrProperty, value); }
    }



    private static void ReadWriteStr_Changed(   DependencyObject d,
                                            DependencyPropertyChangedEventArgs e)
    {   try
        {   if (d is foo)
            {   foo f = d as foo;
                f.SetValue( ReadOnlyIntPropertyKey,
                            fooConv.Convert(f.ReadWriteStr, typeof(int), null,
                                            CultureInfo.CurrentCulture));
            }
        }
        catch { }
    }
}
4

3 回答 3

4

不幸的是,你将需要你拥有的大部分东西。在这种情况下,不需要 IValueConverter,因此您可以将其简化为:

public class foo : FrameworkElement
{
    private static readonly DependencyPropertyKey ReadOnlyIntPropertyKey =
        DependencyProperty.RegisterReadOnly( "ReadOnlyInt", typeof(int),
                                         typeof(foo), null);
    public int ReadOnlyInt
    {
       get { return (int)GetValue(ReadOnlyIntPropertyKey.DependencyProperty); }
    }

    public static readonly DependencyProperty ReadWriteStrProperty =
        DependencyProperty.Register( "ReadWriteStr", typeof(string), typeof(foo),
                                 new PropertyMetadata(ReadWriteStr_Changed));

    public string ReadWriteStr
    {
       get { return (string)GetValue(ReadWriteStrProperty); }
        set { SetValue(ReadWriteStrProperty, value); }
    }

    private static void ReadWriteStr_Changed(DependencyObject d,
                                        DependencyPropertyChangedEventArgs e)
    {
         foo f = d as foo;
         if (f != null)
         {
              int iVal;
              if (int.TryParse(f.ReadWriteStr, out iVal))
                  f.SetValue( ReadOnlyIntPropertyKey, iVal);
         }
    }
}
于 2009-08-19T18:01:00.450 回答
1

没有你建议的那么糟糕,恕我直言......

您可以摆脱转换器 :IValueConverter用于绑定,您不需要它来进行代码隐藏中的转换。除此之外,我不明白你如何使它更简洁......

于 2009-08-19T17:55:27.747 回答
1

是的,有一种简洁的方法可以“使只读 DependencyProperty属性反映另一个属性的值”,但它可能需要对应用程序的整体属性编程模型进行相当根本的转变。简而言之,每个只读都可以有一个CoerceValueDependencyPropertyKey回调,而不是使用将值推送到属性中,该回调通过取它所依赖的所有源值来构建自己的值。DependencyProperty

在这种方法中,传递给的“值”参数将CoerceValue被忽略。相反,每个 DP 的函数通过直接从传入的实例中CoerceValue获取它需要的任何值来“从头开始”重新计算其值(如果您想避免转换为所有者实例类型,您可以使用它)。DependencyObjectCoerceValuedobj.GetValue(...)

尽量不要怀疑忽略提供给的价值CoerceValue可能会浪费一些东西。如果您坚持这个模型,那么这些值将永远不会有用,并且整体工作与“推送”模型相同或更少,因为未更改的源值一如既往地由 DP 系统缓存。改变的只是谁负责计算以及在哪里完成。这里的好处是每个 DP 值的计算总是集中在一个地方,并专门与该 DP 相关联,而不是散布在应用程序中。

你可以扔掉DependencyPropertyKey这个模型,因为你永远不需要它。CoerceValue相反,要更新您刚刚调用或InvalidateValue在所有者实例上的任何只读 DP 的值,以指示所需的 DP。这是因为这两个函数不需要 DP key,而是使用公共DependencyProperty标识符,并且它们是公共函数,因此任何代码都可以从任何地方调用它们。

至于何时何地放置这些CoerceValue/InvalidateValue调用,有两种选择:

  • 渴望:在(目标)   DP的函数中提到的每个(源)DP中InvalidateValue调用(目标)DP, --或--PropertyChangedCallbackCoerceValueCallback
     
  • 懒惰:   总是CoerceValue在获取它的值之前立即调用 DP。

确实,这种方法对 XAML 不太友好,但这不是 OPs 问题的要求。然而,考虑到在这种方法中,您甚至根本不需要获取或保留DependencyPropertyKey,如果您能够围绕“拉动”重新构思您的应用程序,这似乎是最时尚的方法之一语义。


在完全独立的情况下,还有另一种可能更简单的解决方案:

公开INotifyPropertyChanged您的DependencyObject并使用 CLR 属性作为只读属性,现在将有一个简单的支持字段。是的,WPF绑定系统将在同一个类实例上正确检测和监视这两种机制——DependencyProperty和——。INotifyPropertyChanged建议使用 setter(私有或其他)来推送对此只读属性的更改,并且此 setter 应检查支持字段以检测空(冗余)更改,否则会引发旧式 CLRPropertyChanged事件。

要绑定到这个只读属性,要么使用所有者的OnPropertyChanged重载(用于自绑定)从 DP 中推送更改,或者,对于任意外部属性的绑定,使用System.ComponentModel.DependencyPropertyDescriptor.FromProperty获取DependencyPropertyDescriptor相关源 DP 的 a,并使用其AddValueChanged方法来设置一个推入新值的处理程序。

当然,对于非 DP 属性或非DependencyObject实例,您可以只订阅它们的INotifyPropertyChanged事件以监视可能影响您的只读属性的更改。在任何情况下,无论您以何种方式将更改推送到只读属性中,其设置器引发的事件都会确保对只读属性的更改正确地传播到任何进一步的依赖属性,无论是 WPF/DP、CLR、数据-绑定或其他。

于 2017-09-08T19:35:19.377 回答