#CaliburnMicro : WPF Double Click Event

So a lot of the time you may be wanting to create a scenario that is not quite out of the box…such as a user being able to double-click on an object that doesn’t natively support a double-click event.

Caliburn.Micro does an awesome job handling messages on events but WPF has certain intricacies that  you have to take into account so that the framework can perform the job that it was intended to do.

So let’s start out with a very simple example. I will create a view here with just two listboxes. Nothing sneaky here, just two plain ol’ ListBox objects….one is the HereList and one is the ThereList.

image

Now I would like to populate the HereList with a list of objects and allow my user to double click on any given object. Once the double click event occurs then that object is magically transported over to the ThereList. Pretty simple right? Except that the ListBox control doesn’t really have a nice way to allow you to use Caliburn.Micro’s Message.Attach to the objects without totally hosing things up. The reason why is that when you try to attach a message to an object within the ListBox it is done so at the context of the actual object level. So if I have a ObservableCollection<Person> attached to my HereList control…. then it will try to locate the Person.MoveTheStuffOverToTheOtherBoxCommand which more often than not does’t exist.  🙁

Not a problem. We can actually pretty simply apply a style to the listbox in order for Caliburn.Micro to attach a message to each ListBoxItem and also tell the framework to target our parent ViewModel. So let’s get our ViewModel wired up first…we’ll add a simple Person class in as well.

   1:      public class Person: PropertyChangedBase
   2:      {
   3:          private string _FirstName;
   4:          public string FirstName
   5:          {
   6:              get { return _FirstName; }
   7:              set
   8:              {
   9:                  _FirstName = value;
  10:                  NotifyOfPropertyChange(() => FirstName);
  11:                  
  12:              }
  13:          }
  14:   
  15:          private string _LastName;
  16:          public string LastName
  17:          {
  18:              get { return _LastName; }
  19:              set
  20:              {
  21:                  _LastName = value;
  22:                  NotifyOfPropertyChange(() => LastName);
  23:              }
  24:          }
  25:   
  26:          private string _City;
  27:          public string City
  28:          {
  29:              get { return _City; }
  30:              set
  31:              {
  32:                  _City = value;
  33:                  NotifyOfPropertyChange(() => City);
  34:              }
  35:          }
  36:          
  37:          
  38:   
  39:      }

 

Now we’ll add in some properties into our ShellViewModel to support the lists for the ListBoxes and the event for moving a person to the other box.

   1:      public class ShellViewModel : PropertyChangedBase
   2:      {
   3:   
   4:   
   5:          private List<Person> persons;
   6:   
   7:          public ShellViewModel()
   8:          {
   9:                persons = new List<Person>();
  10:                persons.Add(new Person(){ FirstName="Arie", LastName="Jones", City="Indianapolis"});
  11:                persons.Add(new Person(){ FirstName="Brett", LastName="Canova", City="Indianapolis"});
  12:                persons.Add(new Person(){ FirstName="Justin", LastName="Bieber", City="Somewhere in Canada"});
  13:                persons.Add(new Person(){ FirstName="Steve", LastName="Jones", City="Denver"});
  14:                persons.Add(new Person(){ FirstName="Dennis", LastName="Miller", City="Santa Barbara"});
  15:                persons.Add(new Person() { FirstName = "David", LastName = "Letterman", City = "New York" });
  16:   
  17:                HereList = new ObservableCollection<Person>(persons);
  18:                ThereList = new ObservableCollection<Person>();
  19:   
  20:          }
  21:   
  22:   
  23:          private ObservableCollection<Person> _HereList;
  24:          public ObservableCollection<Person> HereList
  25:          {
  26:              get
  27:              {
  28:                  return _HereList;
  29:              }
  30:              set
  31:              {
  32:                  _HereList = value;
  33:                  NotifyOfPropertyChange(() => HereList);
  34:              }
  35:          }
  36:   
  37:          private ObservableCollection<Person> _ThereList;
  38:          public ObservableCollection<Person> ThereList
  39:          {
  40:              get
  41:              {
  42:                  return _ThereList;
  43:              }
  44:              set
  45:              {
  46:                  _ThereList = value;
  47:                  NotifyOfPropertyChange(() => ThereList);
  48:              }
  49:          }
  50:   
  51:   
  52:          public void MoveToThere(Person person)
  53:          {
  54:              HereList.Remove(person);
  55:              ThereList.Add(person);
  56:          }
  57:   
  58:          public void MoveToHere(Person person)
  59:          {
  60:              HereList.Add(person);
  61:              ThereList.Remove(person);
  62:          }
  63:   
  64:      }

 

And our ListBoxes basically look like this

        <ListBox x:Name="HereList" Height="233" HorizontalAlignment="Left" 
                 Margin="12,50,0,0"  VerticalAlignment="Top" Width="173">
            <ListBox.ItemTemplate>
                <DataTemplate>
 
                        <Grid Margin="2" >
                            <Grid.RowDefinitions>
                                <RowDefinition/>
                                <RowDefinition/>
                            </Grid.RowDefinitions>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding FirstName}"/>
                                <TextBlock Text="{Binding LastName}"/>
                            </StackPanel>
                            <TextBlock Grid.Row="1" Text="{Binding City}" FontWeight="SemiBold" />
                        </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

Now we need to make a couple of small changes to the XAML to wire up the events. First thing is that we need to put an ItemsPanel template on our ListBoxes so that we can point caliburn to link up with the correct context. Nothing fancy just an ordinary StackPanel.

            <ListBox.ItemsPanel>
                <ItemsPanelTemplate>
                    <StackPanel cal:Action.TargetWithoutContext="{Binding}" />
                </ItemsPanelTemplate>
            </ListBox.ItemsPanel>

 

Now we can add in an ItemContainerStyle to take care of setting each of the ListBoxItems up with an appropriate call to our functions

            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="cal:Message.Attach" 
                            Value="[Event MouseDoubleClick] = [Action MoveToThere($dataContext)]"/>
                </Style>
            </ListBox.ItemContainerStyle>

 

Now each of our items will call back to the appropriate ViewModel function and pass along the $dataContext which in this instance is just a Person object. And we now have the intended functionality!

image image

And here’s the completed project for you to peruse

WPFDoubleClickExample.zip

Hopefully this helps a few of you out as you being to figure out the framework.

Cheers!

AJ