Capture property change using its setter - c#

I want to be notified when a property changes so that I can log the oldvalue and new value of the property in database.
So I decided to go with the approach of property setter and have a generic method that handles all properties.
I created below class:
public class PropertyChangedExtendedEventArgs<T> : PropertyChangedEventArgs
{
public virtual T OldValue { get; private set; }
public virtual T NewValue { get; private set; }
public PropertyChangedExtendedEventArgs(string propertyName,
T oldValue, T newValue)
: base(propertyName)
{
OldValue = oldValue;
NewValue = newValue;
//write to database the values!!!
}
}
and on my property I call it as such:
private string _surname;
public string Surname
{
get { return _surname; }
set
{
string temp = Surname;
_surname = value;
Helper.PropertyChangedExtendedEventArgs("Surname", temp, value);
}
}
but it is first time working with generics so got few concerns :
how do I call this on my property?
is this a good approach?
would I be able to call a function in public
PropertyChangedExtendedEventArgs(string propertyName, T oldValue, T newValue) and save to database?

You seem to have got a bit of confused in property change usage.
Typically, components that wish to be observable about their property changes INotifyPropertyChanged interface. So as such correct implementation would be something like
private string _surname;
public string Surname
{
get { return _surname; }
set
{
if (_surname != value) // IMP: you want to inform only if value changes
{
string temp = Surname;
_surname = value;
// raise property change event,
NotifyPropertyChanged(temp, _surname);
}
}
}
Typically, base implementation could provide helper implementation to raise the event - for example,
public abstract Component : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged<T>(T oldVal, T newVal, [CallerMemberName] String propertyName = "")
{
var e = PropertyChanged;
if (e != null)
{
e(this, new PropertyChangedExtendedEventArgs(propertyName, oldVal, newVal));
}
}
}
Now, its consumer's responsibility on how to react to property changes. This separates observable components from unrelated concern of what to do when some property changes. Typically, one will have some the common implementation that would say - save the current object state in stacked manner as to provide undo-redo functionality.
So in your case, you wish to log them to database (?), there should be code that would listen to this property change events and does the logging. There will be some controller/binding code that would iterate through all objects implementing this interface and hook up the event. Typically, the root level container does such house keeping - for example, in a designer surface, its the root element (or code that is handling root element) would hook up event whenever a new component is created and added to the design surface.

Related

INotifyPropertyChanged with Properties that have the same name

I'm working on a system to represent data. In it we use a templetized interface that implements INotifyPropertyChanged.
public interface IScalar<T> : ISignal, INotifyPropertyChanged
{
void Check(T value);
/// <summary>
/// Formats the specified value based on the item's formatting
/// characteristics. Will throw an exception if the specified value
/// could not be properly converted to the underlying type.
/// </summary>
/// <param name="value">Value to format.</param>
/// <returns>Formatted value.</returns>
string Format(T value);
T Value { get; set; }
string Units { get; set; }
}
We end having a class that implements IScalar<double> and IScalar<string>. Is there a way to make sure the correct PropertyChanged event is fired? It uses a string representation of the property name. And since I have two properties with the same name I can't guarantee the right event will be fired. We are looking to have a grid in WPF bind to a list of IScalar
You can't implicitly implement an generic interface with two different type parameters. You have to make at least one explicit. Here you see a sample implementation for your class. As you can see you can bind to StringValue and DoubleValue:
public class Both : IScalar<string>, IScalar<double>
{
public string StringValue { get; set; }
string IScalar<string>.Value
{
get
{
return StringValue;
}
set
{
this.StringValue = value;
}
}
public double DoubleValue { get; set; }
double IScalar<double>.Value
{
get
{
return DoubleValue;
}
set
{
DoubleValue = value;
}
}
// other methods and properties left out
}
When you need to raise PropertyChanged, you can raise that event for either StringValue or DoubleValue.
You can't have two properties with the same name on your datacontext.
If you did, you would have a compile error reflecting ambiguity.
Remember that your source is your datacontext.
In addition, the databinding system relies on both source and path in order to perform databinding.
Apart from Scott's proper answer, you may also want to get away from calling the PropertyChanged Method by passing in strings. Here's how you do that:
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
INPC using member/method name is now supported in the attribute class CallerMemberName found in the System.runtime.compilerservices assembly.
You can read more about it here.
ADVANTAGES:
It allows you to easily establish a base class once and for all that handles all notifications based only on the method name. The setter methods merely have this line of code:
OnPropertyChanged();

Determining the caller inside a setter -- or setting properties, silently

Given a standard view model implementation, when a property changes, is there any way to determine the originator of the change? In other words, in the following view model, I would like the "sender" argument of the "PropertyChanged" event to be the actual object that called the Prop1 setter:
public class ViewModel : INotifyPropertyChanged
{
public double Prop1
{
get { return _prop1; }
set
{
if (_prop1 == value)
return;
_prop1 = value;
// here, can I determine the sender?
RaisePropertyChanged(propertyName: "Prop1", sender: this);
}
}
private double _prop1;
// TODO implement INotifyPropertyChanged
}
Alternatively, is it possible to apply CallerMemberNameAttribute to a property setter?
If I understood correctly, you're asking about the caller of the setter. That means, the previous method call in the call stack before getting to the setter itself (which is a method too).
Use StackTrace.GetFrames method for this. For example (taken from http://www.csharp-examples.net/reflection-callstack/):
using System.Diagnostics;
[STAThread]
public static void Main()
{
StackTrace stackTrace = new StackTrace(); // get call stack
StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
// write call stack method names
foreach (StackFrame stackFrame in stackFrames)
{
Console.WriteLine(stackFrame.GetMethod().Name); // write method name
}
}
The output:
Main
nExecuteAssembly
ExecuteAssembly
RunUsersAssembly
ThreadStart_Context
Run
ThreadStart
Basically, what you're asking for would be stackFrames[1].GetMethod().Name.
My first approach to your problem would be to derive from PropertyEventArgs. The new class would have a member called, for instance PropertyChangeOrigin in addition to PropertyName. When you invoke the RaisePropertyChanged, you supply an instance of the new class with the PropertyChangeOrigin set from the information gleaned from the CallerMemberName attribute. Now, when you subscribe to the event, the subscriber could try casting the eventargs to your new class and use the information if the cast is successful.
This is what I always use as a middle-ground between INotifyPropertyChanged and my View Models:
public class NotifyOnPropertyChanged : INotifyPropertyChanged
{
private IDictionary<string, PropertyChangedEventArgs> _arguments;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void OnPropertyChanged([CallerMemberName] string property = "")
{
if(_arguments == null)
{
_arguments = new Dictionary<string, PropertyChangedEventArgs>();
}
if(!_arguments.ContainsKey(property))
{
_arguments.Add(property, new PropertyChangedEventArgs(property));
}
PropertyChanged(this, _arguments[property]);
}
}
Two things here. It uses the [CallerMemberName] attribute to set the property name. This makes the usage syntax as follows:
public string Words
{
set
{
if(value != _words)
{
_words = value;
OnPropertyChanged( );
}
}
}
Beyond that, it stores the PropertyChangedEventArgs object in a dictionary so it's not created a ton of times for properties that are frequently set. I believe this addresses your problem. Good luck!
Whenever I have had to pass in extra information down into a VM I have a great success with using commands:
Commands, RelayCommands and EventToCommand

Elegant way to implement INotifyPropertyChanged across many controls

I'm building a WPF application and I'm slowly uncovering some of the joys and also the frustrations of using WPF. My latest question involves updating the UI using INotifyPropertyChanged
My app has stacked UserControls with each UserControl containing multiple controls, so overall there are hundreds of controls which update every second providing live data. In order to update all controls I'm using something similar to below which does currently work as intended.
namespace ProjectXAML
{
public partial class ProjectX : UserControl, INotifyPropertyChanged
{
#region Declare Getter/Setter with INotifyPropertyChanged groupx3
private string m_group1Text1;
public string group1Text1
{
get
{
return m_group1Text1;
}
set
{
m_group1Text1 = value;
NotifyPropertyChanged("group1Text1");
}
}
private string m_group1Text2;
public string group1Text2
{
get
{
return m_group1Text2;
}
set
{
m_group1Text2 = value;
NotifyPropertyChanged("group1Text2");
}
}
private string m_group2Text1;
public string group2Text1
{
get
{
return m_group2Text1;
}
set
{
m_group2Text1 = value;
NotifyPropertyChanged("group2Text1");
}
}
private string m_group2Text2;
public string group2Text2
{
get
{
return m_group2Text2;
}
set
{
m_group2Text2 = value;
NotifyPropertyChanged("group2Text2");
}
}
private string m_group3Text1;
public string group3Text1
{
get
{
return m_group3Text1;
}
set
{
m_group3Text1 = value;
NotifyPropertyChanged("group3Text1");
}
}
private string m_group3Text2;
public string group3Text2
{
get
{
return m_group3Text2;
}
set
{
m_group3Text2 = value;
NotifyPropertyChanged("group3Text2");
}
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
/// Notifies the property changed.
private void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
#endregion
}
}
My questions are:
Is there a more elegant way to raise PropertyChanged events for lots of controls rather than lots of get/set code?
Is there a way to raise 1 PropertyChanged event covering the whole UserControl containing multiple controls instead of a separate event for every control? Is there a better method than what I'm attempting?
In strict reference to this part of your question..."Is there a way to raise 1 PropertyChanged event covering the whole UserControl containing ".
Yes, you can raise a PropertyChanged notification which says all my properties on my object are updated.
Use:
NotifyPropertyChanged(null);
then this informs the listener of INotifyPropertyChanged that all properties have changed on an object.
This isn't normally used...and can be abused....and cause inefficient updates e.g. if you were only changing a few properties and used that.
But you could argue the case for using it if you have lots of properties in your object, that you were always changing anyway at the same time...and you wanted to collapse lots of individual notifications into 1 that was raised after you had modified all properties.
Example use case (i.e. presumes you are updating all your groups in some way):
void UpdateAllGroupTextProperties()
{
group1Text1 = "groupA";
group1Text2 = "groupA2";
group2Text1 = "groupB";
group2Text2 = "groupB2";
group3Text1 = "groupC";
group3Text2 = "groupC2";
NotifyPropertyChanged(null);
}
For point 1 if you are using VS 2012 you can do the below
private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
and then you can use your set property method without having to hard code the name of the properties.
Note the above code is an except of the below link
http://danrigby.com/2012/03/01/inotifypropertychanged-the-net-4-5-way/
Use the design pattern model view controler. So the model will raise the changes for you. Together with MVVM the controls will see with its dependency objects the changes and view them automatically.

c# marking class property as dirty

The following is a simple example of an enum which defines the state of an object and a class which shows the implementation of this enum.
public enum StatusEnum
{
Clean = 0,
Dirty = 1,
New = 2,
Deleted = 3,
Purged = 4
}
public class Example_Class
{
private StatusEnum _Status = StatusEnum.New;
private long _ID;
private string _Name;
public StatusEnum Status
{
get { return _Status; }
set { _Status = value; }
}
public long ID
{
get { return _ID; }
set { _ID = value; }
}
public string Name
{
get { return _Name; }
set { _Name = value; }
}
}
when populating the class object with data from the database, we set the enum value to "clean". with the goal of keeping most of the logic out of the presentation layer, how can we set the enum value to "dirty" when a property is changed.
i was thinking something along the lines of;
public string Name
{
get { return _Name; }
set
{
if (value != _Name)
{
_Name = value;
_Status = StatusEnum.Dirty;
}
}
}
in the setter of each property of the class.
does this sound like a good idea, does anyone have any better ideas on how the dirty flag can be assigned without doing so in the presentation layer.
When you really do want a dirty flag at the class level (or, for that matter, notifications) - you can use tricks like below to minimise the clutter in your properties (here showing both IsDirty and PropertyChanged, just for fun).
Obviously it is a trivial matter to use the enum approach (the only reason I didn't was to keep the example simple):
class SomeType : INotifyPropertyChanged {
private int foo;
public int Foo {
get { return foo; }
set { SetField(ref foo, value, "Foo"); }
}
private string bar;
public string Bar {
get { return bar; }
set { SetField(ref bar, value, "Bar"); }
}
public bool IsDirty { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void SetField<T>(ref T field, T value, string propertyName) {
if (!EqualityComparer<T>.Default.Equals(field, value)) {
field = value;
IsDirty = true;
OnPropertyChanged(propertyName);
}
}
protected virtual void OnPropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You might also choose to push some of that into an abstract base class, but that is a separate discussion
One option is to change it on write; another is to keep a copy of all the original values and compute the dirtiness when anyone asks for it. That has the added benefit that you can tell exactly which fields have changed (and in what way) which means you can issue minimal update statements and make merge conflict resolution slightly easier.
You also get to put all the dirtiness-checking in one place, so it doesn't pollute the rest of your code.
I'm not saying it's perfect, but it's an option worth considering.
If you want to implement it in this way, and you want to reduce the amount of code, you might consider applying Aspect Oriented Programming.
You can for instance use a compile-time weaver like PostSharp , and create an 'aspect' that can be applied to properties. This aspect then makes sure that your dirty flag is set when appropriate.
The aspect can look like this:
[Serializable]
[AttributeUsage(AttributeTargets.Property)]
public class ChangeTrackingAttribute : OnMethodInvocationAspect
{
public override void OnInvocation( MethodInvocationEventArgs e )
{
if( e.Delegate.Method.ReturnParameter.ParameterType == typeof(void) )
{
// we're in the setter
IChangeTrackable target = e.Delegate.Target as IChangeTrackable;
// Implement some logic to retrieve the current value of
// the property
if( currentValue != e.GetArgumentArray()[0] )
{
target.Status = Status.Dirty;
}
base.OnInvocation (e);
}
}
}
Offcourse, this means that the classes for which you want to implement ChangeTracking, should implement the IChangeTrackable interface (custom interface), which has at least the 'Status' property.
You can also create a custom attribute ChangeTrackingProperty, and make sure that the aspect that has been created above, is only applied to properties that are decorated with this ChangeTrackingProperty attribute.
For instance:
public class Customer : IChangeTrackable
{
public DirtyState Status
{
get; set;
}
[ChangeTrackingProperty]
public string Name
{ get; set; }
}
This is a little bit how I see it.
You can even make sure that PostSharp checks at compile-time whether classes that have properties that are decorated with the ChangeTrackingProperty attribute, implement the IChangeTrackable interface.
This method is based on a set of different concepts provided in this thread. I thought i'd put it out there for anyone that is looking for a way to do this cleanly and efficiently, as i was myself.
The key of this hybrid concept is that:
You don't want to duplicate the data to avoid bloating and resource hogging;
You want to know when the object's properties have changed from a given original/clean state;
You want to have the IsDirty flag be both accurate, and require little processing time/power to return the value; and
You want to be able to tell the object when to consider itself clean again. This is especially useful when building/working within the UI.
Given those requirements, this is what i came up with, and it seems to be working perfectly for me, and has become very useful when working against UIs and capturing user changes accurately. I have also posted an "How to use" below to show you how I use this in the UI.
The Object
public class MySmartObject
{
public string Name { get; set; }
public int Number { get; set; }
private int clean_hashcode { get; set; }
public bool IsDirty { get { return !(this.clean_hashcode == this.GetHashCode()); } }
public MySmartObject()
{
this.Name = "";
this.Number = -1;
MakeMeClean();
}
public MySmartObject(string name, int number)
{
this.Name = name;
this.Number = number;
MakeMeClean();
}
public void MakeMeClean()
{
this.clean_hashcode = this.Name.GetHashCode() ^ this.Number.GetHashCode();
}
public override int GetHashCode()
{
return this.Name.GetHashCode() ^ this.Number.GetHashCode();
}
}
It's simple enough and addresses all of our requirements:
The data is NOT duplicated for the dirty check...
This takes into account all property changes scenarios (see scenarios below)...
When you call the IsDirty property, a very simple and small Equals operation is performed and it is fully customizable via the GetHashCode override...
By calling the MakeMeClean method, you now have a clean object again!
Of course you can adapt this to encompass a bunch of different states... it's really up to you. This example only shows how to have a proper IsDirty flag operation.
Scenarios
Let's go over some scenarios for this and see what comes back:
Scenario 1
New object is created using empty constructor,
Property Name changes from "" to "James",
call to IsDirty returns True! Accurate.
Scenario 2
New object is created using paramters of "John" and 12345,
Property Name changes from "John" to "James",
Property Name changes back from "James" to "John",
Call to IsDirty returns False. Accurate, and we didn't have to duplicate the data to do it either!
How to use, a WinForms UI example
This is only an example, you can use this in many different ways from a UI.
Let's say you have a two forms ([A] and [B]).
The first([A]) is your main form, and the second([B]) is a form that allows the user to change the values within the MySmartObject.
Both the [A] and the [B] form have the following property declared:
public MySmartObject UserKey { get; set; }
When the user clicks a button on the [A] form, an instance of the [B] form is created, its property is set and it is displayed as a dialog.
After form [B] returns, the [A] form updates its property based on the [B] form's IsDirty check. Like this:
private void btn_Expand_Click(object sender, EventArgs e)
{
SmartForm form = new SmartForm();
form.UserKey = this.UserKey;
if(form.ShowDialog() == DialogResult.OK && form.UserKey.IsDirty)
{
this.UserKey = form.UserKey;
//now that we have saved the "new" version, mark it as clean!
this.UserKey.MakeMeClean();
}
}
Also, in [B], when it is closing, you can check and prompt the user if they are closing the form with unsaved changes in it, like so:
private void BForm_FormClosing(object sender, FormClosingEventArgs e)
{
//If the user is closing the form via another means than the OK button, or the Cancel button (e.g.: Top-Right-X, Alt+F4, etc).
if (this.DialogResult != DialogResult.OK && this.DialogResult != DialogResult.Ignore)
{
//check if dirty first...
if (this.UserKey.IsDirty)
{
if (MessageBox.Show("You have unsaved changes. Close and lose changes?", "Unsaved Changes", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
e.Cancel = true;
}
}
}
As you can see from the examples above, this can be a very useful thing to have since it really streamlines the UI.
Caveats
Every time you implement this, you have to customize it to the object you're using. E.g.: there's no "easy" generic way of doing this without using reflection... and if you use reflection, you lose efficiency, especially in large and complex objects.
Hopefully this helps someone.
Take a look at PostSharp (http://www.postsharp.org/).
You can easily create a Attribute which marks it as dirty you can add the attrubute to each property that needs it and it keeps all your code in one place.
Roughly speaking Create an interface which has your status in make the class implement it.
Create an attribute which can be applied on properties and cast to your interface in order to set the value when something changes one of the marked properties.
Your approach is basically how I would do it. I would just
remove the setter for the Status property:
public StatusEnum Status
{
get { return _Status; }
// set { _Status = value; }
}
and instead add a function
public SetStatusClean()
{
_Status = StatusEnum.Clean;
}
As well as SetStatusDeleted() and SetStatusPurged(), because I find it better indicates the intention.
Edit
Having read the answer by Jon Skeet, I need to reconsider my approach ;-) For simple objects I would stick with my way, but if it gets more complex, his proposal would lead to much better organised code.
If your Example_Class is lightweight, consider storing the original state and then comparing the current state to the original in order to determine the changes. If not your approach is the best because stroing the original state consumes a lot of system resources in this case.
Apart from the advice of 'consider making your type immutable', here's something I wrote up (and got Jon and Marc to teach me something along the way)
public class Example_Class
{ // snip
// all properties are public get and private set
private Dictionary<string, Delegate> m_PropertySetterMap;
public Example_Class()
{
m_PropertySetterMap = new Dictionary<string, Delegate>();
InitializeSettableProperties();
}
public Example_Class(long id, string name):this()
{ this.ID = id; this.Name = name; }
private void InitializeSettableProperties()
{
AddToPropertyMap<long>("ID", value => { this.ID = value; });
AddToPropertyMap<string>("Name", value => { this.Name = value; });
}
// jump thru a hoop because it won't let me cast an anonymous method to an Action<T>/Delegate
private void AddToPropertyMap<T>(string sPropertyName, Action<T> setterAction)
{ m_PropertySetterMap.Add(sPropertyName, setterAction); }
public void SetProperty<T>(string propertyName, T value)
{
(m_PropertySetterMap[propertyName] as Action<T>).Invoke(value);
this.Status = StatusEnum.Dirty;
}
}
You get the idea.. possible improvements: Use constants for PropertyNames & check if property has really changed.
One drawback here is that
obj.SetProperty("ID", 700); // will blow up int instead of long
obj.SetProperty<long>("ID", 700); // be explicit or use 700L
Here is how i do it.
In cases where i do not need to test for specific fields being dirty,
I have an abstract class:
public abstract class SmartWrap : ISmartWrap
{
private int orig_hashcode { get; set; }
private bool _isInterimDirty;
public bool IsDirty
{
get { return !(this.orig_hashcode == this.GetClassHashCode()); }
set
{
if (value)
this.orig_hashcode = this.orig_hashcode ^ 108.GetHashCode();
else
MakeClean();
}
}
public void MakeClean()
{
this.orig_hashcode = GetClassHashCode();
this._isInterimDirty = false;
}
// must be overridden to return combined hashcodes of fields testing for
// example Field1.GetHashCode() ^ Field2.GetHashCode()
protected abstract int GetClassHashCode();
public bool IsInterimDirty
{
get { return _isInterimDirty; }
}
public void SetIterimDirtyState()
{
_isInterimDirty = this.IsDirty;
}
public void MakeCleanIfInterimClean()
{
if (!IsInterimDirty)
MakeClean();
}
/// <summary>
/// Must be overridden with whatever valid tests are needed to make sure required field values are present.
/// </summary>
public abstract bool IsValid { get; }
}
}
As well as an interface
public interface ISmartWrap
{
bool IsDirty { get; set; }
void MakeClean();
bool IsInterimDirty { get; }
void SetIterimDirtyState();
void MakeCleanIfInterimClean();
}
This allows me to do partial saves, and preserve the IsDirty state if there is other details to save. Not perfect, but covers a lot of ground.
Example of usage with interim IsDirty State (Error wrapping and validation removed for clarity):
area.SetIterimDirtyState();
if (!UpdateClaimAndStatus(area))
return false;
area.MakeCleanIfInterimClean();
return true;
This is good for most scenarios, however for some classes i want to test for each field with a backing field of original data, and either return a list of changes or at least an enum of fields changed.
With an enum of fields changed i can then push that up through a message chain for selective update of fields in remote caches.
You could also think about boxing your variables, which comes at a performance cost, but also has its merits. It is pretty consise and you cannot accidentally change a value without setting your dirty status.
public class Variable<T>
{
private T _value;
private readonly Action<T> _onValueChangedCallback;
public Variable(Action<T> onValueChangedCallback, T value = default)
{
_value = value;
_onValueChangedCallback = onValueChangedCallback;
}
public void SetValue(T value)
{
if (!EqualityComparer<T>.Default.Equals(_value, value))
{
_value = value;
_onValueChangedCallback?.Invoke(value);
}
}
public T GetValue()
{
return _value;
}
public static implicit operator T(Variable<T> variable)
{
return variable.GetValue();
}
}
and then hook in a callback that marks your class as dirty.
public class Example_Class
{
private StatusEnum _Status = StatusEnum.New;
private Variable<long> _ID;
private Variable<string> _Name;
public StatusEnum Status
{
get { return _Status; }
set { _Status = value; }
}
public long ID => _ID;
public string Name => _Name;
public Example_Class()
{
_ID = new Variable<long>(l => Status = StatusEnum.Dirty);
_Name = new Variable<string>(s => Status = StatusEnum.Dirty);
}
}
Another method is to override the GetHashCode() method to somthing like this:
public override int GetHashCode() // or call it GetChangeHash or somthing if you dont want to override the GetHashCode function...
{
var sb = new System.Text.StringBuilder();
sb.Append(_dateOfBirth);
sb.Append(_marital);
sb.Append(_gender);
sb.Append(_notes);
sb.Append(_firstName);
sb.Append(_lastName);
return sb.ToString.GetHashCode();
}
Once loaded from the database, get the hash code of the object. Then just before you save check if the current hash code is equal to the previous hash code. if they are the same, don't save.
Edit:
As people have pointed out this causes the hash code to change - as i use Guids to identify my objects, i don't mind if the hashcode changes.
Edit2:
Since people are adverse to changing the hash code, instead of overriding the GetHashCode method, just call the method something else. The point is detecting a change not whether i use guids or hashcodes for object identification.

Problems with combining Serialization and DataBinding

Actually i'm working with C# and already got DataBinding and Serialization to work. But now i'd like to combine both methods in one class and i have a little problem with it.
So let's start with a little sample class:
using System;
using System.Runtime.Serialization;
using System.Windows.Forms;
namespace MySample
{
[DataContract(IsReference = true)]
class SerializeAndBind : IExtensibleDataObject
{
[DataMember]
private String bindedControlName;
[DataMember]
private String bindedPropertyName;
private DateTime creationTime;
[System.ComponentModel.Browsable(false)]
public virtual ExtensionDataObject ExtensionData { get; set; }
public event EventHandler CreationTimeChanged;
public SerializeAndBind()
{
CreationTime = DateTime.Now;
}
public SerializeAndBind(Control ControlName, String PropertyName)
: this()
{
InitializeDataBinding(ControlName, PropertyName);
}
[DataMember]
public DateTime CreationTime
{
get
{
return creationTime;
}
set
{
creationTime = value;
if (CreationTimeChanged != null)
CreationTimeChanged(this, EventArgs.Empty);
}
}
public override string ToString()
{
return CreationTime.ToString();
}
[OnDeserialized]
private void InitializeDataBindingAfterDeserialization(StreamingContext ctx)
{
if (bindedControlName != null)
{
Control control;
control = FindControlByName(bindedControlName);
if(control != null)
InitializeDataBinding(control, bindedPropertyName);
}
}
private void InitializeDataBinding(Control ControlName, string PropertyName)
{
BindingSource bindingSource = new BindingSource();
bindingSource.DataSource = this;
Binding binding = new Binding(PropertyName, bindingSource, "CreationTime", true, DataSourceUpdateMode.OnPropertyChanged);
binding.Format += new ConvertEventHandler(OnFormat);
ControlName.DataBindings.Add(binding);
bindedControlName = ControlName.Name;
bindedPropertyName = PropertyName;
}
private void OnFormat(object sender, ConvertEventArgs e)
{
if (e.DesiredType == typeof(String))
{
e.Value = this.ToString();
}
}
}
}
As you can see this class has a ctor which takes the Control and PropertyName to which you like to bind this object. For Serialization i save the Control and PropertyName as a string (saving the whole control would be a little to much ;-)).
And i added a function which will be called after deserialization. But as you can see there exists the function FindControlByName() within that doesn't exists.
Now i could start using Reflection to find the appropiate control, but for reflection i need some kind of starting point (i would say the form), but how can the class access this without knowing anything outside of itself?
Or is this just the wrong way to do it and i need a different design?
Any help would be appreciate,
Oliver
I agree with previous post, break out your processing from your data. Much cleaner and easier to debug, maintain and scale.
Personally, I doubt that this is ever going to be "clean" - serialization works well for data, but not so well for objects tightly connected to objects outside of the serialization graph.
At the point of deserialization, the object is in the ether (in fact, DataContractSerializer doesn't even use any constructor). As such, it has no way of getting to a form, etc. You could perhaps do some horrible hacks with a static field (perhaps [ThreadStatic]) to hold the current form, but it would be brittle and ugly.
(I'm hopeful that somebody will have a better answer, though!)

Categories