다음을 통해 공유


A Collection of Useful MVVM Tricks

A collection of useful tricks that came about in conjunction with the MVVM pattern.

For the purpose of those examples, we shall use the databound application template from Visual Studio. Note, that some of those tricks work outside of Windows Phone as well.

  1. Getting the tapped item in a list control.

Microsoft suggests using the SelectionChanged event to figure out when the user selected something. In an honest opinion, the SelectedChanged event is pure evil. It triggers when nothing is actually selected (assuming the user unselected the previously selected item), it triggers when there are multiple items selected, and this all leads up to making the event handler act like a huge state machine, and this becomes tiresome at some point.

The generic item template looks like this:

<DataTemplate>
                      <StackPanel Margin="0,0,0,17">
                          <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                          <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                      </StackPanel>
                    </DataTemplate>

                  

The proposed trick is to use this one instead:

 

                          <DataTemplate>
                      <StackPanel Margin="0,0,0,17" Tap="ItemTapped">
                          <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                          <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                      </StackPanel>
                    </DataTemplate>

Notice that the StackPanel at the root of the template now has a tap event, and the attached method looks like this:

 

            private void  ItemTapped(object  sender, System.Windows.Input.GestureEventArgs e)
        {
            var MySelectedItem = (sender as  FrameworkElement).DataContext as ItemViewModel;
        }

At this point, you can use the field MySelectedItem and access all the properties of the ItemViewModel type. Of course, in real life usage, you have to replace the type ItemViewModel with your type, but the line can easily be used in any API using the MVVM pattern (and C#, of course). And it also works with pretty much any event and any UI element (even if you placed the tap event in one of the text blocks, or if you use any other event available for that UI element). It does so because every UI element used in XAML inherits from FrameworkElement, and FrameworkElement is the class which brings about the DataContext property.

      2. Handling multiple selections.

If you were thinking we would avoid the SelectionChanged event yet again, you would be right.

Instead, we will use some sort of visual element and a new property to represent selection

This time, the item template looks like this:

 

<DataTemplate>
                      <Grid Margin="0,0,0,17" Tap="ItemTapped">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="2*"></ColumnDefinition>
                                <ColumnDefinition Width="8*"></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                            <CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}"></CheckBox>
                                <StackPanel Grid.Column="1" Tap="ItemTapped">
                                <TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
                          <TextBlock Text="{Binding LineTwo}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
                            </StackPanel>
                        </Grid>
                    </DataTemplate>

And those lines added to the ItemViewModel class:

 

         bool isSelected = false;
 
      public bool  IsSelected
      {
          get { return isSelected; }
          set { isSelected = value; }
      }

 

Since the IsChecked property of the checkbox and the IsSelected property of the ItemViewModel object are bound two way, when one changes, it updates the other as well.

So figuring out which item is selected can be done with a query over the bound collection:

var selectedItems = from m in  App.ViewModel.Items where m.IsSelected == true select m;

Of course, there are other, more efficient ways of doing it (with a callback when the IsChecked property changes, using the trick from 1, for example.). This is how the mail app does it in Windows Phone.

        3. Figuring out the index of an item in a LongList(multi)Selector.

Since LongList(Multi)Selectors are virtualized, they cannot return a SelectedIndex property, and since we agreed we would never use the SelectionChanged event, it wouldn’t make much sense looking for SelectedIndex even if it were available.

There are two ways here: first, you can add an Index property to the ItemViewModel class and retrieve it using number one, or you can query the bound collection for the item tapped, also obtained with number one, as illustrated below:

var MySelectedItem = (sender as  FrameworkElement).DataContext as ItemViewModel;
int index = App.ViewModel.Items.IndexOf(MySelectedItem);

However, if your collection is big, the query might take a while to complete, whereas getting the Index property directly from the object type is equally fast, regardless of how many items there are in the collection.

See Also

Another important place to find a huge amount of Windows Phone related articles is the TechNet Wiki itself. The best entry point is Windows Phone Resources on the TechNet Wiki.