WPF Data Templates Part 5 – Editing data in a Data Template and Switching a template on a Single item programmatically

Please follow and share!
Twitter
Facebook
LinkedIn
RSS

The beauty of WPF is that it contains the ability to provide the user enhanced usability along with enhanced user experience.  Too often today, you see applications that sport “admin” functions, which are functions for things like maintaining values that should be displayed in a list box, or data that is used to associate two entities [for instance, a bank account can have more than one contact person associated].  In the typical application, you see an “admin” or “maintenance” section where these entries and associations are made.

Think a moment now on the user experience, user A accesses the application, moves on to a data entry form, gets two-thirds of the way through the form and realizes that a value is missing from a drop down list. This user must now stop everything they are doing on this form, then move to a separate part of the application, make their entry, initiate some type of refresh, then continue on with their work.  What is up with that?  The experience of user A is less to be desired.  Why make the user go to a completely different section of the application and split up their train of thought from the process at hand?  Ultimately, a user should not have to move away from their current work to accomplish something like this, they should be able to edit/add items to a list on the fly, and from within the form that they are currently working. This is where Data Templates come in to play.   

WPF Data Templates provide the ability for you to define a template that can be used to add/edit a list item.  In this example, we’re going to pull in the image favorites data, and provide the ability to edit the properties of an Image directly from within the list box with the help of a data template.  This example is going to use SQL Express database and LINQ to SQL to pull and update the data associated with the Image Favorite.

image_2

In order for any changes to the images to be tracked, I created an ObservableCollection class that simply returns all data from the ImageFavorite table.  This is used to populate the ItemSource of the List Box.  This class is defined as follows:

   1: public class ObservableFavorites : ObservableCollection<DataGoo.ImageFavorite>

 

   2: {

 

   3:     public ObservableFavorites(DataGoo.GooDataContext ctx)

 

   4:     {

 

   5:         foreach (ImageFavorite fav in ctx.ImageFavorites)

 

   6:         {

 

   7:             this.Add(fav);

 

   8:         }

 

   9:     }

 

  10: }

In the code-behind of the window, I instantiated an instance of the data context (DataGoo is simply the project containing my database and LINQ to SQL dbml).  Code to initiate the population of the list box is as follows [where lbFavorites is the list box]:

   1: private static DataGoo.GooDataContext _dc = new DataGoo.GooDataContext();

 

   2: private ObservableFavorites _favorites;

 

   3:  

 

   4: public Window1()

 

   5: {

 

   6:     InitializeComponent();

 

   7:     PopulateList();

 

   8: }

 

   9:  

 

  10: private void PopulateList()

 

  11: {

 

  12:     _favorites = new ObservableFavorites(_dc);

 

  13:     this.lbFavorites.ItemsSource = _favorites;

 

  14: }

I have defined two data templates for use with the list box that displays the Image Favorite data.  The first template “DetailedTemplate” is the default view of the data, it contains a button that is used to switch the item from a display to an edit view.  The XAML for this is defined in Window.Resources.

   1: <DataTemplate x:Key="DetailedTemplate">

 

   2:         <Border BorderBrush="Blue" Margin="3" Padding="3" BorderThickness="2" CornerRadius="5" Background="Beige">

 

   3:             <StackPanel Orientation="Horizontal">

 

   4:                 <Image Margin="10" Width="250" Height="200" Stretch="Fill" Source="{Binding Path=ImageHref}">

 

   5:                     <Image.BitmapEffect>

 

   6:                         <DropShadowBitmapEffect />

 

   7:                     </Image.BitmapEffect>

 

   8:                 </Image>

 

   9:                 <StackPanel Orientation="Vertical" VerticalAlignment="Center">

 

  10:                     

 

  11:                     <TextBlock FontSize="25" Foreground="Goldenrod" Text="{Binding Path=ImageName}" />

 

  12:                     

 

  13:                     <StackPanel Orientation="Horizontal">

 

  14:                         <Label Content="{Binding Converter={StaticResource RatingConverter}}" Width="300" />

 

  15:                         <Button Height="50" Width="50" Click="btnEdit_Click" 

 

  16:                                 CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">

 

  17:                             <Image Source="images/processIcon.png" />

 

  18:                         </Button>

 

  19:                         <Label Content="" />

 

  20:                     </StackPanel>

 

  21:                 </StackPanel>

 

  22:             </StackPanel>

 

  23:         </Border>

 

  24:     </DataTemplate>

The command parameter is set to the ListBoxItem that contains the button being pressed, this is accomplished through the binding statement: {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}} … this binding statement essentially walks up the current XAML tree from the button up to the first element that is of type ListBoxItem, thus returning the List Box Item whose edit button was clicked.  The click event of the button is then handled by the following method:

   1: private void btnEdit_Click(object sender, RoutedEventArgs e)

 

   2: {

 

   3:     Button btn = (Button)sender;

 

   4:     //command contains the list item

 

   5:     ListBoxItem itm = (ListBoxItem)btn.CommandParameter;

 

   6:     itm.ContentTemplate = this.FindResource("EditTemplate") as DataTemplate;

 

   7: }

In order to switch the Data Template on only a single item in the list box, we need to assign the new template to the ContentTemplate property of the list item, which is the data template defined in resources as “EditTemplate”.  The EditTemplate provides the user the ability to update the image name and image rating, is defined in XAML in Window.Resources as:

   1: <DataTemplate x:Key="EditTemplate">

 

   2:     <Border BorderBrush="Blue" Margin="3" Padding="3" BorderThickness="2" CornerRadius="5" Background="Beige">

 

   3:         <StackPanel Orientation="Horizontal">

 

   4:             <Image Margin="10" Width="250" Height="200" Stretch="Fill" Source="{Binding Path=ImageHref}">

 

   5:                 <Image.BitmapEffect>

 

   6:                     <DropShadowBitmapEffect />

 

   7:                 </Image.BitmapEffect>

 

   8:             </Image>

 

   9:             <StackPanel Orientation="Vertical" VerticalAlignment="Center">

 

  10:                 <TextBox Width="300" FontSize="25" Foreground="Goldenrod" Text="{Binding Path=ImageName}" />

 

  11:                 <TextBox Width="50" FontSize="25" Foreground="Blue" Text="{Binding Path=ImageRating}" HorizontalAlignment="Left" />

 

  12:                 <Button Content="Save" Click="btnSave_Click" Width="40" HorizontalAlignment="Left"

 

  13:                         CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}"/>

 

  14:             </StackPanel>

 

  15:         </StackPanel>

 

  16:     </Border>

 

  17: </DataTemplate>

A Save button is included in the Edit Data Template to trigger the saving of the data and then switching the template of the list item from the Edit template back to the Detailed template.  It uses the same binding for the Command Parameter to latch on to the list box item element that contains the button being clicked.  The click event handler for the save button is as follows:

   1: private void btnSave_Click(object sender, RoutedEventArgs e)

 

   2: {

 

   3:     //commit the changes to the database

 

   4:     _dc.SubmitChanges();   

 

   5:  

 

   6:     //switch item template

 

   7:     Button btn = (Button)sender;

 

   8:     //command contains the list item

 

   9:     ListBoxItem itm = (ListBoxItem)btn.CommandParameter;

 

  10:     itm.ContentTemplate = this.FindResource("DetailedTemplate") as DataTemplate;

 

  11:    

 

  12: }

 

 

image_4 image_6

Full Source code for this example is available here.

Please follow and share!
Twitter
Facebook
LinkedIn
RSS

Comments

  1. Thanks for this!

    I have been looking everywhere for an example of how to use a button within a template!

    This helped a lot!

Comments are closed.