Feeds:
Posts
Comments

Its been quite a while since my last post so I want to first welcome myself back before getting my hands dirty again. Welcome back me! Over the past few months I’ve shifted direction a bit from building desktop client apps with C#/WPF to creating web applications to support infrastructure at my company. Our platform of choice now is ASP.NET MVC 3 and I must say I like it quite a bit. Recently I was tasked with creating some charts on a webpage so our higher-ups could easily quantify/analyze/be confused by/or do who knows what with some of the data we have in our system. In order to simplify the task at hand, I figured why not just create a pie chart using static data and get that on a page, so here goes.

Once you have a new ASP.NET MVC 3 project created, add a new view to your /Views/Home folder and call it PieChart.cshtml. Copy and paste the code below into this new file:

@{
    DataSet myDataSet = new DataSet();
    DataTable myTable = myDataSet.Tables.Add();

    //-- Add columns to the data table
    myTable.Columns.Add("Department", typeof(string));
    myTable.Columns.Add("Number of Employees", typeof(int));

    //-- Add rows to the data table
    myTable.Rows.Add("Accounting", 7);
    myTable.Rows.Add("Purchasing", 17);
    myTable.Rows.Add("Shipping", 14);
    myTable.Rows.Add("Engineering", 25);
    myTable.Rows.Add("IT", 11);

    var dataView = new DataView(myDataSet.Tables[0]);

    var myChart = new Chart(width: 400, height: 400, themePath: "~/Content/CustomChartTheme.xml")
        .AddTitle("Employees / Department")
        .AddSeries("Default", chartType: "Pie",
                    xValue: dataView, xField: "Department",
                    yValues: dataView, yFields: "Number of Employees")
        .Write();
}

This will create a new DataSet for the pie chart that will allow us to visualize how many employees work in each department of the company. As you can see there are 5 departments and a total of 74 employees. After defining the DataSet we can declare a new Chart object. I think the code is quite self explanatory. The themePath parameter points to our custom theme file that I will discuss a bit later. However, out of the box, there are several themes you could use instead by using the theme parameter. They are as follows: blue, green, vanilla, vanilla3d, and yellow. Since I don’t like any of them, I’ll create a custom one later.

Now that we have our chart being created, we need to add an ActionResult method to the home controller that will return the proper view.


public ActionResult PieChart()
{
    return View();
}

This will show the chart on an empty page all by itself. However, we want to embed it in a specific webpage rather than have it sit all alone. For this simple example, we’re going to stick it into the /Views/Home/Index.cshtml page. You can drop it anywhere you like in the Index page by using the img source like so:

<img src="@Url.Action("PieChart")" />

Now when you run your project, you’ll see the pie chart embedded in the index page correctly.

Lastly, we’re going to set up the custom style mentioned previously. Create the CustomChartTheme.xml file and place it wherever you like in the project. For me, I put it in the Content folder, thus the themePath above is specified appropriately as: ~/Content/CustomChartTheme.xml. Paste the content below into the .xml file:

<pre><Chart BackColor="#D3DFF0"
       BackGradientStyle="TopBottom"
       BackSecondaryColor="White"
       BorderColor="26,59,105"
       BorderlineDashStyle="Solid"
       BorderWidth="2"
       Palette="BrightPastel">
  <ChartAreas>
    <ChartArea Name="Default"
               _Template_="All"
               BackColor="64,165,191,228"
               BackGradientStyle="TopBottom"
               BackSecondaryColor="White"
               BorderColor="64,64,64,64"
               BorderDashStyle="Solid"
               ShadowColor="Transparent" />
  </ChartAreas>
  <Legends>
    <Legend _Template_="All"
            BackColor="Transparent"
            Font="Trebuchet MS,8.25pt,style=Bold"
            IsTextAutoFit="False" />
   </Legends>
   <BorderSkin SkinStyle="Emboss" />
</Chart></pre>

Now you’re probably wondering where I came up with that custom theme right? Well its essentially the same as one of the default themes you can find here. Why re-invent the wheel when you can base your new custom theme entirely on one Microsoft already created? Once you’ve tweaked your custom theme you should be able to run the application and see a nice little pie chart on your /Home/Index.cshtml page that is styled how you like it. Enjoy!

Many desktop applications have their own status bar embedded and docked to the bottom of the app. Recently I was working on an application where this was the case. Periodically, messages get sent to the status bar when things happen in the app (file saved, transferring data, error reported, etc). At first, these messages would remain in the status bar until a new message was received. However, I only wanted the message to be displayed for a short duration of time, say ten seconds. This is an MVVM application and my first thought was to just play a storyboard that sets the opacity of the message to zero after 10 seconds. When a new message arrives, set the opacity back to 1, then after 10 seconds fade to zero again. It sounded simple, but I was not sure of how to play the storyboard from my ViewModel in the MVVM structure.

After doing some digging around, I came to realize that I didn’t have to do anything in the ViewModel and a property I never used before came in very handy…. so I guess I’ll tell you what that property was,😮, Binding.NotifyOnTargetUpdated. The MSDN says it “gets or sets a value that indicates whether to raise the TargetUpdated event when a value is transferred from the binding source to the binding target.” Now, lets say I set this property to true on a TextBlock.Text’s binding. In layman’s terms, when the text changes, the TargetUpdated event is thrown and I can use that to run my storyboard. How simple!! Here is an example.

<TextBlock Text="{Binding Path=StatusBarText, NotifyOnTargetUpdated=True}">
    <TextBlock.Triggers>
        <EventTrigger RoutedEvent="Binding.TargetUpdated">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:0" To="1.0" />
                    <DoubleAnimation Storyboard.TargetProperty="Opacity" Duration="0:0:2" From="1.0" To="0.0" BeginTime="0:0:10" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </TextBlock.Triggers>
</TextBlock>

Now, when the binding updates, the opacity of the text is initially set to 1. Then, after ten seconds, the opacity will begin to fade to zero in a span of two seconds. Viola! The storyboard plays when the target text is updated and there is no need to muck up the ViewModel. Happy coding!

This article is intended for people who already have a solid understanding of data binding, data templates, converters, and WPF in general. In this article I will attempt to show you how to display a collection of items in multiple ways based on some user feedback. Fore example, say you had a collection of person objects and a toggle button on the UI to show a basic or detailed view of the people. Lets say the basic view will only show a person’s first name and photo, while the detailed view would show their last name, address, and special interests (no photo). How could something like this be easily accomplished? Try using DataTriggers and DataTemplates. The following example won’t demonstrate the situation described exactly as above but it will give you the foundation to just that (as well as some other pretty cool things). Now lets get to the code.

First is the MainWindow.cs where I will create two properties. One is a collection of people and the other is an enum value that will be bound to three radio buttons that will allow us to switch between our views of the person collection. For this example, we will allow the user to display each person item as a styled RadioButton, simple TextBlock, or simple CheckBox. Here is all of the code for the MainWindow.cs file.

namespace DataTemplateSwitchingTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
       public ObservableCollection<Person> People
       {
            get
            {
                if (_people == null)
                {
                    _people = new ObservableCollection<Person>()
                    {
                        new Person("Doe", "John", "Johnny"),
                        new Person("Doe", "Jane", "Janie"),
                        new Person("Smith", "Mike", "Big Mike"),
                        new Person("Simpson", "Mark", "El Capitan"),
                    };
                }
                return _people;
            }
        }private ObservableCollection<Person> _people;
        public Element ElementView
        {
            get { return _element; }
            set
            {
                if (value == _element) return;

                _element = value;
                this.OnPropertyChanged("ElementView");
            }
        }private Element _element;

        public MainWindow()
        {
            InitializeComponent();
            ElementView = Element.RadioButtons;
            mainWindow.DataContext = this;
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                handler(this, e);
            }
        }

        #endregion
    }
}

Our Element enum consists of three values, RadioButtons, TextBlocks, and CheckBoxes. This enum allows us to switch the view of our ItemsControl’s content. Below is Xaml for the layout of the page. It is very simple and contains only three radio buttons and the ItemsControl to display the people collection. I will comment on the DataTemplates following the code. I have also not included the SquareRadioButton style here as it is not pertinent to the article but you can find it in the link to the sample project at the end of the article. Here is the MainWindow.xaml.

<Window.Resources>
    <Local:EnumToBooleanConverter x:Key="EnumToBooleanConverter"/>

    <DataTemplate x:Key="RadioButtonTemplate">
        <RadioButton Style="{StaticResource SquareRadioButtonStyle}"
                     Content="{Binding FirstName}"
                     GroupName="People"/>
    </DataTemplate>

    <DataTemplate x:Key="TextBlockTemplate">
        <TextBlock Text="{Binding LastName}"
                   HorizontalAlignment="Center"/>
    </DataTemplate>

    <DataTemplate x:Key="CheckBoxTemplate">
        <CheckBox Content="{Binding Nickname}"
                  HorizontalAlignment="Center"
                  Margin="3">
        </CheckBox>
    </DataTemplate>

    <DataTemplate x:Key="PersonItemTemplate">
        <ContentPresenter x:Name="PeoplePresenter"
                          Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}"
                          ContentTemplate="{StaticResource RadioButtonTemplate}"/>
        <DataTemplate.Triggers>
            <DataTrigger Binding="{Binding ElementName=mainWindow, Path=DataContext.ElementView, UpdateSourceTrigger=PropertyChanged}"
                         Value="{x:Static Local:Element.RadioButtons}">
                <Setter TargetName="PeoplePresenter"
                        Property="ContentTemplate"
                        Value="{StaticResource RadioButtonTemplate}"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=mainWindow, Path=DataContext.ElementView, UpdateSourceTrigger=PropertyChanged}"
                         Value="{x:Static Local:Element.TextBlocks}">
                <Setter TargetName="PeoplePresenter"
                        Property="ContentTemplate"
                        Value="{StaticResource TextBlockTemplate}"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding ElementName=mainWindow, Path=DataContext.ElementView, UpdateSourceTrigger=PropertyChanged}"
                         Value="{x:Static Local:Element.CheckBoxes}">
                <Setter TargetName="PeoplePresenter"
                        Property="ContentTemplate"
                        Value="{StaticResource CheckBoxTemplate}"/>
            </DataTrigger>
        </DataTemplate.Triggers>
    </DataTemplate>

</Window.Resources>

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="50" />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <RadioButton Content="RadioButtons"
                 Style="{StaticResource SquareRadioButtonStyle}"
                 IsChecked="{Binding ElementView, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Local:Element.RadioButtons}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <RadioButton Grid.Column="1"
                 Content="TextBlocks"
                 Style="{StaticResource SquareRadioButtonStyle}"
                 IsChecked="{Binding ElementView, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Local:Element.TextBlocks}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <RadioButton Grid.Column="2"
                 Content="CheckBoxes"
                 Style="{StaticResource SquareRadioButtonStyle}"
                 IsChecked="{Binding ElementView, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Local:Element.CheckBoxes}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

    <Grid Grid.Row="1" Grid.ColumnSpan="3" VerticalAlignment="Center" HorizontalAlignment="Center">
        <ItemsControl Width="200" Height="200" Margin="0"
                      ItemsSource="{Binding People}"
                      ItemTemplate="{StaticResource PersonItemTemplate}"/>
    </Grid>

</Grid>

As you can see, there are four data templates. The RadioButtonTemplate, TextBlockTemplate, and CheckBoxTemplate will  change the way a person item is displayed to a RadioButton, TextBlock, and CheckBox (obviously ;o)). Now comes the important part, the PersonItemTemplate. This template has a ContentPresenter that sets the default DataTemplate for a person item to the RadioButtonTemplate. Now, using DataTriggers and property setters, we can change the template to be used when the ElementView enum property changes. Thus, when the user clicks on the CheckBoxes radio button, the people items will then be displayed as check boxes showing just a nickname. The user can click on the TextBoxes radio button and will see only plain text showing last names. Finally, the default template shows each person item as styled radio button showing only first names on the button. Here are some screenshots showing the three views.

Here is a link to the sample project. Happy coding!!

First off, what the fork is a Human Interface Device? Well, you are potentially using two right now, your keyboard and mouse. When referring to an HID we are usually speaking of a USB-HID, or a device connected to your PC that supports bi-directional communication. Wait, how does your keyboard support bi-directional communication? Well, when Caps Lock isn’t there a light to notify you of that? In fact, on your Windows OS, if you go to Start > Control Panel > Device Manager, and expand the Keyboards Node, there is a good chance you’ll see a HID Keyboard Device listed. While there, expand the Human Interface Devices node to see what other HIDs are currently connected if any. For more general information on Human Interface Devices have a look here.

Now, if you’ve ever written an application that requires to you to communicate with one of these HIDs, then you probably have to go find the correct target device first. I’m going to show you how to do that in the following sample. First, we need a class to give us access to functions like SetupDiGetDeviceInterfaceDetail, SetupDiEnumDeviceInterfaces, and a few others. Much of the functionality in the following class, and the others, originated from a nice codeplex project here. Now for the HIDImports.cs code.

//////////////////////////////////////////////////////////////////////////////////
//	HIDImports.cs
//	For more information: http://wiimotelib.codeplex.com/
//////////////////////////////////////////////////////////////////////////////////
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace CreateBinaryFromXml
{
        public class HIDImports
        {
            //Flags controlling what is included in the device information set built by SetupDiGetClassDevs
            internal const int DIGCF_DEFAULT = 0x00000001;   //only valid with DIGCF_DEVICEINTERFACE
            internal const int DIGCF_PRESENT = 0x00000002;
            internal const int DIGCF_ALLCLASSES = 0x00000004;
            internal const int DIGCF_PROFILE = 0x00000008;
            internal const int DIGCF_DEVICEINTERFACE = 0x00000010;

            [Flags]
            internal enum EFileAttributes : uint
            {
                   Readonly         = 0x00000001,
                   Hidden           = 0x00000002,
                   System           = 0x00000004,
                   Directory        = 0x00000010,
                   Archive          = 0x00000020,
                   Device           = 0x00000040,
                   Normal           = 0x00000080,
                   Temporary        = 0x00000100,
                   SparseFile       = 0x00000200,
                   ReparsePoint     = 0x00000400,
                   Compressed       = 0x00000800,
                   Offline          = 0x00001000,
                   NotContentIndexed= 0x00002000,
                   Encrypted        = 0x00004000,
                   Write_Through    = 0x80000000,
                   Overlapped       = 0x40000000,
                   NoBuffering      = 0x20000000,
                   RandomAccess     = 0x10000000,
                   SequentialScan   = 0x08000000,
                   DeleteOnClose    = 0x04000000,
                   BackupSemantics  = 0x02000000,
                   PosixSemantics   = 0x01000000,
                   OpenReparsePoint = 0x00200000,
                   OpenNoRecall     = 0x00100000,
                   FirstPipeInstance= 0x00080000
            }

            [StructLayout(LayoutKind.Sequential)]
            internal struct SP_DEVINFO_DATA
            {
                   public uint cbSize;
                   public Guid ClassGuid;
                   public uint DevInst;
                   public IntPtr Reserved;
            }

            [StructLayout(LayoutKind.Sequential)]
            internal struct SP_DEVICE_INTERFACE_DATA
            {
                   public int cbSize;
                   public Guid InterfaceClassGuid;
                   public int Flags;
                   public IntPtr RESERVED;
            }

            [StructLayout(LayoutKind.Sequential, Pack = 1)]
            internal struct SP_DEVICE_INTERFACE_DETAIL_DATA
            {
                   public UInt32 cbSize;
                   [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
                   public string DevicePath;
            }

            [StructLayout(LayoutKind.Sequential)]
            public struct HIDD_ATTRIBUTES
            {
                   public int Size;
                   public short VendorID;
                   public short ProductID;
                   public short VersionNumber;
            }

            [DllImport(@"hid.dll", CharSet=CharSet.Auto, SetLastError = true)]
            internal static extern void HidD_GetHidGuid(out Guid gHid);

            [DllImport("hid.dll")]
            internal static extern Boolean HidD_GetAttributes(IntPtr HidDeviceObject, ref HIDD_ATTRIBUTES Attributes);

            [DllImport("hid.dll")]
            internal extern static bool HidD_SetOutputReport(IntPtr HidDeviceObject, byte[] lpReportBuffer, uint ReportBufferLength);

            [DllImport(@"setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
            internal static extern IntPtr SetupDiGetClassDevs(
                   ref Guid ClassGuid,
                   [MarshalAs(UnmanagedType.LPTStr)] string Enumerator,
                   IntPtr hwndParent,
                   UInt32 Flags);

            [DllImport(@"setupapi.dll", CharSet=CharSet.Auto, SetLastError = true)]
            internal static extern Boolean SetupDiEnumDeviceInterfaces(
                   IntPtr hDevInfo,
                   IntPtr devInvo,
                   ref Guid interfaceClassGuid,
                   UInt32 memberIndex,
                   ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);

            [DllImport(@"setupapi.dll", SetLastError = true)]
            internal static extern Boolean SetupDiGetDeviceInterfaceDetail(
                   IntPtr hDevInfo,
                   ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
                   IntPtr deviceInterfaceDetailData,
                   UInt32 deviceInterfaceDetailDataSize,
                   out UInt32 requiredSize,
                   IntPtr deviceInfoData);

            [DllImport(@"setupapi.dll", SetLastError = true)]
            internal static extern Boolean SetupDiGetDeviceInterfaceDetail(
                   IntPtr hDevInfo,
                   ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
                   ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData,
                   UInt32 deviceInterfaceDetailDataSize,
                   out UInt32 requiredSize,
                   IntPtr deviceInfoData);

            [DllImport(@"setupapi.dll", CharSet=CharSet.Auto, SetLastError = true)]
            internal static extern UInt16 SetupDiDestroyDeviceInfoList(IntPtr hDevInfo);

            [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
            internal static extern SafeFileHandle CreateFile(
                   string fileName,
                   [MarshalAs(UnmanagedType.U4)] FileAccess fileAccess,
                   [MarshalAs(UnmanagedType.U4)] FileShare fileShare,
                   IntPtr securityAttributes,
                   [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
                   [MarshalAs(UnmanagedType.U4)] EFileAttributes flags,
                   IntPtr template);

            [DllImport("kernel32.dll", SetLastError=true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool CloseHandle(IntPtr hObject);
       }
}

And now on to the fun part. Here is where we enumerate through the connected HIDs. There are a bunch of comments in the code that should guide you through what is going on.

public static string EnumerateHids()
{
    StringBuilder details = new StringBuilder();
    HIDImports.HIDD_ATTRIBUTES deviceAttributes;
    Guid guid;
    uint index = 0;

    // get the GUID of the HID class
    HIDImports.HidD_GetHidGuid(out guid);

    // get a handle to all devices that are part of the HID class
    IntPtr hDevInfo = HIDImports.SetupDiGetClassDevs(ref guid, null, IntPtr.Zero, HIDImports.DIGCF_DEVICEINTERFACE);

    // create a new interface data struct and initialize its size
    HIDImports.SP_DEVICE_INTERFACE_DATA diData = new HIDImports.SP_DEVICE_INTERFACE_DATA();
    diData.cbSize = Marshal.SizeOf(diData);

    // get a device interface to a single device (enumerate all devices)
    while (HIDImports.SetupDiEnumDeviceInterfaces(hDevInfo, IntPtr.Zero, ref guid, index, ref diData))
    {
        UInt32 size = 0;

        // get the buffer size for this device detail instance (returned in the size parameter)
        HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, IntPtr.Zero, 0, out size, IntPtr.Zero);

        // create a detail struct and set its size
        HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA diDetail = new HIDImports.SP_DEVICE_INTERFACE_DETAIL_DATA();

        //On Win x86, cbSize = 5, On x64, cbSize = 8
        diDetail.cbSize = (IntPtr.Size == 8) ? (uint)8 : (uint)5;

        // actually get the detail struct
        if (HIDImports.SetupDiGetDeviceInterfaceDetail(hDevInfo, ref diData, ref diDetail, size, out size, IntPtr.Zero))
        {
            // open a read/write handle to our device using the DevicePath returned
            SafeHandle safeHandle = HIDImports.CreateFile(diDetail.DevicePath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, HIDImports.EFileAttributes.Overlapped, IntPtr.Zero);

            // create an attributes struct and initialize the size
            deviceAttributes = new HIDImports.HIDD_ATTRIBUTES();
            deviceAttributes.Size = Marshal.SizeOf(deviceAttributes);

            // get the attributes of the current device
            if (HIDImports.HidD_GetAttributes(safeHandle.DangerousGetHandle(), ref deviceAttributes))
            {
                details.AppendLine("--- HID DEVICE FOUND ---");
                details.AppendLine(String.Format("ProductID: 0x{0}", deviceAttributes.ProductID.ToString("X4")));
                details.AppendLine(String.Format("VendorID: 0x{0}", deviceAttributes.VendorID.ToString("X4")));
                details.AppendLine(String.Format("VersionNumber: 0x{0}", deviceAttributes.VersionNumber.ToString("X4")));
                details.AppendLine(String.Format("Size: 0x{0}", deviceAttributes.Size.ToString("X4")));
                details.AppendLine();
            }
        }
        index++;
    }
    return details.ToString();
}

So as you can see, we’re looping over all HIDs and creating a string to let you know which devices are connected and what their attributes are. Now all you have to do is connect to the device that matches your ProductId, VendorId, etc. I may go through doing this in a follow up post, but that’s it for now. Again, I would like to attribute the bulk of this work to the WiiMote codeplex project referenced earlier in the post. Enjoy!!

Extension Methods Example

Have you ever wanted to add a method to an existing type without recompiling? Well in C# 3.0 you were given that ability with extension methods. On the MSDN it states that “extension methods are a special kind of static method, but they are called as if they were instance methods on the extended type.” I am going to give you a very simple example. Lets say you are creating a music management application and you have a class called Artist that looks like the following:

namespace MusicApp
{
     public class Artist
     {
          #region Properties

          public DateTime BirthDate { get; set; }
          public string FirstName { get; set; }
          public string LastName { get; set; }

          #endregion

          #region Constructor(s)

          public Artist(string lastName, string firstName, DateTime birthDate)
          {
               this.BirthDate = birthDate;
               this.LastName = lastName;
               this.FirstName = firstName;
          }

          #endregion
     }
}

As you can see the artist class has a birth date. What if we wanted to find the age of the artist? We could easily add a method to the class to do so, but lets use an extension method instead. We’ll be placing the code for our extension method inside a new public static class conveniently called ExtensionMethods. Here is the code:

static class ExtensionMethods
{
     public static int Age(this Artist artist)
     {
          DateTime now = DateTime.Now;
          int yearsPassed = DateTime.Now.Year - artist.BirthDate.Year;

          if (now.Month < artist.BirthDate.Month || (now.Month == artist.BirthDate.Month && now.Day < artist.BirthDate.Day))
          {
               yearsPassed--;
          }

          return yearsPassed;
     }
}

Please note that the first parameter to the extension method specifies the type the method operates on and is preceded by the this modifier. In order to use the new extension method we must import the namespace it was defined in. We are then able to determine an artist’s age like so. Enjoy!

Artist tomPetty = new Artist("Petty", "Tom", new System.DateTime(1950, 10, 20));
int age = tomPetty.Age();

Most of us have used ObjectDataProvider to create an object to use as a binding source in Xaml. However, many of you may have never used the XmlDataProvider to do the same. In this post I am going to explain how to use an external XML file to populate a list of ComboBox options. I will then bind a property to the currently selected item of the ComboBox.

The reason I find this so useful is when your application’s end user may need things to frequently change in the future, but has no clue as to how in the present. For example, lets say they have a list of colors that need to be available for their automobile paint. Well next week maroon is the new thing. Do you really want to update your app and re-publish? Wouldn’t you rather give them the ability to just add a new node in an XML file that is sitting on a network drive (or the web) that they have access to? Well now I’m going to show you how.

First we must make an XML file to contain our list of color options. We’ll call the file PaintColors.xml and place it on the root C:\ drive for now (it could be on a network drive, the web, etc.). Here are the contents of the file.


<?xml version="1.0" encoding="utf-8"?>
<PaintColors>
   <Color Name="None"/>
   <Color Name="Red"/>
   <Color Name="White"/>
   <Color Name="Blue"/>
   <Color Name="Maroon"/>
</PaintColors>

Next we must create a new WPF Application. For now we’ll call it XmlDataProviderTest. Once the application is created, we’ll add some code to the MainWindow.xaml.cs file. We need to create a CurrentColor property and give it property change notification by implementing the INotifyPropertyChanged interface. We are going to assign the DataContext as well as the initial CurrentColor value inside the MainWindow constructor. Here is all of the code for the aforementioned.


using System.Windows;
using System.ComponentModel;

namespace XmlDataProviderTest
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
            CurrentColor = "Blue";
        }

        public string CurrentColor
        {
            get {  return _currentColor; }
            set
            {
                if (value != _currentColor)
                {
                    _currentColor = value;
                    OnPropertyChanged("CurrentColor");
                }
            }
        }private string _currentColor;

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        void OnPropertyChanged(string propName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged(
                    this, new PropertyChangedEventArgs(propName));
        }

        #endregion

   }
}

Finally we must place a ComboBox and TextBox on our MainWindow.xaml file. This is also where the XmlDataProvider will be declared. In the following code you’ll notice the Source property of the XmlDataProvider points to the location of the PaintColors.xml file on the C:\ drive. As mentioned before, this could point to a network drive, URL, embedded XML file, etc. The XPath property is set here as well. Please note that the XmlDataProvider’s default value for IsAsynchronous is True. This is because when retrieving XML data from RSS feeds or external XML files it is expected to take a while. This is the opposite of an ObjectDataProvider whose default IsAsynchronous value is set to false. The reason for this default is when retrieving data for an ObjectDataProvider we expect retrieval of the source to be very quick. I am setting IsAsynchronous here to false because our XML file is on a network share and contains very little information and thus should be quick to access.

Please note the binding in the ComboBox. The ItemsSource is bound to our PaintColors XmlData. We have chosen to display the Name attribute in the ComboBox and make it the selected value. Finally we are binding our SelectedValue to the CurrentColor string property we previously created in the MainWindow.xaml.cs file. To show everything is working well, I have bound the CurrentColor to a TextBox so we can see it update on the fly.


<Window x:Class="XmlDataProviderTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">

    <Window.Resources>
        <XmlDataProvider x:Key="PaintColors" Source="C:\PaintColors.xml" XPath="PaintColors" IsAsynchronous="False" />
    </Window.Resources>

    <StackPanel HorizontalAlignment="Center">
        <ComboBox Grid.Column="4" Grid.Row="2" Width="200" Height="25" Margin="5"
                  ItemsSource="{Binding Source={StaticResource PaintColors}, XPath=./Color}"
                  DisplayMemberPath="@Name"
                  SelectedValuePath="@Name"
                  SelectedValue="{Binding CurrentColor}"/>
        <StackPanel Orientation="Horizontal">
            <Label Content="Current Color:"/>
            <TextBox Text="{Binding CurrentColor, UpdateSourceTrigger=PropertyChanged}" Width="120"/>
        </StackPanel>
    </StackPanel>
</Window>

I hope this provides you all with a clean and simple introduction to XmlDataProviders. For more on synchronous and asynchronous binding have a look at Bea Stollnitz’s blog post here. Enjoy!!

Well I hope the title of this post is fairly self explanatory. In this example I am going to show you how to first populate a ComboBox with the values in an enumeration. I am then going to databind a property of type enum to the SelectedItem of the ComboBox. If you would like to extend the sample and sort the values displayed in the ComboBox I suggest you check out this post by the almighty Josh Smith. Now on to the code.

First off, here is my animal enumeration:

public enum Animal
{
     Bear,
     Cheetah,
     Deer,
     Elehpant,
}

Here is my MainWindow.cs.

public partial class MainWindow : Window, INotifyPropertyChanged
{
     public Animal MyAnimal
     {
          get { return _myAnimal; }
          set
          {
               if (_myAnimal != value)
               {
                    _myAnimal = value;
                    OnPropertyChanged("MyAnimal");
               }
          }
     }private Animal _myAnimal;

     public MainWindow()
     {
          InitializeComponent();
     }

     #region INotifyPropertyChanged Members

     public event PropertyChangedEventHandler PropertyChanged;

     void OnPropertyChanged(string propName)
     {
          if (this.PropertyChanged != null)
               this.PropertyChanged(
                    this, new PropertyChangedEventArgs(propName));
     }

     #endregion
}

Finally, here is the MainWindow.xaml where I am using an ObjectDataProvider and GetValues to provide the content for my ComboBox’s ItemsSource. When you run the app and change the SelectedItem in the ComboBox, you should see the TextBox update with the newly selected animal. Enjoy!

<Window x:Class="ComboBoxBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ComboBoxBinding"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="125" Width="200"
        x:Name="mainWindow">

     <Window.Resources>
          <ObjectDataProvider x:Key="Animals" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
               <ObjectDataProvider.MethodParameters>
                    <x:Type TypeName="local:Animal" />
               </ObjectDataProvider.MethodParameters>
          </ObjectDataProvider>
     </Window.Resources>

     <Grid Background="LightBlue" DataContext="{Binding ElementName=mainWindow}">
          <StackPanel HorizontalAlignment="Center">
               <ComboBox ItemsSource="{Binding Source={StaticResource Animals}}"
                         SelectedItem="{Binding MyAnimal}"
                         Margin="10"/>

               <TextBox Text="{Binding MyAnimal}" Width="100"/>
          </StackPanel>
     </Grid>
</Window>