RecentComments

Comment RSS

Going from Model-View-Presenter to MV-VM with WPF Commands (Part #1: MVP)

by Ioannis 6. October 2009 08:01

In this post I will try to show the application of the MVP and MV-VM programming approaches and the use of WPF commands in them. I will start with a simple Window in WPF with all the code intertwined in the code-behind file and transform it to a Window with no code at all at the code-behind file, showing the separation between the view and the logic that is achieved with MV-VM and Commands.

The Window is the following:

Window Screenshot

The "Add Client" adds a new client, the "Delete Client" removes the selected client and the "Reset Client" resets the values of the selected client. Note that the two menu items that require a client to be selected are currently disabled. This is the functionality we want to provide.

We create a support class named "ClientRepository" that will play the role of our database-datalayer in this trivial example:

public static class ClientRepository
{
    private static ObservableCollection<Client> _clients = new ObservableCollection<Client>()
    {
        new Client(){FirstName="Test1",LastName="Test1",Phone="9999999999"},
        new Client(){FirstName="Test2",LastName="Test2",Phone="9999999999"},
        new Client(){FirstName="Test3",LastName="Test3",Phone="9999999999"},
        new Client(){FirstName="Test4",LastName="Test4",Phone="9999999999"},
        new Client(){FirstName="Test5",LastName="Test5",Phone="9999999999"},
    };

    public static void AddClient(Client me)
    {
        _clients.Add(me);
    }

    public static ObservableCollection<Client> GetClients()
    {
        return _clients;
    }

    public static void DeleteClient(Client me)
    {
        _clients.Remove(me);
    }
}

Approach #1 - View and Model fully intertwined

In this approach everything will reside in the code-behind file. In the XAML file Menu Items are related to event handlers as follows:


<MenuItem Header="Add Client" Click="MenuItemAddClient_Click" />
<MenuItem Header="Delete Client" Click="MenuItemDeleteClient_Click" />
<MenuItem Header="Reset Client" Click="MenuItemEditClient_Click" />
 

The clients in the ListBox, are displayed using a DataTemplate as follows:


<DataTemplate x:Key="DataTemplate1">
  <Canvas Width="502" Height="37">
    <TextBox ... Text="{Binding Path=FirstName,Mode=TwoWay}" TextWrapping="Wrap"/>
    <TextBox ... Text="{Binding Path=LastName,Mode=TwoWay}" TextWrapping="Wrap"/>
    <TextBox ... Text="{Binding Path=Phone,Mode=TwoWay}" TextWrapping="Wrap"/>
  </Canvas>
</DataTemplate>

 The code-behind file handles all jobs, from binding the ListBox's ItemsSource to the clients' list to handling the MenuItems' events:

public partial class Window1 : Window
    {
        private Client _selectedClient = null;
        public Client SelectedClient
        {
            get { return _selectedClient; }
            set
            {
                _selectedClient = value;
                this.MenuItemEditClient.IsEnabled = (_selectedClient!=null);
                this.MenuItemDeleteClient.IsEnabled = (_selectedClient != null);
            }
        }

        public Window1()
        {
            InitializeComponent();
            ListBoxClients.ItemsSource = ClientRepository.GetClients();
        }

        private void MenuItemEditClient_Click(object sender, RoutedEventArgs e)
        {
            if (_selectedClient != null)
            {
                _selectedClient.FirstName = "RESET";
                _selectedClient.LastName = "RESET";
                _selectedClient.Phone = "9999999999";
            }
        }

        private void ListBoxClients_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            SelectedClient = ListBoxClients.SelectedItem as Client;
        }

        private void MenuItemDeleteClient_Click(object sender, RoutedEventArgs e)
        {
            if (_selectedClient != null)
            {
                ClientRepository.DeleteClient(_selectedClient);
            }
        }

        private void MenuItemAddClient_Click(object sender, RoutedEventArgs e)
        {
            ClientRepository.AddClient(new Client());
        }
    }

Note that the logic for enabling/disabling the MenuItems resides in the setter of the SelectedClient property. Additionally, the logic for the MenuItems' actions is implemented within the event handlers. This means that if we need to change the view in the future (implement another Window for the same functionailty), we practically need to do everything from scratch. Not to mention, that it is nearly impossible to test the GUI.

Approach #2 - Converting the App to MVP (Model-View-Presenter)

We want to separate the logic from the view. So we apply the following rules:

  • The View (code-behind) will not have any properties related to the Business Logic (eg the property SelectedClient should go). Those properties will reside in the Presenter Class.
  • The View will do nothing more in its event handlers other than calling a method of the presenter.
  • If the Presenter needs to do something in the View (disable the MenuItems), it will do it by calling a method of the View.
  • To decouple the implementations both View and Presenter should implement an Interface (IView and IPresenter) respectively which will be used in the method calls.

This in action is as follows. The XAML remains intact, but the code-behind file:

    public partial class Window2 : Window,IView
    {
        IPresenter myPresenter = new Presenter();

        public Window2()
        {
            myPresenter.SetView(this);

            InitializeComponent();

            myPresenter.InitGUI();
        }

        private void MenuItemEditClient_Click(object sender, RoutedEventArgs e)
        {
            myPresenter.ResetClient();
        }

        private void ListBoxClients_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            myPresenter.SetSelectedClient(ListBoxClients.SelectedItem as Client);
        }

        private void MenuItemDeleteClient_Click(object sender, RoutedEventArgs e)
        {
            myPresenter.DeleteClient();
        }
        private void MenuItemAddClient_Click(object sender, RoutedEventArgs e)
        {
            myPresenter.AddClient();
        }

        #region IView Members

        public void ToggleMenuItems(bool Enable)
        {
            this.MenuItemEditClient.IsEnabled =Enable;
            this.MenuItemDeleteClient.IsEnabled = Enable;
        }
        public void InitializeList(ObservableCollection<Client> Clients)
        {
            this.ListBoxClients.ItemsSource = Clients;
        }

        #endregion
    }

First thing to notice is that the View implements the IView interface.The IView defines two methods which (as you will see) are used by the Presenter. Second, note that the View is bound to the IPresenter interface and whenever logic need to be executed it diverts the call to a method in the Presenter. The Presenter's implementation is as follows:

 public class Presenter:IPresenter
{
    public IView MyView = null;
    private Client _selectedClient = null;

    public void AddClient()
    {
        ClientRepository.AddClient(new Client());
    }
    public void SetSelectedClient(Client me)
    {
        _selectedClient = me;
        MyView.ToggleMenuItems(_selectedClient != null);
    }
    public void InitGUI()
    {
        MyView.InitializeList (ClientRepository.GetClients());
    }
    public void DeleteClient()
    {
        if (_selectedClient != null)
        {
            ClientRepository.DeleteClient(_selectedClient);
        }
    }
    public void ResetClient()
    {
        if (_selectedClient != null)
        {
            _selectedClient.FirstName = "RESET";
            _selectedClient.LastName = "RESET";
            _selectedClient.Phone = "9999999999";
        }
    }
    public void SetView(IView view)
    {
        MyView = view;
    }
}

By using the MVP approach, we have succesfully separated the GUI from the logic. Now, if we need to test the GUI we can just implement a dummy class that implements the IView interface and attach it to the Presenter. Moreover, we can provide more than one Presenter for the same View. Finally, if we need to change the GUI, we just implement a new Window and all we have to do is make sure that the new Window implements the IView interface.

But there is still some nusty wiring to be done in the code-behind file. Is there a better approach? As it turns out for WPF there is and it ows its existence to WPF's powerful Binding Engine (its ability to have any property of the GUI data bound to something) and to the Command Pattern implemented for the event handlers in WPF.

The next post will be following by the end of the week.

 

kick it on DotNetKicks.com Shout it

Tags:

WPF

WPF Designer Error "Could not create an instance of type"

by Ioannis 20. September 2009 09:48

Suppose that in a WPF application there are some custom User Controls implemented.

During development in some windows that use the custom User Control you may find that the designer cannot load them and instead it gives the following error:

"Could not create an instance of type YOURCONTROLHERE"

This happens because the designer runs the constructor for the User Control whenever it needs to display it in design mode. Within the constructor something throws an exception. Sometimes this does not mean necessarily that the excepion will be thrown during runtime. So you need a way to allow the designer show the window but also not alter the code that runs correctly at runtime.

A way to solve this is to change your code as follows:

Original:

public UserControlConstructor()
{
    InitializeComponent();
    //Code that throws the exception
}

Change To:

public UserControlConstructor()
{
    InitializeComponent();
    if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
    {
        //Code that throws the exception
    }
}

The directive GetIsInDesignMode(this) does the trick of knowing whether the constructor is run in Design or Runtime mode.

 

 kick it on DotNetKicks.com Shout it

Tags:

WPF

NHibernate, PropertyChanged event and WPF

by Ioannis 28. August 2009 15:30

A typical implementation of an entity that supports the INotifyPropertyChanged interface for WPF Binding is for example as follows:

public class Detail:INotifyPropertyChanged
{

    private long _iD;
    public virtual long ID
    {
        get { return _iD; }
        set { _iD = value;
              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs("ID"));}
    }
 
    private string _line1;
    public virtual string Line1
    {
        get { return _line1; }
        set { _line1 = value;
              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs("Line1"));}
    }
 
    private string _line2;
    public virtual string Line2
    {
        get { return _line2; }
        set { _line2 = value;
              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs("Line2"));}
    }
 
    private Entity _relatedEntity;
    public virtual Entity RelatedEntity
    {
        get { return _relatedEntity; }
        set { _relatedEntity = value;
              if (PropertyChanged != null)
                  PropertyChanged(this, new PropertyChangedEventArgs("RelatedEntity"));}
    }
    public virtual event PropertyChangedEventHandler PropertyChanged;

 

Class Detail supports the INotfiyPropertyChanged interface and is ready for data-binding in a WPF Window. Try to bind the properties "Line1" and "Line2" to TextBoxes in a WPF Window and soon you will realize that if you load the entity from a database using NHibernate by:

 

  • Load<Detaill>(id) : The PropertyChanged events are ignored by WPF
  • Get<Detail>(id): The PropertyChanged events are processed as expected by WPF

This behaviour is due to the fact that Load returns a proxy object while Get doesn't. More seriously though if in the same window you want to bind to properties of the property RelatedEntity (eg RelatedEntity.Description)  which is instantiated by a many-to-one relationship and you use "Lazy-Loading", you will have a proxied Entity object and the PropertyChanged event will be ignored as before.

It seems that when a proxy is involved, ProperyChanged events are ignored by WPF.The obvious choice is to always use Get and disable "Lazy-Loading" which is of course something that usually is not an option.

But there seems to be another solution:

The reason the problem occurs has to do with the fact that WPF expects a PropertyChanged event from the proxy object and gets it from the actual object. This can be better explained with the following figure:

 

When the property "Line1" is changed(Bottom), a PropertyChanged event is generated that goes through the proxy to the WPF TextBox. WPF ignores the event since it is databound to the _detailProxy object and not to the _detail object. In other words it awaits events of the form PropertyChanged (_detailProxy,"Line1") and not PropertyCahnged (_detail,'Line1").

The solution is to extend the proxy implementaion so that it takes the resoonsibility of notifying WPF when a property is changed. We will keep the original implementation of the entity as is to support the NotifyPropertyChanged mechanism when NHibernate is not involved in the object creation process and we will create a new Interceptor for the proxy to support the same functionality when NHibernate creates the object for us. Moreover the process will be as non invasive as possible in case in future implementations of NHibernate the issue is resolved.

We create a new file called NotifyProprtyChangedInterceptor.cs and first, we add a bunch of .using statements (make sure you reference the appropriate libraries):

using System.ComponentModel;
using NHibernate.Proxy.Poco.Castle;
using System.Reflection;
using NHibernate.Engine;
using NHibernate.Type;
using Castle.Core.Interceptor;
using Castle.DynamicProxy.Generators;
 
using System.Collections;
using NHibernate.Proxy;
using NHibernate;

We implement in it the following class which will create our extended proxy:

// We override this class implementation to handle the proxy creation ourselves. We
// need to specify our own Interceptor when a method of our entity is called.
public class DataBindingNotifyPropertyProxyFactory : CastleProxyFactory
{
    public override INHibernateProxy GetProxy(object id, ISessionImplementor session)
    {
        // If it is not a proxy for a class do what you usually did.
        if (!IsClassProxy) return base.GetProxy(id, session);
 
        try
        {               
            CastleLazyInitializer initializer = new DataBindingInterceptor(EntityName,PersistentClass, id,
                       GetIdentifierMethod, SetIdentifierMethod,ComponentIdType, session);
           
            // Add to the list of the interfaces that the proxy class will support the INotifyPropertyChanged interface.
            // This is only needed in the case when we need to cast our proxy object as INotifyPropertyChanged interface.
            ArrayList list = new ArrayList(Interfaces);
            list.Add(typeof(INotifyPropertyChanged));
            System.Type[] interfaces = (System.Type[])list.ToArray(typeof(System.Type));
 
            //We create the proxy (for the class, Interfaces Supported, Interceptor to use)
            object generatedProxy = DefaultProxyGenerator.CreateClassProxy(PersistentClass, interfaces, initializer);
 
            initializer._constructed = true;
            return (INHibernateProxy)generatedProxy;
        }
        catch (Exception e)
        {
            log.Error("Creating a proxy instance failed", e);
            throw new HibernateException("Creating a proxy instance failed", e);
        }
    }
}

In the same file we implement the interceptor that is used by the proxy:

public class DataBindingInterceptor : CastleLazyInitializer
 {
     private PropertyChangedEventHandler subscribers = delegate { };
 
     public DataBindingInterceptor(String EntityName,Type persistentClass, object id,MethodInfo getIdentifierMethod, MethodInfo setIdentifierMethod,IAbstractComponentType aType, ISessionImplementor session)
         : base(EntityName,persistentClass, id, getIdentifierMethod, setIdentifierMethod,aType, session)
     {
     }
 
     public override void Intercept (IInvocation invocation)
     {
         // WPF will call a method named add_PropertyChanged to subscribe itself to the property changed events of
         // the given entity. The method to call is stored in invocation.Arguments[0]. We get this and add it to the
         // proxy subscriber list.
         if (invocation.Method.Name.Contains("PropertyChanged"))
         {
             PropertyChangedEventHandler propertyChangedEventHandler = (PropertyChangedEventHandler)invocation.Arguments[0];
             if (invocation.Method.Name.StartsWith("add_"))
             {
                 subscribers += propertyChangedEventHandler;
             }
             else
             {
                 subscribers -= propertyChangedEventHandler;
             }
         }
        
         // Here we call the actual method of the entity
         base.Intercept(invocation);
 
         // If the method that was called was actually a proeprty setter (set_Line1 for example) we generate the
         // PropertyChanged event for the property but with event generator the proxy. This must do the trick.
         if (invocation.Method.Name.StartsWith("set_"))
         {              
             subscribers(invocation.InvocationTarget, new PropertyChangedEventArgs(invocation.Method.Name.Substring(4)));
         }
     }
 }

Finally we implement the creator of the proxy factory in the same file:

public class ExtendedWithNotifyProxyFactoryFactory :NHibernate.Bytecode.IProxyFactoryFactory
{
    public IProxyFactory BuildProxyFactory()
    {
        return new DataBindingNotifyPropertyProxyFactory();
    }
}

And we are done! To use the new interceptor, when inititalizing NHibernate we add the following (line needed in red color):

Configuration cfg = new Configuration();
cfg.AddAssembly("NHibernateTests");
cfg.Properties[NHibernate.Cfg.Environment.ProxyFactoryFactoryClass] = typeof(ExtendedWithNotifyProxyFactoryFactory).AssemblyQualifiedName;
_sessionFactory = cfg.Configure().BuildSessionFactory();

Now we can work with proxies and WPF receives the appropriate PropertyChanged events when properties change. Whenever we want to disable this functionality we just omit the line that adds it in the configuration.

If you just need to grab the code and keep working download the following .cs file, add it to your project, add the References and the configuration in the previous paragraph (in red) and you are good to go:

NotifyPropertyChangedInterceptor.zip

This solution-code was based and insipired by the following articles:

Castle Dynamic Proxy tutorial by Krzysztof Koźmic
Extending NHibernate Proxies by Ayende Rahien
The discussion started by Arthur Dorochowicz

kick it on DotNetKicks.com

Shout it

Tags:

.NET | WPF

Powered by BlogEngine.NET 1.5.0.7

Programming Blogs - BlogCatalog Blog Directory Add to Technorati Favorites

MVP Award

Ioannis Panagopoulos





This blog is using BlogEngine.Net and is hosted in the hoster below. I have not experienced any problems installing BlogEngine.Net in the host and I am satisfied with the host's response times. Therefore I recommend it.


DiscountASP Add