Feed Icon  

Contact

  • Bryant Likes
  • Send mail to the author(s) E-mail
  • twitter
  • View Bryant Likes's profile on LinkedIn
  • del.icio.us
Get Microsoft Silverlight
by clicking "Install Microsoft Silverlight" you accept the
Silverlight license agreement

Hosting By

Hot Topics

Tags

Open Source Projects

Archives

Ads

Enabling WPF Magic Using WCF - Part 1

Posted in WCF | WPF at Wednesday, 20 September 2006 06:25 Pacific Daylight Time

One of the magical features of WPF is the way that data bindings automagically refresh when they are bound to an object that implements INotifyPropertyChanged. You can also get this same magic by binding to a collection of objects (which implement INotifyPropertyChanged) by making the collection an ObservableCollection. This means that you can build a WPF application that is bound to a list of objects that will automatically refresh itself whenever that list changes.

But how do you keep the list itself up-to-date? One example is given in Dan Crevier's excellent series on the DataModel - View - ViewModel design pattern for WPF (starts here). In part 6, Dan uses a Timer to ping the data service to see if there is new data. While this works great for examples, in real life you probably don't want to be constantly pinging to see if there is new information. Since I'm currently working on building a WPF application that will need this kind of functionality I thought I would work through how this might work out in a real application and how what this might look like using WCF (this will be in part 2).

In order to walk before we run, let's start with a very simple WPF application that reads a list of contacts from an XML file and displays them. This will let us explore the INotifyPropertyChanged and ObservableCollection magic first. So to start, here is our XML file that lists out some contacts:

<ArrayOfContact>

    <Contact>

        <FirstName>Tom</FirstName>

        <LastName>Jones</LastName>

        <Phone>888-111-2333</Phone>

    </Contact>

    <Contact>

        <FirstName>Jill</FirstName>

        <LastName>Smith</LastName>

        <Phone>800-222-4455</Phone>

    </Contact>

    <Contact>

        <FirstName>Ed</FirstName>

        <LastName>Baker</LastName>

        <Phone>877-444-4321</Phone>

    </Contact>

</ArrayOfContact>

These contacts will be read from the file and will populate our Contact class shown below:

 6 namespace ContactApp

 7 {

 8     public class Contact : INotifyPropertyChanged

 9     {

10         public event PropertyChangedEventHandler PropertyChanged;

11 

12         private string _firstName;

13 

14         public string FirstName

15         {

16             get { return _firstName; }

17             set

18             {

19                 _firstName = value;

20                 NotifyPropertyChanged("FirstName");

21             }

22         }

23 

24         private string _lastName;

25 

26         public string LastName

27         {

28             get { return _lastName; }

29             set

30             {

31                 _lastName = value;

32                 NotifyPropertyChanged("LastName");

33             }

34         }

35 

36         private string _phone;

37 

38         public string Phone

39         {

40             get { return _phone; }

41             set

42             {

43                 _phone = value;

44                 NotifyPropertyChanged("Phone");

45             }

46         }

47 

48 

49         private void NotifyPropertyChanged(string propertyName)

50         {

51 

52             if (PropertyChanged != null)

53             {

54                 PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

55             }

56 

57         }

58 

59     }

60 }

The list of contacts will be retrieved using a very simple interface which is defined like this:

 5 namespace ContactApp

 6 {

 7     public interface IContactProvider

 8     {

 9         List<Contact> GetContacts();

10     }

11 }

Now we can define a class that reads our XML file and returns the list of contacts by implementing our interface above:

 7 namespace ContactApp

 8 {

 9     public class ContactProvider : IContactProvider

10     {

11         private string _filepath;

12 

13         public ContactProvider(string filepath)

14         {

15             _filepath = filepath;

16         }

17 

18         public List<Contact> GetContacts()

19         {

20             using (FileStream fs = new FileStream(_filepath, FileMode.Open))

21             {

22                 XmlSerializer xs = new XmlSerializer(typeof(List<Contact>));

23                 return (List<Contact>)xs.Deserialize(fs);

24             }

25         }

26     }

27 }

At this point we need to create what Dan calls the DataModel. The DataModel wraps any code that manipulates the data and makes it accessible to WPF. For our example here the DataModel will be very simple and will be an ObservableList so that our WPF window will be able to bind to the collection and refresh when it changes. At this point our DataModel looks like the following:

 6 namespace ContactApp

 7 {

 8     public class ContactDataModel : ObservableCollection<Contact>

 9     {

10         public ContactDataModel(List<Contact> contacts) : base(contacts)

11         {}

12     }

13 }

Next we create the ViewModel. The ViewModel is what our window will bind to. It will take in an instance of a ContactProvider class and expose the collection of contacts which is our ContactDataModel. Below is what this looks like:

 6 namespace ContactApp

 7 {

 8     public class ContactViewModel

 9     {

10         private ContactProvider _provider;

11         private ContactDataModel _contacts;

12 

13         public ContactViewModel(ContactProvider provider)

14         {

15             _provider = provider;

16             _contacts = new ContactDataModel(_provider.GetContacts());

17         }

18 

19         public ContactDataModel Contacts

20         {

21             get { return _contacts; }

22         }

23     }

24 }

Now we can add the data templates to our application.xaml file to tell WPF how to format the objects we have created so far.

<Application x:Class="ContactApp.App"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:local="clr-namespace:ContactApp"

    StartupUri="Window1.xaml"

    >

    <Application.Resources>

        <DataTemplate DataType="{x:Type local:Contact}">

            <StackPanel Orientation="Horizontal">

                <TextBlock Text="{Binding LastName}" />

                <TextBlock Text=", " />

                <TextBlock Text="{Binding FirstName}" />

                <TextBlock Text=" " />

                <TextBlock Text="{Binding Phone}" />

            </StackPanel>

        </DataTemplate>

        <DataTemplate DataType="{x:Type local:ContactViewModel}">

            <ListBox Name="ContactList" ItemsSource="{Binding Contacts}" />

        </DataTemplate>

    </Application.Resources>

</Application>

Finally, we can create our Window to load bind to a ContactViewModel and display our contacts:

 1 <Window x:Class="ContactApp.Window1"

 2     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

 3     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

 4     Title="ContactApp" Height="300" Width="300"

 5     >

 6     <Grid>

 7         <ContentControl x:Name="_content" />

 8     </Grid>

 9 </Window>

And here is the code behind that does the binding:

14 namespace ContactApp

15 {

16     public partial class Window1 : System.Windows.Window

17     {

18         public Window1()

19         {

20             InitializeComponent();

21             _content.Content = new ContactViewModel(new ContactProvider(@"contacts.xml"));

22         }

23     }

24 }

So this is a very incomplete application that has a lot of holes and plenty of room for improvement. However, it does what I wanted it to do in that it loads a list of contacts from an XML file and displays them. Below is a screenshot of the application as it now stands:

 

What happens if a change is made to that XML file? At this point nothing will happen. In order to track changes to the XML file we will need to implement a file system watcher on that file and then reload the list of contacts whenever that happens. In order to implement this we will first add the following code (in bold) to the ContactProvider class:

 9 public class ContactProvider : IContactProvider

10 {

11     private string _filepath;

12     private FileSystemWatcher _fileWatch;

13 

14     public event EventHandler ListChanged;

15 

16     public ContactProvider(string filepath)

17     {

18         _filepath = filepath;

19         _fileWatch = new FileSystemWatcher(

20             Path.GetDirectoryName(_filepath), Path.GetFileName(_filepath)

21             );

22         _fileWatch.Changed += new FileSystemEventHandler(OnListChanged);

23         _fileWatch.EnableRaisingEvents = true;

24     }

25 

...

35     private void OnListChanged(object sender, FileSystemEventArgs e)

36     {

37         if (ListChanged != null)

38         {

39             ListChanged(this, EventArgs.Empty);

40         }

41     }

42 }

Additionally we will modify the ContactViewModel to reload our list when it receive a ListChanged event. This will have to be done on the UI thread so we will use the Dispatcher trick demonstrated by Dan. Below is the updated ContactViewModel:

10 public class ContactViewModel

11 {

12     private ContactProvider _provider;

13     private ContactDataModel _contacts;

14     private Dispatcher _dispatcher;

15 

16     public ContactViewModel(ContactProvider provider)

17     {

18         _dispatcher = Dispatcher.CurrentDispatcher;

19         _provider = provider;

20         _provider.ListChanged += ListChanged;

21         _contacts = new ContactDataModel(_provider.GetContacts());

22     }

23 

...

29     public void ListChanged(object sender, EventArgs e)

30     {

31         List<Contact> contacts = _provider.GetContacts();

32         _dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,

33             new ThreadStart(delegate

34                 {

35                     _contacts.Clear();

36                     foreach (Contact contact in contacts)

37                         _contacts.Add(contact);

38                 }));

39     }

40 }

That's it! Now if you run the application and edit the XML file the WPF application will update as soon as you hit save. Magic! Now in a real world application you're probably not going to be using a local file, so the next step will be to migrate our Provider to WCF. This will done in part 2 since this post is already too long...

Technorati Tags: WPF WCF

Thursday, 29 March 2007 11:46:33 (Pacific Standard Time, UTC-08:00)
Great article, that's exactly what I was looking for. Any chance of having the source code posted (all 3 articles) someday?
Thanks!
Emmanuel
Thursday, 15 November 2007 02:13:52 (Pacific Standard Time, UTC-08:00)
Hi,

I realise its a while since you wrote this series (which was excellent!) but I was wonder if you could provide some advice. I've got a pretty basic ViewModel that is used to edit the properties of a single object. There are two buttons in the template for the ViewModel. They are OK and Cancel and htey are bound to commands in the ViewModel. Basically I'd like to display the ViewModel template in a dialog box and close the box when either of the buttons is hit. Problem is, I dont have any reference to the container.. never mind being able to get access to a DialogResult that I can set! Any ideas on how you go about implementing a seemingly simple modal dialog in M-V-VM??

Thanks in Advance!

N
Neil Dunlop
Thursday, 15 November 2007 06:47:32 (Pacific Standard Time, UTC-08:00)
If you are adding and removing data through your program (ie, adding and removing rss feeds from an RSS reader or Books from a library catalog)this can be accomplished much quicker and simpler by creating a Binding, using the XmlDataProvider as the source for the binding, then attaching the binding to the ItemsSource property of the ListBox. To update the ListBox, add the following to the tail end of your add/remove data methods:

1. Remove any "SelectionChanged" EventHandler from the ListBox
2. Set the SelectedIndex of the ListBox to -1
3. Call the XmlDataProvider's "Refresh" method
4. Reinstate the "SelectionChanged" EventHandler

ex: ("xData" is the XmlDataProvider, and "feedList_lbx" is the ListBox. "getItemList" is the method my SelectionChangedEventHandler calls)

feedList_lbx.SelectionChanged -= new SelectionChangedEventHandler(getItemList);
feedList_lbx.SelectedIndex = -1;
xData.Refresh();
feedList_lbx.SelectionChanged += new SelectionChangedEventHandler(getItemList);

4 lines of code.
Thursday, 01 October 2009 01:05:42 (Pacific Daylight Time, UTC-07:00)
How about the source code? Trying to learn WPF WCF and the source would help al lot!
Andy Ainsworth
Comments are closed.