I am using INotifyPropertyChanged to propagate changes across my WinForm app.
In this, I have code that converts between a UNIX epoch integer timestamp and a System DateTime object. It receives a ConvertEventArgs object that contains an object value and the DesiredType to convert to.
When using INotifyPropertyChanged and adding a dynamic binding to a Control, you can provide handlers that feed the DesiredType in the ConvertEventArg object.
I have a base class that I call NotifyBase which includes this static function:
public class NotifyBase {
public static void UpdateDataBindings(string propertyName
, Control ctl
, object bindingSource
, string bindingPropertyName
, Action<object, ConvertEventArgs> formatFunc = null
, Action<object, ConvertEventArgs> parseFunc = null
, bool clearBefore = true
, bool formattingEnabled = true) {
var b = new Binding(propertyName, bindingSource, bindingPropertyName, formattingEnabled, DataSourceUpdateMode.OnPropertyChanged);
if (formatFunc != null) b.Format += (sender, args) => formatFunc(sender,args);
if (parseFunc != null) b.Format += (sender, args) => parseFunc(sender,args);
if(clearBefore) ctl.DataBindings.Clear();
ctl.DataBindings.Add(b)
The base class also implements this:
public event PropertyChangedEventHandler PropertyChanged;
protected void SetPropertyField<T>(ref T field, T newValue,
[System.Runtime.CompilerServices.CallerMemberName] string propertyName = "") {
if (EqualityComparer<T>.Default.Equals(field, newValue)) return;
field = newValue;
// Ignoring online conjecture about the need to keep a reference to the event property
// before invoking it.
// 1. this is a single threaded app.
// 2. documentation clearly says that event handlers should merely be checked for being null.
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
I then initialize my databindings. Here is the example with a Winform DateTimePicker.
NotifyBase.UpdateDataBindings("Value", TimePickerBox, Config, nameof(Config.UnitTimestamp)
, (o, args) => { NotifyBase.PropertyTypeChange(args, ReportingTimeZoneInfo); }
, (o, args) => { NotifyBase.PropertyTypeChange(args, ReportingTimeZoneInfo); });
The PropertyTypeChange function essentially takes the ConvertEventArg and feeds it the type that it wants:
Type u = Nullable.GetUnderlyingType(carg.DesiredType);
if (carg.DesiredType == typeof(DateTime) || u == typeof(DateTime)) {
if (u == null) {
if (carg.Value is int) {
carg.Value = epoch.AddSeconds((int) carg.Value);
}
else if (carg.Value is DateTime) carg.Value = carg.Value; // do not throw if type is already the expected type.
else throw new ArgumentOutOfRangeException("Value type conversion to DateTime is not supported.");
} else {
DateTime tp;
if (carg.Value is int) {
tp = epoch.AddSeconds((int) carg.Value);
}
else if (carg.Value is DateTime) tp = (DateTime) carg.Value; // do not throw if type is already the expected type.
else throw new ArgumentOutOfRangeException("Value type conversion to DateTime is not supported.");
carg.Value = tp.MakeNullable<DateTime>();
}
}
Anyway, thought using "is" to do type correspondence was more elegant. But as my watch window shows, it isn't resolving to an int even though it clearly shows the object is an integer:
My question:
I thought the is
expression was equivalent to object.GetType() == typeof(int)
. Is this not true?
It doesn't appear to be and I'd like to better understand the conditions under which that's true.
Anyway, in the absence of a solid answer, I'm going to move on and just make everything use GetType().
Using: Visual Studio 2015 Using: Microsoft.Net 4.6.1 Framework.