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

Page 1 of 2 in the WPF category(RSS) Next Page

Links and Code from my Code Camp Session

Posted in WPF | Silverlight | Astoria at Monday, January 28, 2008 2:26 AM Pacific Standard Time

 

Thanks to everyone who came to my session, hope you enjoyed it.

First off, instead of slides I used WPF based on the example by Beatriz Costa:

Next I demoed exporting the Xaml created by the WPF application to XPS. Here is a good link that covers that topic:

After that we published our WPF application as an XBAP. Here are some links related to that:

I also ran through a quick demo of the service that I was using which was based on the project astoria code:

Finally I went through the Silverlight version of the application. I mentioned a few relevant posts on that topic:

Last but not least, here is the link to the sample code. If you need help getting it started let me know and I'll try to help out.

 

Technorati Tags: ,,,

Speaking at SoCal Code Camp this Weekend

Posted in SharePoint | WPF | Silverlight at Friday, January 25, 2008 4:11 AM Pacific Standard Time

I'll be speaking on this coming Sunday at the SoCal Code Camp at Cal State Fullerton.

My session is at 9:00 AM on Sunday and is titled Bridging the Desktop/Web Divide with WPF and Silverlight. I'm hoping to demo (still working on my code) a simple application I built in WPF and show how easy it is to port it to XBAP and Silverlight with a few potential extras (exporting to XPS and a very short demo of ADO.Net Data Services which is the data provider).

My fellow Avanaut Elmer Morales will also be presenting on Sunday afternoon on Building Stunning Sites with SharePoint 2007. Elmer has been building SharePoint 2007 sites well before the product was released and from what I've seen of his presentation so far it should be great.

I'll probably miss the Saturday presentations since I still have some work to do on my own :)

So see you at Code Camp!

 

Video.Show

Posted in ASP.Net/Web Services | .NET | WPF | Silverlight at Monday, November 12, 2007 1:01 AM Pacific Standard Time

Tim Sneath [via Lamont] shows off the new Video.Show reference application:

Video.Show is an end-to-end solution that provides a reference-quality sample for user-generated video content sites. Taking advantage of all of our latest technologies: .NET Framework 3.5, ASP.NET AJAX, LINQ, Silverlight, Expression Encoder and imageSilverlight Streaming, Video.Show provides support for uploading, encoding, tagging, viewing and commenting on videos.

I was especially interested to see how they wrote the Linq data layer since it is shared out as a service. My own recent experience with this (which I will be sharing shortly) was that it was a little difficult to do. I was happy to see that the approach I took was the same as the one Vertigo took. You end up creating a set of objects that you pass outside of the service layer which are almost replicas of the Linq objects that get generated out of the database.

Overall it looks like a great reference app. Lots of code to look at and digest. You can download the bits here.

Some Designer Love

Posted in WPF | WPF/E at Tuesday, March 13, 2007 8:43 AM Pacific Standard Time

Wow, I just came across the DesignersLove.Net blog today via Rob's post about CHATBlender which is a very cool combination of WPF/E and MSN Messenger. So I subscribed to it and since then there have been two more great posts that are worth sharing.

The first is a short tutorial about how to use Blend to do WPF/E design work. This is probably one of the top requests over in the WPF/E forums that I didn't have a good answer for and now I do. I won't spoil the read by copying any of the post since there is lots of good stuff to read over there.

The second post highlights a new WPF/E game: Rock, Paper, Scissors done by Thannap. Pretty fun little WPF/E game, go give it a try. :)

The WPF/E community is definitely growing fast!

My First WPF/E App

Posted in ASP.Net/Web Services | Avanade | WPF | WPF/E at Thursday, December 14, 2006 7:27 AM Pacific Standard Time

I've always thought that Flash was cool. When I was in college I purchased a copy of Macromedia studio at the academic price and tried to create some Flash animations for a website. However, I'm not a designer and the learning curve was too much for me at the time, so Flash didn't stick with me.

A couple of projects back I spent a lot of time with WPF since the client portion of the application was going to be built with WPF. Armed with a basic knowledge of WPF, I thought I would give WPF/E a try. One of the big advantages of WPF/E is that if you like what someone else has done you can just take a look at their XAML. So to get started on my WPF/E project I took a look at the WPF/E Egg Timer (via Tim) and the WPF Blog's Header. You can download the XAML and the JavaScript for both to see how they work. I also read through this article on getting started with WPF/E which includes IIS configuration information.

One of the projects that I've been working on in my spare time is a social website for the Avanade solution developers in Southern California. So far I don't have much to show except a cool url and now a very simple WPF/E animation. You can check it out at http://wsup.la.

It is just a place holder for now, but it was still fun to create. In addition to the links above, I also used:

Since WPF/E is so easy to create (and edit) I'm guessing we will be seeing a lot of it in the near future.

.NET 3.0 Released!!!

Posted in WF | WCF | .NET | WPF at Monday, November 06, 2006 9:14 AM Pacific Standard Time

Via ActiveWin:

The Microsoft .NET Framework 3.0 is the new managed code programming model for Windows®. It combines the power of the .NET Framework version 2.0 with new technologies for building applications that have visually compelling user experiences, seamless communication across technology boundaries, and the ability to support a wide range of business processes. These new technologies are Windows Presentation Foundation, Windows Communication Foundation, Windows Workflow Foundation, and Windows CardSpace. The .NET Framework 3.0 is included as part of the Windows Vista™ operating system; you can install it or uninstall it using Windows Features Control Panel. This redistributable package is for Windows XP and Windows Server 2003.

The download to the redistributable package is here. Looks like the final release! The final version of the SDK has also been released here.

So we have the final versions of everything .NET 3.0 except the VS extensions for WCF and WPF. Very cool!

Technorati Tags: WPF, WCF, WF

WPF Patterns

Posted in WPF at Wednesday, September 27, 2006 6:30 AM Pacific Daylight Time

If you're doing WPF development, you really need to check out Dan Crevier's series on DataModel-View-ViewModel. Right now there is no easy way to read through all his posts on the subject without navigating through them using the calendar control on his blog. So for my own reference (and for yours as well) here is the list of links in the series:

  1. DataModel-View-ViewModel pattern: 1
  2. DataModel-View-ViewModel pattern: 2
  3. DM-V-VM part 3: A sample DataModel
  4. DM-V-VM part 4: Unit testing the DataModel
  5. DM-V-VM part 5: Commands
  6. DM-V-VM part 6: Revisiting the data model
  7. DM-M-VM part 7: Encapsulating commands*
  8. DM-M-VM part 8: View Models*

In addition to Dan's work, John Gossman's work on the Model-View-ViewModel pattern which is pretty similar and is also important when it comes to understanding how to design a WPF application. He has several posts on this pattern which I've collected below:

I think they are both saying pretty much the same thing. From my limited experience with designing WPF application I'd say what they are saying is correct. You really do need the kind of seperation they are talking about to make your WPF app work well. If you hard-code the click event in the XAML code-behind, how do you extend that to the context menu, the short-cut key, etc. without making things tightly coupled?

I'm currently working on updating my Contacts Sample to be more in line with DM-V-VM and I'll be adding a few more posts to the series as soon as I get it working. In the meantime, if you want to get a good education in WPF patterns that work, read through the links above.

* I'm pretty sure the title has a typo, these are still talking about DM-V-VM.

Technorati Tags: WPF

Enabling WPF Magic Using WCF - Part 3

Posted in Sql and Xml | WCF | WPF at Thursday, September 21, 2006 6:45 AM Pacific Daylight Time

In Part 1 we created a simple WPF application that demonstrated WPF's ability to automatically update bindings and in Part 2 we extended the application to use WCF by using callbacks to notify WPF of the changes. In this part we will get rid of the XML file and move our application to use a SQL Server 2005 database for storage. In doing so we will replace the file system watcher with a SQL Notification. In order to learn about SQL Notifications I would suggest this article for a good overview and this article for code examples.

The first thing we will do is create our database. To do this I used VS Database Professional which is currently at CTP5. Below are the steps to create this database in VSDP:

  1. Create a new SQL Server 2005 project called ContactData
  2. In the project properties check the box for "Enable Service Broker"
  3. Add a new table called Contact (definition shown below)

The contact table is very similar to our contact XML data:

create table [dbo].[Contact]
(
    ContactID int identity(1,1) primary key not null, 
    FirstName nvarchar(50) not null,
    LastName nvarchar(50) not null,
    Phone nvarchar(15) not null
);

In the project I also edited the Scripts\Post-Deployment\Script.PostDeployment.sql script to add in our default contacts:

insert into Contact values ('Tom', 'Jones', '800-333-1111')
insert into Contact values ('Jill', 'Smith', '800-222-1111')
insert into Contact values ('Ed', 'Baker', '877-666-1111')
insert into Contact values ('Mary', 'Johnson', '866-777-1111')

Now we just right click our database project and select "Deploy". The database should get created and our table should get created and populated. Note: if you're not using VSDP you can still manually create the database and table and enable service broker following the instructions in the first article.

Now that we have a database to connect to we need to update our app.config file for our WCF service with the connection string. Below is the updated config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="cDB" 
       connectionString="Database=ContactData;Server=(local);
Integrated Security=SSPI;"
       providerName="System.Data.SqlClient"/>
  </connectionStrings>
  <system.serviceModel>...</system.serviceModel>
</configuration>

After we've added the connection string to the database we can reference it in our ContactProvider class as follows:

private static readonly string ConnStr = 
  ConfigurationManager.ConnectionStrings["cDB"].ConnectionString;

Now that we have a connection string we can pull the contacts from the database and return them to the client. To do this we will create a new method called GetClientsFromDb as shown below:

private List<Contact> GetContactsFromDb()
{
  List<Contact> contacts = new List<Contact>();
  using (SqlConnection conn = new SqlConnection(ConnStr))
  {
    SqlCommand cmd = new SqlCommand(
      "select ContactID, FirstName, LastName, Phone from dbo.Contact", 
      conn);
 
    conn.Open();
    IDataReader dr = cmd.ExecuteReader();
 
    while (dr.Read())
    {
      Contact c = new Contact();
      c.FirstName = dr.GetString(1);
      c.LastName = dr.GetString(2);
      c.Phone = dr.GetString(3);
      contacts.Add(c);
    }
  }
  return contacts;
}

Normally we would use something like Enterprise Library to deal with the data access, but since this is a very simple example I just used the standard SqlClient objects. All we are doing is pulling all the contacts from the database and returning them as a list of Contact objects. Notice that the t-sql is very specific with column naming and table referencing, this will be important later when we add our Sql Dependency object. At this point we could return this to the client by changing GetContacts to use this method instead of the GetContactsFromFile method. However, let's continue to move forward and add in our Sql Dependency now. Below is the updated class definition for the ContactProvider class:

namespace ContactService
{
  public class ContactProvider : IContactProvider, IDisposable
  {
    private static readonly string ConnStr = ...        
    private SqlDependency _sqlDep;
    private List<Contact> _contacts;
 
    public ContactProvider()
    {
      SqlDependency.Start(ConnStr);
    }
 
    private static List<IListChangedCallback> _callbacks = ...
 
    public List<Contact> GetContacts()
    {
      ... 
      if (_contacts == null)
      {
        _contacts = GetContactsFromDb();
      }
 
      return _contacts;
    }
 
    private List<Contact> GetContactsFromDb()
    {
      ...
      SqlCommand cmd = new SqlCommand(...);
 
      _sqlDep = new SqlDependency(cmd);
      _sqlDep.OnChange += OnListChanged;
 
      conn.Open();
      ...
    }
 
    private void OnListChanged(object sender, EventArgs e)
    {
      if (_callbacks.Count > 0)
      {
        _contacts = GetContactsFromDb();
        Action<IListChangedCallback> invoke =
          delegate(IListChangedCallback callback)
          {
            callback.OnCallback(_contacts.ToArray());
          };
        _callbacks.ForEach(invoke);
      }
    }
 
    public void Dispose()
    {
      SqlDependency.Stop(ConnStr);
    }
  }
}

I've highlighted the changed in bold, hopefully you can see them. The first thing to note is that the class now implements IDisposable. This is to enable use to call SqlDependency.Start when the class is contructed and to call SqlDependency.Stop when the class is disposed. This is done, to quote Sanchan Sahai Saxena in the article above, to "create the necessary queue, service and procedure and starts a listener to monitor the queue".

The next item to note is that when we get our initial list of contacts we are creating the dependency object using the same command. This ensures us that when any item in our list of contacts changes the dependency will be fired. We are also storing the list of contacts locally to avoid hitting the database multiple times. The dependency object's Change event is then wired to our OnListChanged event handler.

Finally we update our OnListChanged method to pull the contacts from the database and make the callback to the client with the updated list of contacts. To test the application fire up the service and then one or more of the client applications. Then open the table using SQL Server Management Studio and update/insert/delete records in the table. The results should be reflected almost instantly in the client applications. That's it!

Hopefully this has been a helpful series that demonstrates some of the cool features in both WCF and WPF (and SQL Server 2005). It has been a good learning experience for me. As we progress on our real application using some of these techniques I will try to add to this example.

Technorati Tags: WPF WCF Sql Server

Enabling WPF Magic Using WCF - Part 2

Posted in WCF | WPF at Wednesday, September 20, 2006 11:13 AM Pacific Daylight Time

In part 1 we created a very simple application that actually didn't even use WCF. The next step in the process is to migrate our ContactProvider from an in-proc class to be a WCF service. We will then use WCF callbacks to notify the application that the contact list has changed. In order to learn WCF and specifically how to use call backs I used two resources: this MSDN Magazine Article and this Rough Cuts Book, both by Juval Löwy.

The first thing to do is create a new console application and add references to System.ServiceModel and System.Runtime.Serialization. Next we will recreate both our Contact class and the ContactProvider class and interface. First, here is our new Contact class as defined in our ContactService console project:

namespace ContactService
{
  [DataContract(Namespace="ContactService")]
  public class Contact

    [DataMember]
    public string FirstName;

    [DataMember]
    public string LastName;

    [DataMember]
    public string Phone;
  }
}

The first thing you will probably notice is the DataContract/DataMember attributes. If you don't know what these are then I would suggest reading a WCF primer like the book mentioned above. The other thing is that I've changed the properties to fields and no longer implement INotifyPropertyChanged. I've switched to fields because really this is just a structure from the services point of view. The INotifyPropertyChanged will be taken care of later on. Next we create our interface as shown below:

namespace ContactService
{
  [ServiceContract]
  public interface IContactProvider
  {
    [OperationContract]
    List<Contact> GetContacts();
  }
}

This defines the WCF contract that we will be implementing to provide a list of contacts to our application. Next we need to create the ContactProvider implementation:

namespace ContactService
{
  public class ContactProvider : IContactProvider
  {
    private static readonly string Filepath = 
      ConfigurationManager.AppSettings["contacts"];

    public ContactProvider()
    {}

    public List<Contact> GetContacts() 
    { 
      using (FileStream fs = new FileStream(Filepath, FileMode.Open)) 
      { 
        DataContractSerializer dcs = 
          new DataContractSerializer(typeof(List<Contact>)); 
        return (List<Contact>)dcs.ReadObject(fs); 
      } 
    } 
  } 
}

This class looks very similar to our previous ContactProvider class. However, since this is running on the server side we don't allow the client to pass in the path to the file, we just use a value from the config file. We are also using the DataContractSerializer instead of the XmlSerializer class. This is a new serializer that is part of WCF and is more appropriate in this case because we are deserializing an object that is a DataContract. However, because we are using this class we need to add a default namespace to our XML as shown below:

<ArrayOfContact xmlns="ContactService">...</ArrayOfContact>

Now we are ready to create our host. Our host is very simple since we are using a config file to manage all the WCF settings:

namespace ContactService
{
  class Program
  {
    static void Main(string[] args)
    {
      ServiceHost host = new ServiceHost(typeof(ContactProvider));
      
      host.Open();
      Console.WriteLine("Ready to accept connections...");
      Console.ReadLine();
      host.Close();

    }
  }
}

If you run the console application you should get an error since we have not configured anything at this point. We will add an App.config file to the project and fill it up with all the settings shown below:

<?xml version="1.0" encoding="utf-8" ?>
  <configuration>
      <appSettings>
          <add key="contacts" value="c:\dev\ContactSample\contacts.xml"/>
      </appSettings>
      <system.serviceModel>
          <services>
              <service name="ContactService.ContactProvider" 
                       behaviorConfiguration="MEXGET">
                  <host>
                      <baseAddresses>
                          <add baseAddress="net.tcp://localhost:8001/"/>
                      </baseAddresses>
                  </host>
                  <endpoint
                      address="contacts"
                      binding="netTcpBinding"
                      contract="ContactService.IContactProvider" />
                  <endpoint
                      address="MEX"
                      binding="mexTcpBinding"
                      contract="IMetadataExchange" />
              </service>
          </services>
          <behaviors>
              <serviceBehaviors>
                  <behavior name="MEXGET">
                      <serviceMetadata />
                  </behavior>
              </serviceBehaviors>
          </behaviors>
      </system.serviceModel>
  </configuration>

In this file we have defined our service along with two endpoints and a behavior. The behavior enabled the metadata exchange to occur which we will use to build our client proxy. There is one endpoint for the metadata exchange interface and another for our client to connect to via TCP. At this point we should be able to start our console application and it should run. With the service running, open a command prompt in the ContactApp folder and run the following command (note: you will need to have (1) installed the Windows SDK for RC1 and (2) added the SDK bin folder to your path for this to work):

svcutil net.tcp://localhost:8001/MEX /edb /config:app.config /async /out:Contact.g.cs

This will generate two files which we will need to add to our WPF project: app.config and Contact.g.cs. Note that Contact.g.cs contains the new contact definition (I use Contact.g.cs to so that it is clear the file is generated). If you look at the definition you will notice that the generated class implements INotifyPropertyChanged. The file also contains the service contract interface and proxy class. Our next step will be to plug in these new classes to our application. First remove the Contact.cs, IContactProvider.cs, and ContactProvider.cs files from the project. Next open the ContactDataModel and add the following constructor:

public class ContactDataModel : ObservableCollection<Contact>
{
  public ContactDataModel(Contact[] contacts) : base(new List<Contact>(contacts))
  { } 

  ... 
}

We will need this constructor since our new proxy class doesn't return a generic list of contacts but an array of contacts. We could edit the generated file to change this and it would work, but I prefer to not edit generated files. Next we need to update our ContactViewModel class to use the new proxy class. In this case we will also add the IDisposable interface to our class so that we can call the Close method on the proxy. Below is the updated code for our class:

namespace ContactApp
{

    public class ContactViewModel : IDisposable
    {
        private ContactProviderClient _proxy;
        private ContactDataModel _contacts;
        private Dispatcher _dispatcher;

        public ContactViewModel()
        {

            _proxy = new ContactProviderClient();
            _dispatcher = Dispatcher.CurrentDispatcher;
            _contacts = new ContactDataModel(_proxy.GetContacts());
        }

        public ContactDataModel Contacts
        {
            get { return _contacts; }
        }

        public void Dispose()
        {
            _proxy.Close();

        }
    }
}

This is very similar to our previous ContactViewModel, but this time we are getting our list of contacts via WCF instead of loading them in-proc. You can ignore the Dispatcher for now, but we will be using it shortly. The last change we need to make before our application will work is to modify our Window1.xaml.cs file as follows:

public Window1()
{
  InitializeComponent();

  _content.Content = new ContactViewModel();

}

All we did was to remove the ContactProvider object in the constructor since the new constructor is empty. At this point you should be able to fire up the console app and then once you get the "ready" message on the console you can fire up the WPF application. You should get a list of contacts as before, but again we've removed the updating feature. So if you edit the contacts.xml file the change will not be reflected in the WPF application unless you reload it.

The next step will be to build in the notification of a change to the file into the service. When this was hosted in-proc we did this using an event. However, we are no longer in-proc so we must do things the WCF way which is a client callback. I recommend you read the MSDN article referenced above prior to continuing since this might not make sense otherwise.

The first step is to create our callback contract interface. It is important to note that this contract will be implemented by the client, not the service. Since we are reporting a change to the list of changes, our interface will pass the updated list to the client. Below is the interface:

namespace ContactService
{ 
  interface IListChangedCallback 
  { 
    [OperationContract(IsOneWay = true)] 
    void OnCallback(Contact[] contacts); 
  } 
}

Next we need to reference this contract in our original contract as a callback:

[ServiceContract(CallbackContract = typeof(IListChangedCallback))]
public interface IContactProvider
{...}

Now that we have all the proper contracts defined we can implement them in our service. Since we're still using a simple file we can just reuse our code from our previous ContactProvider (with a few changes). The first thing we need is to define the file system watcher variable and initialize it when we create our ContactProvider:

  private FileSystemWatcher _fileWatch;


  public ContactProvider()
  {
    _fileWatch = new FileSystemWatcher(
      Path.GetDirectoryName(Filepath),
      Path.GetFileName(Filepath)
      );

    _fileWatch.Changed += new FileSystemEventHandler(OnListChanged);

    _fileWatch.EnableRaisingEvents = true;
  }

When the file changes our (currently undefined) OnListChanged method will be called. The trick is that when this method is called we will need to call back to the client. Before we can call back to the client we need some kind of reference to the client's callback. In order to do this we need to grab that callback from the first call to our service (GetContacts). In order to do this we create a list to store the callbacks and we grab them during the first call using the OperationContext. I've also refactored the actual call to get the contacts out of this method for reasons that will be apparent soon. Below is our new GetContacts method and GetContactsFromFile method:

  private static List<IListChangedCallback> _callbacks = 
    new List<IListChangedCallback>();

  public List<Contact> GetContacts()
  {
     IListChangedCallback callback = 
         OperationContext.Current.GetCallbackChannel<IListChangedCallback>();

     if (_callbacks.Contains(callback) == false)
     {
         _callbacks.Add(callback);
     }

     return GetContactsFromFile();
  }  

  private List<Contact> GetContactsFromFile()
  {
     using (FileStream fs = new FileStream(Filepath, FileMode.Open))
     {
        DataContractSerializer dcs = 
            new DataContractSerializer(typeof(List<Contact>));

        return (List<Contact>)dcs.ReadObject(fs);
     }
  }

Now when the first call to GetContacts comes in we will grab the client's callback from the OperationContext which we will make use of when the file changed event is fired. Below is our OnListChanged method:

private void OnListChanged(object sender, FileSystemEventArgs e)
  {
    if (_callbacks.Count > 0)
    {
        List<Contact> contacts = GetContactsFromFile();
        Action<IListChangedCallback> invoke =
          delegate(IListChangedCallback callback)
          {
             callback.OnCallback(contacts.ToArray());
          };
        _callbacks.ForEach(invoke);
    }
  }

Of course, passing this entire list back anytime anything changes might not be the smartest move, but for this example it works. A real implementation would probably spend some time figuring out what changed and only send back the changes. Now that we've implemented everything on the server side the next step is to update the client. However, before we can do that we need to regenerate our proxy files. Start up the console application and the run the svcutil command shown above again.

The updated generated file will contain the new callback interface that we defined. The next step is to implement this interface in our ContactViewModel so that the service can call back to us. Below is the updated code with changes in bold:

namespace ContactApp
  {
      public class ContactViewModel : IContactProviderCallback, IDisposable
      {
          private ContactProviderClient _proxy;
          private ContactDataModel _contacts;
          private Dispatcher _dispatcher;
   
          public ContactViewModel()
          {
              _proxy = new ContactProviderClient(new InstanceContext(this));
              _dispatcher = Dispatcher.CurrentDispatcher;
              _contacts = new ContactDataModel(_proxy.GetContacts());
          }
   

          public ContactDataModel Contacts
          {
              get { return _contacts; }
          }
   

          public void OnCallback(Contact[] contacts)
          {
              _dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle,
                      new ThreadStart(delegate
                          {
                              _contacts.Clear();
                              foreach (Contact contact in contacts)
                                  _contacts.Add(contact);
                          }));
          }
   
          public IAsyncResult BeginOnCallback(Contact[] contacts, AsyncCallback callback, object asyncState)
          {
              throw new Exception("The method or operation is not implemented.");
          }
   
          public void EndOnCallback(IAsyncResult result)
          {
              throw new Exception("The method or operation is not implemented.");
          }
   
          public void Dispose()
          {
              _proxy.Close();
          }
      }
  }

When the contact list is updated the service will call the OnCallback method and pass us the new list of contacts. That is all there is to it. Now you get to see the magic. :)

Fire up the console app, wait for the "Ready" message, and then fire up the WPF application. Now make a change to the contacts.xml file and save your changes. Magic! The changes are reflected almost instantly.

So what's next? While storing contacts in a file might work in some situations, you're probably going to use a database in the real world. In part 3 I will demonstrate how to apply the same magic using SQL Server 2005 and query notifications.

Technorati Tags: WPF WCF

Enabling WPF Magic Using WCF - Part 1

Posted in WCF | WPF at Wednesday, September 20, 2006 6:25 AM 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

Page 1 of 2 in the WPF category(RSS) Next Page