Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 是否有一种“干净”的方法使只读依赖项属性反映另一个属性的值?_C#_.net_Wpf_Data Binding_Dependency Properties - Fatal编程技术网

C# 是否有一种“干净”的方法使只读依赖项属性反映另一个属性的值?

C# 是否有一种“干净”的方法使只读依赖项属性反映另一个属性的值?,c#,.net,wpf,data-binding,dependency-properties,C#,.net,Wpf,Data Binding,Dependency Properties,下面的代码是我当前的解决方案 如果你花上几分钟来弄清楚这段代码在做什么,我会听到的 这是一个丑陋的混乱,如果有一个。我很想找到一个替代方案(但不要因此而气馁……:-)。哦,天哪,我甚至通过删除转换器代码来简化它(实质上),当我看到这段代码时,我仍然感到阅读困难 我试图模仿的一个很好的例子是FrameworkElement.ActualWidth属性。您知道在Width属性更改、控件重绘或其他任何时候,如何计算和重新分配ActualWidth属性吗?---- 从开发人员的角度来看,它看起来就像是在

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

如果你花上几分钟来弄清楚这段代码在做什么,我会听到的

这是一个丑陋的混乱,如果有一个。我很想找到一个替代方案(但不要因此而气馁……:-)。哦,天哪,我甚至通过删除转换器代码来简化它(实质上),当我看到这段代码时,我仍然感到阅读困难

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

从开发人员的角度来看,它看起来就像是在努力工作的数据绑定。
但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 { }
    }
}

这并不像你说的那么糟,我想


您可以去掉转换器:
IValueConverter
用于绑定,在代码隐藏中进行转换时不需要它。除此之外,我不知道如何使它更简洁…

不幸的是,你需要你拥有的大部分。这种情况下不需要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);
         }
    }
}

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

在这种方法中,传递到
improverevalue
的“value”参数被忽略。相反,每个DP的
强制值
函数“从头开始”重新计算其值,方法是直接从传递到
强制值
DependencyObject
实例中获取所需的值(如果要避免转换为所有者实例类型,可以使用
dobj.GetValue(…)

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

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

至于何时何地放置这些
强制evalue
/
无效evalue
调用,有两个选项:

  • 急切:在(目标)DP的
    强制evalueCallback
    函数中提到的每个(源)DP的
    属性ChangedCallback
    中为(目标)DP调用
    InvalidateValue
    ,或者--
  • Lazy:始终在获取DP值之前立即调用DP上的
    强制值
诚然,这种方法对XAML不太友好,但这不是OPs问题的要求。然而,考虑到在这种方法中,你根本不需要获取或保留
DependencyPropertyKey
,如果你能够重新接受你的应用程序的“拉动”语义,这似乎是最灵活的方法之一


完全不同的是,还有另一种解决方案可能更简单:

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

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

当然,对于非DP属性或非
DependencyObject
实例,您只需订阅它们的
INotifyPropertyChanged
事件,即可监视可能影响只读属性的更改。在任何情况下,无论您以何种方式将更改推入只读属性,事件