Fun with WPF Data Binding

来源:百度文库 编辑:神马文学网 时间:2024/04/30 04:27:15
Fun with WPF Data Binding
I‘ve been exploring WPF data binding features and ended up with a simple examplethat illustrates many concepts that I haven‘t really seen all combined togetherin other examples. Featuresused in this app include Data Templates, MultiBinding and IMultiConverters, sharingDataContext, implicit binding to a DataSource‘s CurrentItem, Control Templates,XPath and RelativeSource binding inside a DataTemplate, and DependencyProperty hijacking(used just as a shortcut to save some coding, not really to demonstrate it).
The application allows you to pick one of five animals from a dropdown and displaysthe name and a picture of the animal in a button. There is also a checkbox whichswitches the image from a photo of the selected animal to a drawing. The imagesare located at a url under 2 folders: animals and sketches. Each folder has 5 identicallynamed images. The displayed image comes from a url created based on the dropdownselection, checkbox state, and a base url hardcoded in the app XAML.

The list of animals comes from an XML file (ImageData.xml) which simply lists the animal name andthe image file name. The file is in the project at the root directoryand compiled as a Resource so it can be accessed with the pack://application syntax in XAML.



Eagle
eagle.jpg


Giraffe
giraffe.jpg


Penguin
penguin.jpg


Tortoise
tortoise.jpg


Whale
whale.jpg

To get the MultiBinding to work correctly there needs to be a Value Converter thatcombines the binding inputs to produce a single output. An IMultiValueConverterlooks like the standard IValueConverter but instead of an object as the Convertinput, it takes an object array (ConvertBack also returns an object array). Herethe converter takes an image name and folder name as values, with a base url asthe parameter. Since BitmapImage URIs can‘t be assigned in XAML (I don‘t know thefull explanation or if it‘s just a bug, but they can‘t) we‘re going to just returna BitmapImage pointing to our constructed Uri rather than returning the Uri itself.Here‘s the complete code for our converter:
using System;
using System.Windows.Data;
using System.Windows.Media.Imaging;
namespace BindingApp
{
public class SubFolderImageConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string urlBase = parameter.ToString();
string imageName = values[0].ToString();
string folderName = values[1].ToString();
Uri uri = new Uri(urlBase + folderName + "/" + imageName);
BitmapImage source = new BitmapImage(uri);
return source;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
#endregion
}
}
Now that the supporting files are ready, the rest is going to be all XAML. Firstwe need to set up the converter and data source as Resources:

There‘s plenty of good info out there on the pack syntax if you want a full explanationof the three commas. Note that I set the initial XPath so anything using this datasource will start out at the animal level and the DataSource CurrentItem propertywill be pointing at an Animal element.
Next we need some DataTemplates. These will control the rendering of a single itemfrom our DataSource into displayable content. When attached to a ContentControllike a Button the DataTemplate defines the look of the Content, but leaves the restof the control template alone. We‘re going to need two templates, one for our dropdownthat just shows the animal name as text, and a second more complicated one for ourButton that will display the image.










ConverterParameter="http://blogs.interknowlogy.com/downloads/johnbowen/images/">

RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ContentControl}}" />




The DataImageTemplate is where some of the really interesting stuff is happening.The Image Source is going to be set using the Converter that was set up earlier.Since the Binding being used is a MultiBinding we need to use the exploded XAMLsyntax with nested Binding elements instead of the normal {Binding} syntax likeused in the TextBlock. One of the Converter input Bindings being used is an XPathpointing to the Image element from the DataSource. The second Binding is to theTag DependencyProperty of the the Button the DataTemplate will be bound to. TheTag will be used to hold the name of the folder to load images from. To make itmore flexible a RelativeSource binding is used to find the nearest ContentControlancestor.
Next we‘re going to make a Style to create a new template for the Button. The templateis going to change the Button‘s look, assign the DataTemplate to the Content andset up a Trigger to switch the Tag between the two available folders.
Notice here that the Trigger is using the Selector.IsSelected attached propertyeven though there are no Selectors related to the Button. This is property hijackingand is generally bad practice. I use it here for brevity, but in a real applicationif you need an extra property use subclassing or some other means to add a new propertythat can be used for its designed purpose. Also note that only 1 Trigger existsto set the Tag and there is no explicit means of resetting it. This is because thedefault value is automatically re-applied when the IsSelected=True Trigger is notactive.
Last but not least we have the controls.

ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataNameTemplate}" />