拦截每个数据绑定以使用窄参数执行函数

Intercept every data binding to execute function with narrow parameter

我有一个包含原始数据类型的文件,例如 shortuint 以及 float 的原始数据形式。我将文件解析为 class,类似这样,以便独立跟踪所有内容的 有效性 并保存在 属性:

// Every class inherits this and therefore has the property "Valid"
public class Object { public bool Valid {get;set;} }

// Primitive data types as classes
public class Short : Object { public short Value {get;set;} }
public class UInt : Object { public uint Value {get;set;} }
public class Float : Object { public float Value {get;set;} }
// ... and other data types as classes

使用了这些原始 classes 的更大分组,但与此问题无关。最终,我通过包含的属性到达最大范围,它代表文件本身:

public class File : Object { public byte[] Data {get;private set;} }
public class FileTypeA : File { public Float X {get;set;} }

我正在使用数据绑定和 xaml 以及 Windows Presentation Foundation。我在这里展示了一个过度简化的示例,但足以说明数据绑定使用一堆 INotifyPropertyChanged 东西按预期工作。

我将文件数据的 CRC32 存储在包含的对象中,如下所示:

// This class contains private methods for calculating the CRC32 using a byte[] parameter
public class CRC32 : UInt { }

// The file's header data stores a CRC32 of the file data (excluding the header)
public class Header : Object { public CRC32 CRC {get;set;} }

// The aforementioned FileTypeA class is really more along the lines of this:
public class FileTypeA : File { public Header H {get;set;} public Float X {get;set;} }

当用户打开 FileTypeA 类型的文件时,我实例化了一个 FileTypeA 对象,它具有有效解析所有文件数据的连锁反应,如下所示:

// OpenFileDialog results in a string of the file path, for example:
string filepath = @"path\to\tests\test.a";

// Obtain byte[] of file data and store it along with the filepath in the FileTypeA object
FileTypeA fta = new FileTypeA(System.IO.File.ReadAllBytes(filepath), filepath);

// Instantiate and show a window suitable for displaying the data of type FileTypeA.
// The window stores a property of type FileTypeA.
FTAWindow fta_window = new FTAWindow(fta);
fta_window.Show();

FTAWindow 包含我用来引用所有 FileTypeA 数据的单个 属性:

public class FTAWindow : Window { public FileTypeA A {get;set;} }

我正在使用数据绑定来显示和提供 A 中各种属性的更改,最值得注意的是包含的基本类型对象中称为 Value 的属性。例如,在 xaml 中,我做了类似的事情:

<TextBox
    x:Name="X_TextBox_Value"
    Foreground="{Binding Path=A.X.Valid, Converter={StaticResource ValidToColor}}"
    Text="{Binding Path=A.X.Value}"
/>

我也在window中显示了CRC32值。

问题就在这里: 我想在用户通过 window 中的数据绑定更改文件数据时动态地重新计算文件数据的 CRC32。数据绑定直接从 window 中的用户控件传递到属性;但我需要传递 CRC32 计算函数 A.Data 而数据绑定属性都是特定的、不相关的、范围较窄的 A.X.Value 并且构造属性无法访问它们包含的相邻构造属性的值class。有没有什么方法可以拦截每个数据绑定(或注册所有更改的通知,可能通过某种临时通知中心 class 就像 Apple 的 AppKit 一样)而不对所有内容使用 IValueConverter 以便我可以调用 CRC 计算FTAWindow 中的函数可以访问 A?在绑定 属性 范围内,无法访问 A。我认为我不应该以某种方式将 xaml 中的 A 传递给绑定属性的 setters.

我想让 window 的 A 属性 的每个数据绑定 setter(例如设置 A.X.Value)做它做的事通常(设置值)然后执行位于 window 的代码隐藏中的此函数:

private void RecalculateCRC()
{
    A.H.CRC.Value = CRC32.Calculate(A.Data);
}

我该怎么做?每次使用数据绑定设置 window 的 属性 时,如何使用参数 (A.Data) 调用 window 方法 (RecalculateCRC())?

是不是像“How do I call a function via xaml?" If so, then how do I call the function after the data binding is resolved? Must I use a style trigger such as with a TextBox's LostFocus event? Also related: Calling functions with parameters in xaml

一样简单

更新:

我有这个用于 INotifyPropertyChanged:

public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    // I can put something here...
}

我可以将 A 绑定到 window 本身并使用它,以便在任何 属性 更改时调用它。这意味着将文件对象本身绑定到 window 并使用 IValueConverter 作为数据解析器,这没有任何意义。

我看到两个选项:

样式触发器看起来也很像 hack。我会说事件最适合,但实际上意味着远离数据操作任务。

现在,我正在使用样式和事件,这很简单!


相关:

Subscribe to INotifyPropertyChanged for nested (child) objects

How do I subscribe to PropertyChanged event in my ViewModel?

在容器 class 的构造函数中订阅容器 class 的属性更改,如下所示:

public class FileTypeA : File
{
    public Header H {get;set;}
    public Float X {get;set;}

    // constructor:
    public FileTypeA(byte[] givenData, string givenPath) : base(givenData, givenPath)
    {
        X = new Float(...);
        // watch for changes to the property:
        X.PropertyChanged += MetaChanged;
    }

    // gets called when the aforementioned watched properties change:
    private void MetaChanged(object sender, PropertyChangedEventArgs e)
    {
        H.UpdateCRC(Encode(...), ...);
    }
}

您可以使用像这样的通用函数,一个带有所有属性反射的 for 循环,或者手动指定属性。

这很有帮助: