Wednesday, May 28, 2008

Missing WPF Dialog windows

We have been having some fun (read defects raised) with dialog windows in our WPF app. I remember when we wrote the system that showed a custom Warning Message that we wanted it to be displayed modally and not show up in the task bar. Sounds simple so far. However, if I tabbed away from the app and came back to it, the dialog was hiding. Naughty little dialog. Our fix at the time was to set Topmost = true. This just got raised as a defect because it was top most for all apps. A bit of a pain for the users as it covered up other apps (probably the bug tracking system LOL). The fix was really simple and one the makes you feel like a bit of a tool. Window window = new Window(); window.Owner = App.Current.MainWindow; //Or the relevant window. window...... //set other properties. window.ShowDialog(); Now that we don't have an Orphaned window he is so much better behaved. Good little dialog.

Sunday, May 25, 2008

Automatic implementation of INotifyPropertyChanged

Recently I have read a couple of places that are toying with the idea of making property changed events a little bit easier. Serial Seb had some ideas and so has Paul Stovel. My take on it is if we can just decorate the Class or property with an attribute. Either
public class MyClass : INotifyPropertyChanged
{
    [Notify]
    public string MyProperty { get; set;}

    public PropertyChangedEventHandler PropertyChanged
}
or
[Notify]
public class MyClass : INotifyPropertyChanged
{
    public string MyProperty { get; set;}

    public PropertyChangedEventHandler PropertyChanged
}
Now that we have considered the desired outcome we can identify possible options. Both options that come to mind involve something more than just the .Net framework. One option is to use Injectors as Seb showed. My other option is to use PostSharp to inject the code at compile time. I might knock up some tests to see if there is any performance difference. [Update] Here is the code that I shamelessly stole from Seb. It is a post sharp implementation as we don't use Windsor on the project I am on. This code doesn't check if the value actually changed.
[Serializable]
[AttributeUsage(AttributeTargets.Assembly 
    | AttributeTargets.Class 
    | AttributeTargets.Struct 
    | AttributeTargets.Constructor 
    | AttributeTargets.Method 
    | AttributeTargets.Property 
    | AttributeTargets.Event, AllowMultiple = true, Inherited = false)]
public sealed class NotifyAspectAttribute : OnMethodBoundaryAspect
{
    public override void OnExit(MethodExecutionEventArgs eventArgs)
    {
        if (eventArgs == null)
            return;

        //Why are property sets not properties? they are methods?
        if (
            (eventArgs.Method.MemberType & System.Reflection.MemberTypes.Method)
            == System.Reflection.MemberTypes.Method
            &&
            eventArgs.Method.Name.StartsWith("set_")
            )
        {
            Type theType = eventArgs.Method.ReflectedType;
            string propertyName = eventArgs.Method.Name.Substring(4);

            // get the field storing the delegate list that are stored by the event.
            FieldInfo[] fields = theType.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
            FieldInfo field = null;
            foreach (FieldInfo f in fields)
            {
                if(f.FieldType == typeof(PropertyChangedEventHandler))
                {
                    field = f;
                    break;
                }
            }

            if (field != null)
            {
                // get the value of the field
                PropertyChangedEventHandler evHandler = field.GetValue(eventArgs.Instance) as PropertyChangedEventHandler;

                // invoke the delegate if it's not null (aka empty)
                if (evHandler != null)
                    evHandler.Invoke(eventArgs.Instance, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

Wednesday, May 21, 2008

Strong typed CAB events

Out of pure guilt of not posting for 6 months (wow lazy), I thought i had better share some love. Having been in the CAB space for a while and followed the EventTopics constants file pattern we have found that it can get very loosey goosey and have created a new pattern. Only argument less events are defined in the EventTopics constants. Ie anything that just takes EventArgs.Empty goes in here. /// /// Public CAB events that only require empty . /// public class EventTopicNames : MyCompany.Cab.Infrastructure.Interface.Constants.EventTopicNames { /// /// The CAB event string to use to launch a customer search use case. /// /// /// Only need to be provided as the event arguments. /// public const string LAUNCH_CUSTOMER_SEARCH = "MyCompany.Examples.MyCustomerModule.Interface.LaunchCustomerSearch"; } However if you need arguments passed with your event different rules apply. First, never use generic EventArgs. What a stupid idea generic EventArgs are. Take the 30seconds out of your life and create a strongly type event arg. Good practice tells us that in general it should be immutable so you can set the private field backing stores to readonly. Next provide arguments in the constructor to set any properties and then expose the properties. Also, seal the class as I bet no-one will want to inherit from you ultra specific CAB event arg. Now, the event topic name belongs on this class. This now makes the whole thing so much more cohesive and discoverable. public sealed class LaunchCustomerEditEventArgs : System.EventArgs { public const string CAB_ID = "MyCompany.Examples.MyCustomerModule.LaunchCustomerEditEventArgs"; private readonly int _customerId; public LaunchCustomerEditEventArgs(int customerId) { _customerId = customerId; } public int CustomerId { get { return _customerId; } } } Here I have used the convention of "CAB_ID" to expose the event topic name. That is just to satisfy the coding standards for the current company. I would prefer it to be "EventTopicName" . Remember this needs to be constant so that it can be used in attribute on you publications and subscriptions. As it needs to be public and constant, changing it is a breaking change and requires all your dependant assemblies to recompile. To avoid the need to change it please give it a truly unique value. A good option is to use the full type name of the EventArgs.