Welcome to SqlXml Blogs Sign in | Join | Help

Bryant Likes's Blog

It's all about WebData
Enabling WPF Magic Using WCF - Part 1

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

Posted: Wednesday, September 20, 2006 1:25 PM by bryantlikes
Filed under: ,

Comments

Bryant Likes's Blog said:

In part 1 we created a very simple application that actually didn't even use WCF. The next step in the

# September 20, 2006 6:13 PM

Bryant Likes's Blog said:

In Part 1 we created a simple WPF application that demonstrated WPF's ability to automatically update

# September 21, 2006 1:45 PM

TrackBack said:

# September 22, 2006 7:32 AM

Bryant Likes's Blog said:

If you're doing WPF development, you really need to check out Dan Crevier 's series on DataModel-View-ViewModel.

# September 27, 2006 1:32 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS