Sony Arouje

a programmer's log

Persisting Combo box SelectedItem – Caliburn Micro

with 7 comments

In this post I am going to explain how to persist Selected Item in a Combo box. In my application I have several screens and the user can navigate between them. I need to persist the Combobox’s Selected Item and bind it back when user navigate back to the screen. Here persisting is not db level persistence.

My application is based on Caliburn Micro, a wonderful lightweight MVVM framework.

To persist the selections, I wont create new ViewModel when ever the user clicks on the Navigation button. Only once the instance of the view model will get created. The following code shows the implementation, very simple approach.

FontPropertiesViewModel _fontPropertiesViewModel;
public void GetFontProperties()
{
    if (_fontPropertiesViewModel == null)
        _fontPropertiesViewModel = new FontPropertiesViewModel();
    ActiveScreen = _fontPropertiesViewModel;
}

Let’s examine the xaml file

<UserControl x:Class="MVVMTracer.Pages.Views.FontPropertiesView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:ca="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
    mc:Ignorable="d"
    d:DesignWidth="640" d:DesignHeight="480">
    
    <Grid x:Name="LayoutRoot" Background="Transparent" Margin="0,0,10,0">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="25"/>
                <RowDefinition Height="35"/>
                <RowDefinition Height="30"/>
                <RowDefinition Height="30"/>
                <RowDefinition Height="30"/>
                <RowDefinition Height="30"/>
                <RowDefinition Height="35"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="150"/>
                <ColumnDefinition Width="0.555*"/>
            </Grid.ColumnDefinitions>
            <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Text" VerticalAlignment="Center" FontSize="13.333" Margin="10,0,0,0" Foreground="#FF0F5DA7" Grid.ColumnSpan="2" Width="297"/>
            <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Font Type" Margin="10,0,0,0" Grid.Row="2" FontSize="11" VerticalAlignment="Center"/>
            <ComboBox x:Name="FontType" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" FontSize="11" SelectedItem="{Binding SelectedFontType}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <ca:ActionMessage MethodName="FontTypeSelectionChanged">
                            <ca:Parameter Value="{Binding ElementName=FontType,Path=SelectedItem}"></ca:Parameter>
                        </ca:ActionMessage>
                    </i:EventTrigger>
                </i:Interaction.Triggers>                
            </ComboBox>
            
            <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Font Size" Margin="10,0,0,0" Grid.Row="3" FontSize="11" VerticalAlignment="Center"/>
            <ComboBox x:Name="FontSize" Margin="0,0,0,0" Grid.Row="3" FontSize="11" HorizontalAlignment="Left" VerticalAlignment="Center" Width="53" Grid.Column="1" SelectedItem="{Binding SelectedFontSize}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <ca:ActionMessage MethodName="FontSizeSelectionChanged">
                            <ca:Parameter Value="{Binding ElementName=FontSize,Path=SelectedItem}"></ca:Parameter>
                        </ca:ActionMessage>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </ComboBox>
                        
            <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="Font Style" Margin="10,0,0,0" Grid.Row="4" VerticalAlignment="Center" FontSize="11"/>
            <ComboBox x:Name="FontStyle" Grid.Row="4" Grid.Column="1" FontSize="11" VerticalAlignment="Center" SelectedItem="{Binding SelectedFontStyle}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <ca:ActionMessage MethodName="FontStyleSelectionChanged">
                            <ca:Parameter Value="{Binding ElementName=FontStyle,Path=SelectedItem}"></ca:Parameter>
                        </ca:ActionMessage>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </ComboBox>
            <Button x:Name="Done" Content="Done"  Grid.Row="5" Grid.Column="1" Width="70" HorizontalAlignment="Right" d:LayoutOverrides="Height" VerticalAlignment="Bottom"/>
        </Grid>
    </Grid>
</UserControl>

If you examine the xaml you can see that the selected item of the combo boxes are binded to different properties, also I use ActionMessaging to notify the view model when ever a selection change happen in the Comboboxes. You might wonder why am using ActionMessaging rather than I could bind SelectedItem in two way mode. Yes I tried it first but unfortunately the Selected Item is not showing when user navigates back to the page.

Time to go through the view model

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Caliburn.Micro;
using MVVMTracer.MockServices;
using EmblemDesigner.Controls;

namespace MVVMTracer.Pages.ViewModels
{
    public class FontPropertiesViewModel:PropertyChangedBase
    {
        public FontPropertiesViewModel()
        {
            this.FontType = FontServices.GetFontFamily();
            this.FontStyle = FontServices.GetFontStyle();
        }

        private List<string> _fontType;
        public List<string> FontType
        {
            get { return _fontType; }
            private set 
            {
                _fontType = value;
                NotifyOfPropertyChange(() => FontType);
            }
        }
        private string _selectedFontType;
        public string SelectedFontType
        {
            get { return _selectedFontType; }
            private set
            {
                _selectedFontType = value;
            }
        }
        public void FontTypeSelectionChanged(object selectedItem)
        {
            this.SelectedFontType = selectedItem as string;
        }

        private List<string> _fontSize;
        public List<string> FontSize
        {
            get { return _fontSize; }
            private set
            {
                _fontSize = value;
                NotifyOfPropertyChange(() => FontSize);
            }
        }
        private string _selectedFontSize;
        public string SelectedFontSize
        {
            get { return _selectedFontSize; }
            private set
            {
                _selectedFontSize = value;
            }
        }
        public void FontSizeSelectionChanged(object selectedItem)
        {
            this.SelectedFontSize = selectedItem as string;
        }


        private List<string> _fontStyle;
        public List<string> FontStyle
        {
            get { return _fontStyle; }
            private set
            {
                _fontStyle = value;
                NotifyOfPropertyChange(() => FontStyle);
            }
        }
        private string _selectedFontStyle;
        public string SelectedFontStyle
        {
            get { return _selectedFontStyle; }
            private set
            {
                _selectedFontStyle = value;
            }
        }
        public void FontStyleSelectionChanged(object selectedItem)
        {
            this.SelectedFontStyle = selectedItem as string;
        }
    }
}

Let’s take an eg. of FontType to understand what I am doing here. In the View one of my Combo box name is FontType. Caliburn Micro is intelligent enough to bind the FontType properties in the view model to the Combo box with name FontType in the view. Really great. If you run the application, you can see the FontType combo box is populated with the fonts we added to the FontType properties.

But it’s not enough to persist the user selection. To do so we need to add some more code to the view model. You might have noticed in xaml that the ActionMessage of FontType combo is attached to FontTypeSelectionChanged. When ever the user change selection it will get notified to FontTypeSelectionChanged function in the view model. This function will intern update the SelectedFontType property. The selected SelectedFontType property is binded to FontType’s Combobox’s SelectedItem.

Below diagram shows the communication between the view and the view model what I explained above.

image

One thing I noticed here is the Auto Implemented or Automatic Properties wont work here.

If any better approach pls update me.

Advertisements

Written by Sony Arouje

September 21, 2010 at 12:15 pm

7 Responses

Subscribe to comments with RSS.

  1. Good one..But I think this should work even with Binding SelectedItem in TwoWay mode. Did you try changing the Setter to public in ViewModel properties and TwoWay binding??

    Renil Joy

    February 12, 2011 at 8:37 am

    • Yes I did, but not worked. That’s the reason I approached the way I mentioned here. It might be a problem how I did with selected Item. I will check it out once again only with SelectedItem.

      sonyarouje

      February 12, 2011 at 8:15 pm

  2. Sony, thanks for sharing your experiences with Caliburn Micro!

    About the automatic binding for ComboBox, I use Categories and SelectedCategorie (notice the ‘ie’) and that works just fine. I agree, though, that most plural conventions don’t work properly (it only removes the ‘s’ at the end), so baby-babies, man-men, bus-buses, scarf-scarves etc. don’t work. To that end, I’ve added a suggestion to Caliburn micro that you can find here:
    http://caliburnmicro.codeplex.com/workitem/92

    BTW, in other to persist a user’s selected item, I often use the regular Settings class in my applications, as shown below (I call the Save() method in my ShellViewModel.OnDeactivate method). That works fine.

    Cheers,
    Erik

    public List Categories { get; set; }

    public string SelectedCategorie {
    get { return Properties.Settings.Default.SelectedCategory; }
    set { if (Properties.Settings.Default.SelectedCategory == value) return;
    Properties.Settings.Default.SelectedCategory = value;
    NotifyOfPropertyChange(() => SelectedCategorie);
    }
    }

    miavvv

    February 12, 2011 at 8:58 pm

    • @miavvv,
      thanks for the valuable info. It’s great to hear that we can persist the selecteditem more easy than what I did. I will surely check it out.

      sonyarouje

      February 12, 2011 at 9:32 pm

  3. Hi..
    Can u give me a demonstration of how to use grid combo box column combo box selection change event using MVVM, I tried with triggers but it’s not working as expected.

    Ishara

    June 5, 2014 at 9:01 am

    • Hi Ishara,
      I dont have any source code to demo it right now. Also I haven’t used any combobox in a grid.

      Regards,
      Sony

      Sony Arouje

      June 5, 2014 at 10:07 am

  4. […] Persisting Combo box SelectedItem – Caliburn Micro … – Sony, thanks for sharing your experiences with Caliburn Micro! About the automatic binding for ComboBox, I use Categories and SelectedCategorie (notice the ‘ie …… […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: