-1

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 like Float, 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?

Sparky
  • 172
  • 15
  • Sorry, I got bored halfway through. Surely we don't need to know _all that_ just to understand your question. As near as I can tell, this boils down to wanting to do some computation whenever a property value changes. So, why not just subscribe to `INotifyPropertyChanged` like everyone else does? Please take some time to distill your question down to the bare necessities, make sure you include a good [mcve], explain precisely what that code does, what you want it to do instead, and what specifically you are having trouble figuring out yourself. – Peter Duniho Jul 07 '17 at 21:30
  • @PeterDuniho Tell me how to subscribe to INotifyPropertyChanged in an answer. – Sparky Jul 14 '17 at 14:23

1 Answers1

0

Subscribe to the container class's properties' changes within that container class's constructor like so:

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(...), ...);
    }
}

You can use a common function like this, a for loop with reflection for all the properties, or manually specify the properties.

This was helpful: https://stackoverflow.com/a/7825054/2846508

Sparky
  • 172
  • 15