Using WPF has made me a fan of INotifyPropertyChanged. I like to use a helper that takes an expression and returns the name as a string (see example code below). In lots of applications I see by very proficient programmers, however, I see code that handles the strings raw (see 2nd example below). By proficient I mean MVP types who know how to use Expressions.
To me, the opportunity for having the compiler catch mistakes in addition to easy refactoring makes the Exression approach better. Is there an argument in favor of using raw strings that I am missing?
Cheers,
Berryl
Expression helper example:
public static string GetPropertyName<T>(Expression<Func<T, object>> propertyExpression)
{
Check.RequireNotNull<object>(propertyExpression, "propertyExpression");
switch (propertyExpression.Body.NodeType)
{
case ExpressionType.MemberAccess:
return (propertyExpression.Body as MemberExpression).Member.Name;
case ExpressionType.Convert:
return ((propertyExpression.Body as UnaryExpression).Operand as MemberExpression).Member.Name;
}
var msg = string.Format("Expression NodeType: '{0}' does not refer to a property and is therefore not supported",
propertyExpression.Body.NodeType);
Check.Require(false, msg);
throw new InvalidOperationException(msg);
}
Raw strings example code (in some ViewModelBase type class):
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG"), DebuggerStepThrough]
public void VerifyPropertyName(string propertyName) {
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null) {
string msg = "Invalid property name: " + propertyName;
if (ThrowOnInvalidPropertyName) throw new Exception(msg);
else Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
To me, the opportunity for having the compiler catch mistakes in addition to easy refactoring makes the Exression approach better. Is there an argument in favor of using raw strings that I am missing?
I agree, and personally, use the expression approach in my own code, in most cases.
However, there are two reasons I know of to avoid this:
It is less obvious, especially to less experienced .NET developers. Writing RaisePropertyChanged(() => this.MyProperty ); is not always as obvious to people as RaisePropertyChanged("MyProperty");, and doesn't match framework samples, etc.
There is some performance overhead to using expressions. Personally, I don't feel this is really that meaningful of a reason, since this is usually used in data binding scenarios (which are already slow due to reflection usage), but it is potentially a valid concern.
The benefit of using the TypeDescriptor approach is that it enables dynamic property scenarios based on ICustomTypeDescriptor implementations where the implementation can effectively create dynamic property metadata on the fly for a type that is being described. Consider a DataSet whose "properties" are determined by the result set it is populated with for example.
This is something that expressions does not provide however because it's based on actual type information (a good thing) as opposed to strings.
I wound up spending more time on this than I expected, but did find a solution that has a nice mix of safety/refactorability and performance. Good background reading and an alternate solution using reflection is here. I like Sacha Barber's solution even better (background here.
The idea is to use an expression helper for a property that will participate in change notification, but only take the hit once for it, by storing the resulting PropertyChangedEventArgs in your view model. For example:
private static PropertyChangedEventArgs mobilePhoneNumberChangeArgs =
ObservableHelper.CreateArgs<CustomerModel>(x => x.MobilePhoneNumber);
HTH,
Berryl
Stack walk is slow and lambda expression is even slower. We have solution similar to well known lambda expression but almost as fast as string literal. See
http://zamboch.blogspot.com/2011/03/raising-property-changed-fast-and-safe.html
A CallerMemberName attribute was introduced in .net 4.5
This attribute can only be attached to optional string parameters and if the parameter is not used by caller in the function call then name of the caller will be passed in the string parameter
This removes the need to specify name of property when raising the PropertyChanged event thus it works with refactoring and because the changes are done at compile time there's no difference in performance.
Below is an example of implementation and more info can be found at http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.callermembernameattribute.aspx and http://msdn.microsoft.com/en-us/library/hh534540.aspx
public class DemoCustomer : INotifyPropertyChanged
{
string _customerName
public string CustomerName
{
get { return _customerNameValue;}
set
{
if (value != _customerNameValue)
{
_customerNameValue = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Related
I've been messing around with Expressions - and I may have gone beyond my capabilities - but here goes... I've implemented 'type-safe' INotifyPropertyChanged implementation (an example is here), but gone a bit farther and included changetracking:
public abstract BaseViewModel<TEntity>:INotifyPropertyChanged
{
private readonly IBaseChangeTracker<TEntity> _changeTracker;
protected void OnPropertyChanged<T>(Expression<Func<T>> property, T value)
{
_changeTracker.AddChange(property, value);
OnPropertyChanged(property);
}
protected virtual void OnPropertyChanged<TProperty>(Expression<Func<TProperty>> property)
{
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs(property.GetMemberInfo().Name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public abstract class BaseChangeTracker<TEntity>:IBaseChangeTracker<TEntity>
{
private readonly IDictionary<Expression, object> _changes = new Dictionary<Expression, object>();
public void AddChange<T>(Expression<Func<T>> expression, T newValue)
{
_changes.Add(expression, newValue);
}
public void ApplyChanges(TEntity entity)
{
foreach (var change in _changes)
{
var property = typeof(TEntity).GetProperty(change.Key.GetMemberInfo().Name);
property.SetValue(entity, change.Value, null);
}
}
public virtual void CopyCurrentState(TEntity entity)
{
_changes.Clear();
}
public virtual void ResetEntity(TEntity entity)
{
_changes.Clear();
}
public bool HasUnsavedChanges
{
get { return _changes.Any(); }
}
}
This may seem a bit excessive - each entity will have it it's own ChangeTracker keeping the original state of the entity when loaded and can reset it back to these, but the idea is that if there is a concurrency conflict when it tries to save the updated entity, I reload the entity from the database and run it through .ApplyChanges and try to save it again. This will remove about 95% of my concurrency problems... if it works. My tests show that for limited entities it works, but that is with simple property-changes.
Known issue:
I have yet to find an elegant way of handling collections.
What else am I missing - or are there obvious flaws in my design?
I realize that this is not a direct answer to your question, but you might consider using the Command Pattern to implement an undo/redo stack.
Encapsulating changes in commands is a very tidy way to cycle/re-cycle through changes, with the added benefits of (1) a nice feature that adds value to the application, (2) you can wrap many actions in any given command, like raising event change notifications for databinding support in both the do and undo directions.
Additionally, managing collection changes is no more or less challenging than simple property updates.
Specific to the code you posted, the OnPropertyChanged implementation will never raise the PropertyChanged event because you call return after the if() statement and then again in bare brackets (these do not correspond to the if condition).
if (PropertyChanged == null) return; // this returns based on if
{
return; // this returns no matter what
}
Additionally, it seems that the user won't ever see any changes in the UI. The values aren't updated until ApplyChanges is called, and when it is there is no PropertyChanged event. (I might not be following your code correctly, but just looking over it this seems to be the case).
Typically in the property setter of an object we may want to raise a PropertyChanged event such as,
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public string UserNote
{
get { return _userNote; }
set
{
_userNote = value;
Notify("UserNote");
}
}
In our existing code base I see instances where PropertyChangedEventArgs is being sent null in order to indicate that all properties of the object have changed. This seems inefficient and seems to lead to far more events being triggered than is needed. It also seems to causes issues where objects update each other in a circular fashion.
Is this ever a good practice?
A comment in the code tries to justify it ...
//The purpose of this method is to wire up clients of NotificationBase that are also
//NotificationBases to *their* clients. Consider the following classes:
public class ClassA : NotificationBase
{
public int Foo
{
get { return 123; }
set { Notify("Foo"); }
}
}
public class ClassB : NotificationBase
{
ClassA A = new ClassA();
public ClassB()
{
A.PropertyChanged += AllChanged;
}
public void SetFoo()
{
A.Foo = 456;
}
}
public class ClassC
{
ClassB B = new ClassB();
public ClassC()
{
B.PropertyChanged += delegate { dosomething(); };
B.SetFoo(); // causes "dosomething" above to be called
}
}
/// ClassB.SetFoo calls ClassA.Foo's setter, which calls ClassA.Notify("Foo").
/// The event registration in ClassB's ctor causes ClassB.AllChanged to be called, which calls
/// ClassB.Notify(null) - implying that ALL of ClassB's properties have changed.
/// The event registration in ClassC's ctor causes the "dosomething" delegate to be called.
/// So a Notify in ClassA is routed to ClassC via ClassB's PropertyChanged event.
protected void AllChanged(Object sender, PropertyChangedEventArgs e)
{
Notify(null);
}
Any thoughts much appreciated.
Regards,
Fzzy
This is actually a problem with the design (or its documentation) of PropertyChangedEventArgs. Setting PropertyName to null means "all properties on this object have changed." But unless the class is sealed, or you're using reflection, you can't actually know that all properties on the object have changed. The most you can say is that all of the properties in the object's base class have changed.
This is reason enough to not use this particular convention, in my book, except in the vanishingly small number of cases where I create sealed classes that implement property-change notification.
As a practical matter, what you're really trying to do is just raise one event that tells listeners "a whole bunch of properties on this object have changed, but I'm not going to bother to tell you about them one by one." When you say:
I see instances where PropertyChangedEventArgs is being sent null in order to indicate that all properties of the object have changed. This seems inefficient and seems to lead to far more events being triggered than is needed.
...the actual intent is the exact opposite. If a method changes the Foo, Bar, Baz, and Bat properties on an object, and the object has only four or five properties, raising one event is probably better than raising four. On the other hand, if the object has sixty properties, raising four events is probably better making every one of the object's listeners - even those that aren't looking at those four properties - do whatever they do when the properties that they care about change, because those properties didn't.
The problem is that the property-change notification system, as designed, isn't a fine-grained enough tool for every single job. It's designed to be completely generic, and has no knowledge of a particular application domain built into it.
And that, it seems to me, is what's missing from your design: application domain knowledge.
In your second example, if a Fixture object has (say) ten properties that depend on the value of FixtureStatus, raising ten property-change events may seem a little excessive. Maybe it is. Maybe the object should raise a FixtureStatusChanged event instead. Then classes with knowledge of your application domain can listen to this one event and ignore the PropertyChanged event. (You still raise the PropertyChanged event on the other properties, so that objects that don't know what a FixtureStatusChanged event means can stay current - that is, if it's still necessary for your class to implement INotifyPropertyChanged once you've implemented FixtureStatusChanged.)
A secondary comment: Most classes in the C# universe, if they implement a method that raises the Foo event, call that method OnFoo. This is an important convention: it makes the relationship between the method and the event explicit, and it makes the fact that the code that's calling the method is raising an event easy to recognize. Notify is a weak name for a method in general - notify who? of what? - and in this case it actually obfuscates something that should be made explicit. Property-change notification is tricky enough without your naming convention concealing the fact that it's happening.
Ignoring the other stuff, I'd say the Notify(null) alone is a bad practice. It's not inherently clear what that means, and to a developer working the code 5 years down the line would probably assume that it meant something else unless they happened upon the comments.
I have come across situations wherein computed properties (without setters) need to fire PropertyChangeNotification when some other property i set via a setter.
eg
double Number
{
get { return num;}
set
{
num=value;
OnPropertyChanged("Number");
OnPropertyChanged("TwiceNumber");
}
}
double TwiceNumber
{
get {return _num * 2.0;}
}
As a rule I only do it with get only properties and I don't see why in this case a property firing a change notification on the other is bad. But I think if I do it for any other case I most likely don't know what I am doing!
When creating a custom IValueConverter for a user-editable field, the Convert implementation is usually fairly straightforward.
The ConvertBack implementation is not, since (in the absence of an explicit validation rule) it has to cope with bad user input (eg. typing "hi" in a numeric input field).
If an error occurs during conversion, there doesn't seem to be any way to communicate the specific error:
ConvertBack isn't allowed to throw exceptions (if it does, the program terminates).
Returning a ValidationError doesn't do anything.
Returning DependencyProperty.UnsetValue (which the docs suggest) results in silent failure (no error UI is shown to the user, even if you've set up an error template).
Returning the original unconverted value does cause error UI to be shown, but with misleading error text.
Does anyone know of any better way to handle this sort of thing?
(Note: while defining a custom ValidationRule would work, I don't think that's a good answer, since it would basically have to duplicate the conversion logic to discover the error anyway.)
You can avoid duplicating logic by having the validation rule's first step being to call the value converter to find out if the value it's validating is even usable.
Of course, this couples your validation rules to your value converters, unless you make a validation rule that searches the binding to find out what value converter is in use. But if you start going down that road, sooner later it will probably occur to you, as it has to many, "wait, if I'm using MVVM, what am I doing screwing around with value converters?"
Edit:
If your ViewModel implements IDataErrorInfo, which is really the only way to live, it's relatively straightforward to hook a value converter into a property setter without writing a lot of property-specific validation logic.
In your ViewModel class, create two private fields:
Dictionary<string, string> Errors;
Dictionary<string, IValueConverter>;
Create them (and populate the second) in the constructor. Also, for IDataErrorInfo:
public string this[string columnName]
{
return Errors.ContainsKey(columnName)
? Errors[columnName]
: null;
}
Now implement a method like this:
private bool ValidateProperty(string propertyName, Type targetType, object value)
{
Errors[propertyName] = null;
if (!Converters.ContainsKey(propertyName))
{
return true;
}
try
{
object result = Converters[propertyName].ConvertBack(value, targetType, null, null)
return true;
}
catch (Exception e)
{
Errors[propertyName] = e.Message;
return false;
}
}
Now your property setter looks like this:
public SomeType SomeProperty
{
set
{
if (ValidateProperty("SomeProperty", typeof(SomeType), value))
{
_SomeProperty = value;
}
}
}
In MVVM it is normal to connect View to the ViewModel with data binding.
Therefore if the name of a properties changes on one of the Model objects that is databound to there is no compiler error.
When the compiler will not stop a bug, the next thing I think of is “UnitTest”, However
How do you unit test this without
spending forever writing a GUI test?
Is there a system that will check that all the properties that are bound to is valid, (without having to run the UI) that I can call in a unit test?
I am looking for something that will take the view, and then loop over all WPF controls, for each WPF control it will look at all the binding and check if they are valid.
By the way there have been a few good questions about how to make OnPropertyChanged safe, and/or how to test it (But done of these get down to the level of a WPF view.)
How to make Databinding type safe and support refactoring
Automatically INotifyPropertyChanged
workarounds for nameof() operator in C#: typesafe databinding
A Fluent Interface For Testing INotifyPropertyChanged
Automatic Class Tester will test all simple proptites and INotifyPropertyChanged
I have put a bounty on this question, as someone must have thought hard about the problem and come up with soltions.
I think I've come up with something that may work using simple reflection, and adapting some code I've used in the past (the code for the FindBindingsRecursively method was written by Martin Bennedik's as part of his Enterprise WPF Validation Control):
[TestMethod]
public void CheckWpfBindingsAreValid()
{
// instansiate the xaml view and set DataContext
var yourView = new YourView();
yourView.DataContext = YourViewModel;
FindBindingsRecursively(yourView,
delegate(FrameworkElement element, Binding binding, DependencyProperty dp)
{
var type = yourView.DataContext.GetType();
// check that each part of binding valid via reflection
foreach (string prop in binding.Path.Path.Split('.'))
{
PropertyInfo info = type.GetProperty(prop);
Assert.IsNotNull(info);
type = info.PropertyType;
}
});
}
private delegate void FoundBindingCallbackDelegate(FrameworkElement element, Binding binding, DependencyProperty dp);
private void FindBindingsRecursively(DependencyObject element, FoundBindingCallbackDelegate callbackDelegate)
{
// See if we should display the errors on this element
MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Static |
BindingFlags.Public |
BindingFlags.FlattenHierarchy);
foreach (MemberInfo member in members)
{
DependencyProperty dp = null;
// Check to see if the field or property we were given is a dependency property
if (member.MemberType == MemberTypes.Field)
{
FieldInfo field = (FieldInfo)member;
if (typeof(DependencyProperty).IsAssignableFrom(field.FieldType))
{
dp = (DependencyProperty)field.GetValue(element);
}
}
else if (member.MemberType == MemberTypes.Property)
{
PropertyInfo prop = (PropertyInfo)member;
if (typeof(DependencyProperty).IsAssignableFrom(prop.PropertyType))
{
dp = (DependencyProperty)prop.GetValue(element, null);
}
}
if (dp != null)
{
// Awesome, we have a dependency property. does it have a binding? If yes, is it bound to the property we're interested in?
Binding bb = BindingOperations.GetBinding(element, dp);
if (bb != null)
{
// This element has a DependencyProperty that we know of that is bound to the property we're interested in.
// Now we just tell the callback and the caller will handle it.
if (element is FrameworkElement)
{
callbackDelegate((FrameworkElement)element, bb, dp);
}
}
}
}
// Now, recurse through any child elements
if (element is FrameworkElement || element is FrameworkContentElement)
{
foreach (object childElement in LogicalTreeHelper.GetChildren(element))
{
if (childElement is DependencyObject)
{
FindBindingsRecursively((DependencyObject)childElement, callbackDelegate);
}
}
}
}
Really good question. Voted it up. I would like to know the answer too.
One of the best practices I know (suggested by Josh Smith, thanks Gishu for pointing to this) is having base view model class to check in the OnPropertyChanged() method whether property really exists. E.g.:
abstract class ViewModelBase
{
[Conditional("DEBUG")]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
if (this.ThrowOnInvalidPropertyName)
throw new ArgumentException(propertyName);
string msg = "Invalid property name: " + propertyName;
Debug.Fail(msg);
}
}
protected void OnPropertyChanged(string propertyName)
{
VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
But this wouldn't help you to find spelling problems in XAML. Hmm... I don't know any existing solution for this. Maybe guys from WPF Disciples could suggest something. I think I would go with PresentationTraceSources.DataBindingSource and add to his Listners collection instance of TextWriterTraceListener and then monitor output. As soon as we get an error or warning on our radar we should fail a test.
Found good example: WPF Snippet - Detecting Binding Errors
Hope this helps. At least a bit :).
Cheers, Anvaka.
I know that this is not the direct answer to your question.
If you know the name of the control element, that you expect to be bound against you can do something like the test below (using nunit). This is the rough version. But here you use expressions and explicitly test that the property is in a binding
[Test]
public void TestBindings()
{
TestBinding<IndividualSolutionViewModel, string>(x => x.Name, "Name", TextBlock.TextProperty);
}
private void TestBinding<TViewModel,TResult>(Expression<Func<TViewModel, TResult>> property, string elementName,
DependencyProperty dependencyProperty)
{
string memberName = ExpressionHelper.GetPropertyName(property); // f.ex v => v.Name will return Name
TestBinding(memberName, elementName, dependencyProperty);
}
private void TestBinding(string memberName, string elementInControlName, DependencyProperty dependencyProperty)
{
//object viewModel = new IndividualSolutionViewModel();
var view = new IndividualSolutionView();
//Assert.That(view.DataContext, Is.EqualTo(viewModel));
var element = view.FindName(elementInControlName);
Assert.That(element, Is.Not.Null, string.Format("Unable to find the element {0} in view {1}", elementInControlName, view.Name));
Assert.That(element, Is.InstanceOf(typeof(DependencyObject)));
var binding = BindingOperations.GetBinding(element as DependencyObject, dependencyProperty);
Assert.That(binding, Is.Not.Null, string.Format("Could not find a binding for the control {0}", elementInControlName));
Assert.That(binding.Path.Path, Is.EqualTo(memberName));
}
Ps. You have to add this to the app.config
<configSections>
<sectionGroup name="NUnit">
<section type="System.Configuration.NameValueSectionHandler"
name="TestRunner"></section>
</sectionGroup>
</configSections>
<NUnit>
<TestRunner>
<add value="STA" key="ApartmentState"></add>
</TestRunner>
</NUnit>
There is also this possibility, that might give you some ideas. THe gist of the idea is property names that you would be binding to are exposed as static string properties. If a binding property name changed you would get a compilation error.
I have not had the opportunity to actually test this technique myself - but it does look interesting:
http://www.codeproject.com/Articles/42036/Project-Metadata-Generation-using-T4
As Anvaka points out, using a base class for your view model that checks property names can help avoid this particular problem (though it won't tell you when your VM class does its own property-change notification and ignores the method in the base class, not that I've ever seen anything like that happen in my code).
And you can (and should) instrument your code so that things that aren't working fail in a way that's visible to you. The thing that's kind of paradoxical about this is that if you know what things may fail and you watch them, they won't, because the fact that you're watching them will keep you from making the mistakes that lead them to fail (like writing a template selector that doesn't always return a template).
But fundamentally, the view is the UI, so I would be pretty surprised to find methods of testing it that weren't also methods for testing the UI.
How are you instrumenting your UI's? In the past I've read that people have instrumented their user interfaces, but what I haven't found is examples or tips on how to instrument a UI.
By instrumenting, I mean collecting data regarding usage and performance of the system. A MSDN article on Instrumentation is http://msdn.microsoft.com/en-us/library/x5952w0c.aspx. I would like to capture which buttons users click on, what keyboard shortucts they use, what terms they use to search, etc.
How are you instrumenting your UI?
What format are you storing the instrumentation?
How are you processing the instrumented data?
How are you keeping your UI code clean with this instrumentation logic?
Specifically, I am implementing my UI in WPF, so this will provide extra challenges compared to instrumenting a web-based application. (i.e. need to transfer the instrumented data back to a central location, etc). That said, I feel the technology may provide an easier implementation of instrumentation via concepts like attached properties.
Have you instrumented a WPF application? Do you have any tips on how this can be achieved?
Edit: The following blog post presents an interesting solution: Pixel-In-Gene Blog: Techniques for UI Auditing on WPF apps
Here is an example of how I use a simple events manager to hook on to the UI events and extract key information of the events, such as name and type of UI element, name of event and the parent window's type name. For lists I also extract the selected item.
This solution only listens for clicks of controls derived from ButtonBase (Button, ToggleButton, ...) and selection changes in controls derived from Selector (ListBox, TabControl, ...). It should be easy to extend to other types of UI elements or to achieve a more fine-grained solution. The solution is inspired of Brad Leach's answer.
public class UserInteractionEventsManager
{
public delegate void ButtonClickedHandler(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowName);
public delegate void SelectorSelectedHandler(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowName, object selectedObject);
public event ButtonClickedHandler ButtonClicked;
public event SelectorSelectedHandler SelectorSelected;
public UserInteractionEventsManager()
{
EventManager.RegisterClassHandler(typeof(ButtonBase), ButtonBase.ClickEvent, new RoutedEventHandler(HandleButtonClicked));
EventManager.RegisterClassHandler(typeof(Selector), Selector.SelectionChangedEvent, new RoutedEventHandler(HandleSelectorSelected));
}
#region Handling events
private void HandleSelectorSelected(object sender, RoutedEventArgs e)
{
// Avoid multiple events due to bubbling. Example: A ListBox inside a TabControl will cause both to send the SelectionChangedEvent.
if (sender != e.OriginalSource) return;
var args = e as SelectionChangedEventArgs;
if (args == null || args.AddedItems.Count == 0) return;
var element = sender as FrameworkElement;
if (element == null) return;
string senderName = GetSenderName(element);
string parentWindowName = GetParentWindowTypeName(sender);
DateTime time = DateTime.Now;
string eventName = e.RoutedEvent.Name;
string senderTypeName = sender.GetType().Name;
string selectedItemText = args.AddedItems.Count > 0 ? args.AddedItems[0].ToString() : "<no selected items>";
if (SelectorSelected != null)
SelectorSelected(time, eventName, senderName, senderTypeName, parentWindowName, selectedItemText);
}
private void HandleButtonClicked(object sender, RoutedEventArgs e)
{
var element = sender as FrameworkElement;
if (element == null) return;
string parentWindowName = GetParentWindowTypeName(sender);
DateTime time = DateTime.Now;
string eventName = e.RoutedEvent.Name;
string senderTypeName = sender.GetType().Name;
string senderName = GetSenderName(element);
if (ButtonClicked != null)
ButtonClicked(time, eventName, senderName, senderTypeName, parentWindowName);
}
#endregion
#region Private helpers
private static string GetSenderName(FrameworkElement element)
{
return !String.IsNullOrEmpty(element.Name) ? element.Name : "<no item name>";
}
private static string GetParentWindowTypeName(object sender)
{
var parent = FindParent<Window>(sender as DependencyObject);
return parent != null ? parent.GetType().Name : "<no parent>";
}
private static T FindParent<T>(DependencyObject item) where T : class
{
if (item == null)
return default(T);
if (item is T)
return item as T;
DependencyObject parent = VisualTreeHelper.GetParent(item);
if (parent == null)
return default(T);
return FindParent<T>(parent);
}
#endregion
}
And to do the actual logging, I use log4net and created a separate logger named 'Interaction' to log user interaction. The class 'Log' here is simply my own static wrapper for log4net.
/// <summary>
/// The user interaction logger uses <see cref="UserInteractionEventsManager"/> to listen for events on GUI elements, such as buttons, list boxes, tab controls etc.
/// The events are then logged in a readable format using Log.Interaction.Info().
/// </summary>
public class UserInteractionLogger
{
private readonly UserInteractionEventsManager _events;
private bool _started;
/// <summary>
/// Create a user interaction logger. Remember to Start() it.
/// </summary>
public UserInteractionLogger()
{
_events = new UserInteractionEventsManager();
}
/// <summary>
/// Start logging user interaction events.
/// </summary>
public void Start()
{
if (_started) return;
_events.ButtonClicked += ButtonClicked;
_events.SelectorSelected += SelectorSelected;
_started = true;
}
/// <summary>
/// Stop logging user interaction events.
/// </summary>
public void Stop()
{
if (!_started) return;
_events.ButtonClicked -= ButtonClicked;
_events.SelectorSelected -= SelectorSelected;
_started = false;
}
private static void SelectorSelected(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowTypeName, object selectedObject)
{
Log.Interaction.Info("{0}.{1} by {2} in {3}. Selected: {4}", senderTypeName, eventName, senderName, parentWindowTypeName, selectedObject);
}
private static void ButtonClicked(DateTime time, string eventName, string senderName, string senderTypeName, string parentWindowTypeName)
{
Log.Interaction.Info("{0}.{1} by {2} in {3}", senderTypeName, eventName, senderName, parentWindowTypeName);
}
}
The output would then look something like this, omitting non-relevant log entries.
04/13 08:38:37.069 INFO Iact ToggleButton.Click by AnalysisButton in MyMainWindow
04/13 08:38:38.493 INFO Iact ListBox.SelectionChanged by ListView in MyMainWindow. Selected: Andreas Larsen
04/13 08:38:44.587 INFO Iact Button.Click by EditEntryButton in MyMainWindow
04/13 08:38:46.068 INFO Iact Button.Click by OkButton in EditEntryDialog
04/13 08:38:47.395 INFO Iact ToggleButton.Click by ExitButton in MyMainWindow
The following blog post gives quite a few good ideas for instrumenting a WPF application:
Techniques for UI Auditing on WPF apps.
You could consider log4net. It is a robust logging framework that exists in a single DLL. It is also done in a "non demanding" type mode so that if a critical process is going on, it won't log until resources are freed up a bit more.
You could easily setup a bunch of INFO level loggers and track all the user interaction you needed, and it wouldn't take a bug crash to send the file to yourself. You could also then log all your ERROR and FATAL code to seperate file that could easily be mailed to you for processing.
If you make use of WPF commands, each custom command could then log the Action taken. You can also log the way the command was initiated.
Perhaps the Microsoft UI Automation for WPF can help out ? Its a framework for automating your UI, perhaps it can be used to log stuff for you...
We use the Automation Framework for auto-testing our UI in WPF.
I have not yet developed using WPF.. But I would assume that its the same as most other applications in that you want to keep the UI code as light as possible.. A number of design patterns may be used in this such as the obvious MVC and Façade. I personally always try and keep the objects travelling between the UI and BL layers as light as possible, keeping them to primitives if I can.
This then helps me focus on improving the UI layer without the concerns of anything going on once I throw my (primitive) data back..
I hope I understood your question correctly, and sorry I cannot offer more contextual help with WPF.
Disclaimer: I work for the company that sells this product, not only that but I am a developer on this particular product :) .
If you are interested in a commercial product to provide this then Runtime Intelligence (a functional add on to Dotfuscator ) that injects usage tracking functionality into your .NET applications is available. We provide not only the actual implementation of the tracking functionality but the data collection, processing and reporting functionality as well.
There was recently a discussion on the Business of Software forum on this topic that I also posted in located here: http://discuss.joelonsoftware.com/default.asp?biz.5.680205.26 .
For a high level overview of our stuff see here: http://www.preemptive.com/runtime-intelligence-services.html .
In addition I am currently working on writing up some more technically oriented documentation as we realize that is an area we could definitely improve, please let me know if anyone is interested in being notified when I have completed it.