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

发布于 2025-02-01 16:49:06 字数 1926 浏览 3 评论 0原文

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

我想模仿的一个很好的例子是框架。什么时候? ------

从开发人员的角度看,它看起来像是在工作中的数据结合。
但是,实际宽度是仅读取的依赖性属性。微软是否真的必须经过这个巨大的代码垃圾孔才能使这项工作?还是有一种更简单的方法来利用数据结合系统的现有功能?

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 { }
    }
}

The code below is my current solution.

A great example of what I am trying to mimic would be the FrameworkElement.ActualWidth property. You know how the ActualWidth property is calculated and reassigned, whenever the Width property changes, or whenever the control is redrawn, or whenever else? ------

From the developer's perspective, it just looks like data-binding hard-at-work.
But ActualWidth is a read-only dependency-property. Does Microsoft really have to go through this gigantic trash-hole of code to make that work? Or is there a simpler way that utilizes the existing functionality of the data-binding system?

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 { }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

冷︶言冷语的世界 2025-02-08 16:49:06

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

Unfortunately, you'll need most of what you have. The IValueConverter isn't required in this case, so you could simplify it down to just:

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);
         }
    }
}
仲春光 2025-02-08 16:49:06

恕我直言,它并不像您建议的那样糟糕...

您可以摆脱转换器:ivalueconverter用于绑定,您不需要它即可以代码为单位的转换。除此之外,我看不到您如何使它更简洁...

It's not as bad as you suggest, IMHO...

You could get rid of the converter : IValueConverter is for bindings, you don't need it for conversions in code-behind. Apart from that, I don't see how you could make it more concise...

本王不退位尔等都是臣 2025-02-08 16:49:06

是的,有一种干净的方法来“使读取 dependencyProperty反映另一个属性的价值”,但是它可能需要对整个属性编程进行相当根本的转变应用程序的模型。简而 Coercevalue 回调,通过 拉动 构建其自身的价值。

在这种方法中,忽略了传递给coercevalue的“值”参数。相反,每个DP的coercevalue函数函数通过直接从dependencyObject实例传递给coercevalue(((如果要避免施放到所有者实例类型),则可以使用dobj.getValue(...)

尝试压制任何怀疑忽略提供给Coercevalue的价值的怀疑可能正在浪费一些东西。如果您遵守此模型,这些值将永远不会有用,并且整体工作与“推送”模型相同或少于“推动”模型,因为尚未更改的源值一如既往地被DP系统所控制。所改变的只是谁负责计算以及完成的位置。这里的好处是,每个DP值的计算总是集中在一个地方,并且特别与该DP相关,而不是散布在整个应用程序上。

您可以在此模型中丢弃depentencyPropertyKey,因为您永远不需要它。取而代之的是,要更新任何只读的DP的值,您只需调用coercevalueInvalidateValue在所有者实例上,表示所需的DP。之所以起作用,是因为这两个函数不需要DP ,他们使用public dependencyProperty标识符,它们是公共功能,因此,任何代码都可以从任何地方调用。

至于何时何地将这些Coercevalue/InvalidateValue调用,有两个选项:

  • 急切:  在中,在property> property ChangedCallback中呼叫(target)dp的每个(source)dp, in (目标)DP的coercevaluecallback函数,
      - 或 -
  • 懒惰:  在获取其值之前,请始终在DP上拨打CoerceValue

的确,此方法不是那么XAML友好,但这不是OPS问题的要求。但是,考虑到这种方法,您甚至不需要获取或保留dependencyPropertyKey,如果您能够使用,这似乎是最光滑的方法之一围绕“拉”语义重新考虑您的应用程序。


在一个完全独立的静脉中,还有另一个可以更简单的解决方案:

在您的depended> dependencyBoct 上展示inotifypropertychanged ,并使用CLR属性作为读取属性,现在将具有一个简单的支持字段。是的, wpf 绑定系统将正确检测和监视这两个机制 - depentencyPropertyinotifyPropertychanged - 在同一类实例上。建议使用私人或其他方式进行固定器,以推动此纯属性的更改,并且该设置器应检查备份字段以检测空置(冗余)更改,否则提高了旧式的CLR property> property Changed 事件。

要绑定到此仅读取属性,要么使用所有者的onProperTychanged Overload(用于自我绑定)以推动DPS的更改,要么为了从任意外部属性绑定,请使用System 。

当然,对于非DP属性或非dependencyObject实例,您可以订阅其inotifyPropertychanged事件,以监视可能会影响您所读取属性的更改。无论如何,无论您以哪种方式将更改添加到仅阅读属性中,其设定器提出的事件都可以确保更改仅读取属性,并正确传播到任何进一步的依赖性属性,是否WPF/DP,CLR,数据,数据,数据 - 结合或其他。

Yes, there is a clean way to "make a read-only DependencyProperty reflect the value of another property," but it may require a pretty fundamental shift in the overall property programming model of your app. In short, instead of using the DependencyPropertyKey to push values into the property, every read-only DependencyProperty can have a CoerceValue callback which builds its own value by pulling all the source values it depends on.

In this approach, the 'value' parameter that's passed in to CoerceValue is ignored. Instead, each DP's CoerceValue function recalculates its value "from scratch" by directly fetching whatever values it needs from the DependencyObject instance passed in to CoerceValue (you can use dobj.GetValue(...) for this if you want to avoid casting to the owner instance type).

Try to suppress any suspicion that ignoring the value supplied to CoerceValue may be wasting something. If you adhere to this model, those values will never be useful and the overall work is the same or less than a "push" model because source values that haven't changed are, as always, cached by the DP system. All that's changed is who's responsible for the calculation and where it's done. What's nice here is that calculation of each DP value is always centralized in one place and specifically associated with that DP, rather than strewn across the app.

You can throw away the DependencyPropertyKey in this model because you'll never need it. Instead, to update the value of any read-only DP you just call CoerceValue or InvalidateValue on the owner instance, indicating the desired DP. This works because those two functions don't require the DP key, they use the public DependencyProperty identifier instead, and they're public functions, so any code can call them from anywhere.

As for when and where to put these CoerceValue/InvalidateValue calls, there are two options:

  • Eager:   Put an InvalidateValue call for the (target) DP in the PropertyChangedCallback of every (source) DP that's mentioned in the (target) DP's CoerceValueCallback function,
      --or--
  • Lazy:   Always call CoerceValue on the DP immediately prior to fetching its value.

It's true that this method is not so XAML-friendly, but that wasn't a requirement of the OPs question. Considering, however, that in this approach you don't ever even need to fetch or retain the DependencyPropertyKey at all, it seems like it might one of the sleekest ways to go, if you're able to reconceive your app around the "pull" semantics.


In a completely separate vein, there's yet another solution that may be even simpler:

Expose INotifyPropertyChanged on your DependencyObject and use CLR properties for the read-only properties, which will now have a simple backing field. Yes, the WPF binding system will correctly detect and monitor both mechanisms--DependencyProperty and INotifyPropertyChanged--on the same class instance. A setter, private or otherwise, is recommended for pushing changes to this read-only property, and this setter should check the backing field to detect vacuous (redundant) changes, otherwise raising the old-style CLR PropertyChanged event.

For binding to this read-only property, either use the owner's OnPropertyChanged overload (for self-binding) to push in the changes from DPs, or, for binding from arbitrary external properties, use System.ComponentModel.DependencyPropertyDescriptor.FromProperty to get a DependencyPropertyDescriptor for the relevant souce DPs, and use its AddValueChanged method to set a handler which pushes in new values.

Of course for non-DP properties or non-DependencyObject instances, you can just subscribe to their INotifyPropertyChanged event to monitor changes that might affect your read-only property. In any case, no matter which way you push changes into the read-only property, the event raised by its setter ensures that changes to the read-only property correctly propagate onwards to any further dependent properties, whether WPF/DP, CLR, data-bound or otherwise.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文