I have a file that contains primitive data types such as short
and uint
and float
in their native data form. I parse the file into a class, something like this, so that the validity of everything is independently tracked and kept in a property:
// 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
Larger groupings of these primitive classes are used, but are extraneous to this question. Eventually, I get by way of contained properties to the largest scope, which represents the file itself:
public class File : Object { public byte[] Data {get;private set;} }
public class FileTypeA : File { public Float X {get;set;} }
I am using data binding and xaml with Windows Presentation Foundation. I am showing an over-simplified example here, but suffice it to say that the data binding works as expected using a bunch of INotifyPropertyChanged stuff.
I have a CRC32 of the file's data stored in a contained object, something like this:
// 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;} }
When the user opens a file of type FileTypeA
, I instantiate a FileTypeA
object and that has the chain reaction of effectively parsing all the file's data, something like this:
// 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
contains a single property that I use to reference all the FileTypeA data:
public class FTAWindow : Window { public FileTypeA A {get;set;} }
I am using data binding to display and provide for alteration of the various properties within A
, most notably the properties called Value
within the contained primitive type objects. For example, in the xaml, I do something like:
<TextBox
x:Name="X_TextBox_Value"
Foreground="{Binding Path=A.X.Valid, Converter={StaticResource ValidToColor}}"
Text="{Binding Path=A.X.Value}"
/>
I also show the CRC32 value in the window.
Herein lies the problem: I want to dynamically recalculate the CRC32 of the file data as the user is changing the file data via data binding in the window. Data binding passes directly from the user controls in the window to the properties; but I need to pass the CRC32 calculation function A.Data
whereas the data-bound properties are all of specific, unrelated, narrower scope like A.X.Value
and the constructed properties cannot access the values of adjacent constructed properties of their contained class. Is there some way to intercept every data binding (or register for notifications to all changes, maybe through some kind of makeshift notification center class like Apple's AppKit has) without using an IValueConverter for everything so that I can call a CRC calculation function from within FTAWindow
that will have access to A
? At the bound property scope, there is no access to A
. I don't think I am supposed to somehow pass A
in the xaml to the bound properties' setters.
I want to have every data binding setter of the window's A
property (such as setting A.X.Value
) do what it does normally (set the value) and then execute this function located in the window's code-behind:
private void RecalculateCRC()
{
A.H.CRC.Value = CRC32.Calculate(A.Data);
}
How do I do this? How do I call a window method (RecalculateCRC()
) with parameter (A.Data
) every time the window's property is set using data binding?
Is this as simple as "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
UPDATE:
I have this for the INotifyPropertyChanged:
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
// I can put something here...
}
I could bind A
to the window itself and use that so that when any property changes, it gets called. That would mean binding the file object itself to the window and using an IValueConverter as a data parser, which doesn't make any sense.
I see two options:
- Store a reference to the file or its
byte[]
data as a property of every class likeFloat
, but that seems like a hack and a waste. - Do not call the function using data binding (so why use data binding at all?). Instead, use concluding events (such as
LostFocus
with each TextBox) on the user controls to call the function.
Style triggers also seem like a hack. I'd say events are most closely suited, but are really meant to be further from the tasks of data manipulation.
For now, I'm using styles and events, it's easy enough!
Related:
Subscribe to INotifyPropertyChanged for nested (child) objects
How do I subscribe to PropertyChanged event in my ViewModel?