Saturday, 7 July 2012

WPF: Customizing ComboBox

(Edit: This turned out to be a very long post so take a cup of coffee and enjoy!)
Problem statement: We need to customize the Combo Box's template by adding an 'Add' button as one of the items in the list.

Background: Let's say we have a list in our model - a list of supplier. This list of suppliers is bound to a combo box. The user needs to select a supplier to complete a business case.  Now the users ask for an 'Add' button, and they want it as one of the items in the combo box.
Approach 1: Let's consider that the list is not in model/view model but contained in XAML.
For e.g.:

            <ComboBox>
                <ComboBoxItem>Supplier 1</ComboBoxItem>
                <ComboBoxItem>Supplier 2</ComboBoxItem>
                <ComboBoxItem>Supplier 3</ComboBoxItem>
                <ComboBoxItem>Supplier 4</ComboBoxItem>
           
</ComboBox>

In this case, we can add the Button as a child of ComboBox:

            <ComboBox height="25" x:name="cmbSupplier">
                <ComboBoxItem>Supplier 1</ComboBoxItem>
                <ComboBoxItem>Supplier 2</ComboBoxItem>
                <ComboBoxItem>Supplier 3</ComboBoxItem>
                <ComboBoxItem>Supplier 4</ComboBoxItem>
                <Button click="Button_Click" Content="Add" />

           
</ComboBox>








 


The Button_Click event:

cmbSupplier.Items.Insert(cmbSupplier.Items.Count - 1, "New Supplier");

Now comes the saving part. How do we save the new supplier? We have given the 'default' list in XAML so it's kind of hard-coded which is not a good thing, it should ideally come from persistence store like database or an XML file. But say we add the default supplier's in XAML and the user can add more. So we can get the newly added suppliers from Combo Box's items list in code-behind and persist them. On loading the application again we can add these 'new' supplier again.

The downside here is that it uses code-behind and there no data binding. The Combo box is not bound to a list and we are using click event of the button not a Command.

MVVM: The above approach can be used in certain exceptional cases. Normally we use MVVM pattern and we bind the Combo box ItemsSource property to a collection in our view model. So what can we do in this case? Again we have more than one option, but first let me build the MVVM infrastructure before customizing the Combo Box.

In our model we have:

    public class Supplier
    {
        public string Name { get; set; }
        public int Code { get; set; }
    }

(We should have the notify property change as well in the properties, but I am keeping things simple here as we focus on adding a new item and not updating existing one.)

Note: I am using MVVMFoundation framework that's why you see the RelayCommand and ObservableObject.

This is our view model:
    public class ViewModel : ObservableObject
    {
        public ICommand AddSupplierCommand { get; set; }
        public ObservableCollection Suppliers { get; set; }
        public ViewModel()
        {
             Suppliers = new ObservableCollection();
             FillSuppliers(); //method to get supplier list from a store
            AddSupplierCommand = new RelayCommand(AddSupplier);
        }
        private void AddSupplier()
        {
            Suppliers.Add(new Supplier { Name = "New Supplier", Code = 999 });
        }
    }

In XAML:
<ComboBox Height="25" ItemsSource="{Binding Suppliers}">
</ComboBox>

In Window's code-behind:
     public partial class Window2 : Window
    {
        ViewModel _vm;
        public Window2()
        {
            InitializeComponent();
            _vm = new ViewModel();
            this.DataContext = _vm;
        }
    }

And we get:



So now we add a data template in XAML to show the name of supplier and not the ToString() implementation.

We change the XAML to:

            <ComboBox Height="25" ItemsSource="{Binding Suppliers}">
                <Combobox.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Margin="5,5" Text="{Binding Name}" />
                     </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>

And the result is:



Great, so now we have everything to start adding the 'add button.

Approach 2 Using a Composite Collection: Composite collections is a recent find for me. From MSDN - "Enables multiple collections and items to be displayed as a single list."

I think that's perfect for us since we want to add different type of objects to a list. Let me show you the code first and then we will discuss it.

Window resources in XAML: ("vm" is namespace of the project, ViewModel is the class name)
    <Window.Resources>
        <vm:viewmodel x:key="viewmodel"></vm:viewmodel>
    </Window.Resources>

We change the XAML to:
            <ComboBox height="25">               
                <ComboBox.ItemSsource>
                    <CompositeCollection>
                        <CollectionContainer Collection="{Binding Source={StaticResource viewmodel}, Path=Suppliers}" />
                        <Button Command="{Binding Source={StaticResource viewmodel}, Path=AddSupplierCommand}" Content="Add" />
                    </CompositeCollection>
                </Combobox.ItemsSource>
             </ComboBox>
             
Window.xaml.cs:
    public partial class Window2 : Window
    {
        public ViewModel ViewModel { get; set; }
        public Window2()
        {
            InitializeComponent();
            this.ViewModel = new ViewModel();
            this.DataContext = this.ViewModel;
            if (this.TryFindResource("viewmodel") != null)
            {
                this.Resources["viewmodel"] = this.ViewModel;
            }
          }
    }

There are couple of hacks here:

- Using CompositeCollection in XAML has a problem. It has no visual properties and is not found in Visual tree - so it doesn't inherit the data context we set in code-behind. So this <CollectionContainer Collection="{Binding Suppliers}"></CollectionContainer> doesn't work.
As a workaround we add the ViewModel object to Resources (later we will see why we are adding the entire view model not just the "Suppliers" list), and refer this resource as a StaticResource in CompositeCollection. It is worth mentioning here that you could also use MultiBinding to which you pass in the two collections and get back the full combined collection.
- We have removed the DataTemplate. This is because now we cannot use a DataTemplate since we have two different types of objects (Supplier and Button) in our collection. We can use a DataTemplateSelector but by using that here we will deviate from our topic, so I have just changed the ToString() in Supplier class to return the "Name" property for now.
- For the Button, we are setting its Command property - again from the ViewModel static resource, because we can't access the DataContext here (this why we store the entire view model in resources).

The result is:



And upon clicking 'Add' we get:



Great, here we can use databinding, MVVM pattern and get the added supplier in our Model. Only problem is using DataTemplate - which can be resolved by DataTemplateSelector. Next we will see another approach where we can actually use a DataTemplate. Other problem is adding the view model in resources.

Approach 3 Using Converters: This approach doesn't use the CompositeCollection. Some people may not like the idea of setting the DataContext and also adding ViewModel object in the resources.

So here we take a different approach - we change the Supplier list to show the 'Add' button.

Let's suppose we get this Suppliers data from our Model:
- Name: Supplier 1, Code: 100
- Name: Supplier 2, Code: 200
- Name: Supplier 3, Code: 300
- Name: Supplier 4, Code: 400

Now just after loading this data we add a "dummy" supplier to the list:
- Name: Supplier -1, Code:-1

Our XAML becomes:

In the resources we add two converters:

     <Window.Resources>
        <vm:SupplierToVisibilityConverter x:Key="supplierToVisibilityConverter" />
        <vm:SupplierToVisibilityForAddConverter x:Key="supplierToVisibilityForAddConverter" />
    </Window.Resources>

The ComboBox:

            <ComboBox Height="25" ItemsSource="{Binding Suppliers}" Style="{StaticResource ResourceKey={x:Type ComboBox}}" >
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Margin="5,5" Text="{Binding Name}" Visibility="{Binding Path=., Converter={StaticResource supplierToVisibilityConverter}}"/>
                            <Button Content="Add" Margin="5,5"
                                    Command="{Binding  RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}, Path=DataContext.AddSupplierCommand}"
                                    Visibility="{Binding Path=.,Converter={StaticResource supplierToVisibilityForAddConverter}}"/>
                        </StackPanel>
                    </DataTemplate>
                </ComboBox.ItemTemplate>               
            </ComboBox>

ViewModel:
    public class ViewModel : ObservableObject
    {
        public ICommand AddSupplierCommand { get; set; }
        public ObservableCollection Suppliers { get; set; }
        public ViewModel()
        {
            Suppliers = new ObservableCollection();
            AddSupplierCommand = new RelayCommand(AddSupplier);
            _dummy = new Supplier { Name = "-1", Code = -1 };
            FillSuppliers();
        }
        Supplier _dummy;
        private void AddSupplier()
        {
            Suppliers.Remove(_dummy);
            Suppliers.Add(new Supplier { Name = "New Supplier", Code = 999 });
            Suppliers.Add(_dummy);
        }
        void FillSuppliers()
        {
            Suppliers.Add(new Supplier { Name = "Supplier1", Code = 1 });
            Suppliers.Add(new Supplier { Name = "Supplier2", Code = 2 });
            Suppliers.Add(new Supplier { Name = "Supplier3", Code = 3 });
            Suppliers.Add(new Supplier { Name = "Supplier4", Code = 4 });
            Suppliers.Add(_dummy);
        }
    }

Explanation:

In the ViewModel, you can see that we are adding a dummy supplier with name and code of value -1. In the DataTemplate of ComboBox we have two controls - TextBlock to see the Supplier details and one Button. Now since DataTemplate is called for every item in the list, we need a way to make visible and hide the TextBlock and Button - based on the supplier item.

For this we use two converters (we could have used one but keeping things simple for now). The converters are SupplierToVisibilityConverter and SupplierToVisibilityForAddConverter:

    public class SupplierToVisibilityConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var supplier = value as Supplier;
            if (supplier != null)
            {
                if (supplier.Code == -1)
                    return Visibility.Collapsed;
                return Visibility.Visible;
            }
            return Binding.DoNothing;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return Binding.DoNothing;
        }
    }

    public class SupplierToVisibilityForAddConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            var supplier = value as Supplier;
            if (supplier != null)
            {
                if (supplier.Code == -1)
                    return Visibility.Visible;
                return Visibility.Collapsed;
            }
            return Binding.DoNothing;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return Binding.DoNothing;
        }
    }

In SupplierToVisibilityConverter we check whether the current supplier item has Code == -1, if yes then we hide it else keep it visible. As you can see in XAML this converter is used in TextBlock's visibility. Similarly we use the SupplierToVisibilityForAddConverter for finding out whether the 'Add' button should be visible or not.

Also in the Button, we have used RelativeResource to bind the Command. This is because the AddSupplierCommand is available on the ViewModel and in the DataTemplate we have access only to the current Supplier item being bound. So we refer to the DataContext of the parent ComboBox.

The output:



The disadvantages of this approach are:
- Using converters complicates the logic a little. Although they can be reused for other scenarios as well
- Adding a dummy entry in the list is also not a good idea. This is changing the Model data for a UI hack, so the developer persisting the data needs to be aware about this hack. Necessary sould also be done that in ViewModel to not let the dummy data go out.

Approach 4 Using Control Template (with attached property): Control templates are modified when we need to change the UI of the control. It gives full freedom to change the layout, behavior, look and feel of the control.

I am using this tool http://thematic.codeplex.com/ to create custom styles. If you use this tool, you will see a file named "combobox.xaml" in the list of generated files.

I am showing here the portion that relevant to our discussion:

    <!--<SnippetComboBoxStyle>-->
    <Style x:Key="{x:Type ComboBox}" TargetType="ComboBox">
        <Setter Property="SnapsToDevicePixels" Value="true"/>
        <Setter Property="OverridesDefaultStyle" Value="true"/>
        <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
        <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ComboBox">
                <Grid>
    ....
            <Popup
              Name="Popup"
              Placement="Bottom"
    ....
                    <ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
                        <StackPanel Orientation="Vertical">
                            <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained" />
                            <Button Margin="5,5" Content="Add" Command="{TemplateBinding app:CommandExtensions.Command}"
                                    Background="{StaticResource WindowBackgroundBrush}"/>
                        </StackPanel>
                    </ScrollViewer>
    .....
                    </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

I have added the lines in bold. The ComboBox has a scroll viewer to scroll the items which we find in the ScrollViewer control. A StackPanel with IsItemsHost="true" is used to display the items. So just after this stack panel we add our 'Add' button.

XAML:
        <ComboBox Height="25" ItemsSource="{Binding Suppliers}" Style="{StaticResource ResourceKey={x:Type ComboBox}}"
                vm:CommandExtensions.Command="{Binding AddSupplierCommand}" />
 
Result:



Ok so we have the button but how do we hook the Button with a command? ComboBox doesn't have a Command property which we can use here. And if we had created a custom control by inheriting the ComboBox we could have created a new Dependency Property called Command and used it.

Attached properties to the rescue. This is a perfect scenario for using attached properties, so let's create one.

Create a new class for this code:

    internal class CommandExtensions : DependencyObject
    {     
        public static ICommand GetCommand(DependencyObject obj)
        {
            return (ICommand)obj.GetValue(CommandProperty);
        }

        public static void SetCommand(DependencyObject obj, ICommand value)
        {
            obj.SetValue(CommandProperty, value);
        }

        // Using a DependencyProperty as the backing store for Command.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(CommandExtensions), new UIPropertyMetadata(null)); 
    }

In the XAML we use the new attached property:
        <ComboBox Height="25" ItemsSource="{Binding Suppliers}" Style="{StaticResource ResourceKey={x:Type ComboBox}}"
                vm:CommandExtensions.Command="{Binding AddSupplierCommand}" />

Approach 5 Using Custom Control:  Finally we could also create a custom control by inheriting the ComboBox class. (code coming soon...)


Wednesday, 27 June 2012

WPF: Create your own style

Today found this cool tool: http://thematic.codeplex.com/

Using this tool you can create your styles, just in case you don't have the expert designer, you always wished you had, in your team.

Will try to create to my own and post it here soon :-)
 

Tuesday, 26 June 2012

WPF: Selecting a style at runtime

Recently I got hold of around 6 styles and needed to try them to see which one fits perfectly in our application. We can either change the <Application.Resources>, add a <ResourceDictionary> to set the style or we could do it dynamically to see the results.

Where to get the styles from?

There are few style available on web. One is at http://wpfthemes.codeplex.com/
Or create your own using http://thematic.codeplex.com/

Using it in Application.Resources:

-> First we add the XAML file containing the style to the project.
We add the file “BureauBlack.xaml” to the project

-> In App.xaml of application we write:
<Application x:Class="StyleSelector.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary Source="BureauBlack.xaml"></ResourceDictionary>
    </Application.Resources>
</Application>
Results:
Without style image   
With:

 image  

(there is some problem with the Style, it doesn't set the Window background color)

Setting style in code

Now let's try to set the style of a window in code. Here is a method which takes in a XAML file name (containing styles and added to project). We create a new ResourceDictionary object and set it to the Window's Resource collection.
        private void SetStyle(string xamlFileName)
        {
            //clear the resources
            this.Resources.Clear();

            if (!string.IsNullOrEmpty(xamlFileName))
            {
                //load new resources
                ResourceDictionary resDict = new ResourceDictionary();
                resDict.Source = new Uri(xamlFileName, UriKind.Relative);

                //merge the resources to preserve any other resource you may have added
                this.Resources.MergedDictionaries.Add(resDict);
  
                //or to overwrite and avoid memory leak you could also do this
                //this.Resources = resDict;
             }
        }        


We can use this method in the "Set Style to:" Combo Box selection changed event.
 

Wednesday, 6 June 2012

WPF Links

Some useful links I found today:

-> The technique described in this link is useful when we want to bind to a view mode's property (or any parent's data context) from a data column. (we can't use RelativeSource there)
http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/

 -> Nice use of attached properties here:
http://www.thomaslevesque.com/2011/10/01/wpf-creating-parameterized-styles-with-attached-properties/

Sunday, 3 June 2012

Ideal skills of a developer

A rough skill set of developer with more than 5 years of experience. Do you find anything missing?
 
Practices: 
  • Test-Driven Development
  • Continuous Integration
  • Domain-Driven Design
  • Dependency Injection 
  • Agile (SCRUM) Project Methodology
  • Design Patterns
  • ReSharper
  • SCRUM
Technologies: 
  • C# 
  • .NET 4.0
  • ASP.NET MVC Framework 
  • Entity Framework or NHibernate
  • SQL Server 2005, 2008
  • WCF
  • LINQ
  • OOP 
  • JavaScript
  • jQuery
  • CSS/XHTML
  • Unit-Testing
  • Workflow Foundation
  • Debugging using WindDbg, Perfmon, DebugDiag
  • Nant, Nant-Contrib

Thursday, 17 May 2012

Programming Windows - 6th Edition

I still remember reading Programming Windows 5th edition in our college. The book was very thorough and gave a nice introduction to all the Windows programming concepts, although we were more interested in API we could use to get the password text on a window or hacking the key strokes ;-)

But it's been a long time. That was the PC era. Now we have tablets and mobile ruling our lives. New toys for new era. And so we have a new book from our guru Charlez Petzold on how to program these toys.

http://www.charlespetzold.com/blog/2012/05/Programming-Windows-6th-Edition-Preview-Ebook-Is-Here.html

What is interesting about book's offer it is only $10, but the book is not yet finished yet - only 7 chapters. Windows 8 is still under consumer preview - and so I think the book is following the release cycle of Windows 8. We live in interesting times.

I am buying this book, are you? Link to buy

Edit: Well I just bought it.

Kinect Resources

Started learning how to program Kinect. Must say we live in interesting times.
Links:
- Project done at NUS using Kinect: http://hci.comp.nus.edu.sg/?page_id=273

Books:
- Free e-book at i-Programmer
http://www.i-programmer.info/ebooks/practical-windows-kinect-in-c.html

- Meet the Kinect



- Beginning Kinect Programming with Microsoft Kinect SDK




Monday, 14 May 2012

Links to Debugging Resources

This is a post to store all links to videos, blogs, PDFs relating to debugging .NET applications. I have become particularly interested in this area and getting down to details such heaps, clr-stacks, GC, analyzing dumps is so much fun!

- Tess Ferrandez:

Previously an ASP.NET escalation engineer at Microsoft, based in Sweden, now working in developer evangelism team. But still her blog has some very good info. to get started with debugging. You should check out the labs she has posted on her blog which show how to resolve performance problems.

> Blog: If broken it is, fix it you should
> (video) Session: Debugging .NET Applications with WinDbg, Developer Conference, Sweden 2009



 - Channel 9

More recently there are 6 videos posted on Channel 9 around .NET debugging by Bradl Golnaz. I have seen two and they look good.
Diagnosing Application Issues - 01 You can find links to all the videos on this link.

- MSDN
Delay's Blog: This post has some good links to other valuable posts.

- PDFs

SOS – “Son of Strike” introduction by Mark Smith.

...more links to be added soon...

ASP.NET MVC Web Sites (for study)


Today found out Suteki Shop at this address: http://code.google.com/p/sutekishop/

Since it is open-source and uses the technologies such as ASP.NET MVC3, MVC Contrib, NHibernate, Windsor; it would be interesting to look at.

Another one is Nerd Dinner. http://www.nerddinner.com/. Its source can be found here: http://nerddinner.codeplex.com/

Happy MVCing! 

Wednesday, 2 May 2012

WPF: Dynamic Menu

In one of projects I am working on, we have a requirement of showing ‘recent files’ used by the application. The files used by the application are XML files – no custom extension.

Our goal is:

 image

For this task we need the following things:
- a RecentFiles class:
    - should contain the list of recent files
    - should have methods to load/save the list to disk (persistence)
    - should have methods to add a recent file used or delete a non-existing file
- a MenuItem (with children) to show the list

Here is the RecentFiles class:

public partial class RecentFiles
    {
          private ObservableCollection<string> filesField;
          public ObservableCollection<string> Files
          {
              get
              {
                   return this.filesField;
              }
              set
              {
                  this.filesField = value;
          }

          //members for persistence – Load, Save
         //you can find the full class definition in attachments
}

In the Windows.cs, we have a view model WindowViewModel having a property named AppRecentFiles.

Code in WindowViewModel:
      public RecentFiles AppRecentFiles { get; set; }

Code in Windows.cs:
     this._mainWindowViewModel = new MainWindowViewModel();
      this.DataContext = this._mainWindowViewModel;

Now the XAML part:

<MenuItem x:Name="mnuRecentFiles" Header="_Recent Files" ItemsSource="{Binding AppRecentFiles.Files}">
    <MenuItem.Resources>
        <Style TargetType="MenuItem">
            <Setter Property="Command" Value="{Binding ElementName=mnuRecentFiles, Path=DataContext.RecentFileChosenCommand}" />
            <Setter Property="CommandParameter" Value="{Binding}" />
        </Style>
    </MenuItem.Resources>
</MenuItem>


And you are done with the basic infrastructure.

Now all we have to do is:
- add an item to the AppRecentFiles.Files list whenever we load a new file (from the Load option in menu). Tip: add the new item at top of list so that it shows as first item on the menu.
- remove file from list if the user selects a file from 'recent files’ is not found, perhaps after a confirmation message box
- load the recent files by using RecentFiles.LoadFromFile() method – usually done during Window load or app start
- and save (persist) the list by calling RecentFiles.SaveToFile() on App close or window close

Shorts - week 3, 2022

Post with links to what I am reading: 1. A very good post on different aspects of system architecture: https://lethain.com/introduction-to-a...