20 WPF formatted data binding

Recommended for you: Get network issues from WhatsUp Gold. Not end users.

Data binding review

See page 601.

Data conversion

Use the StringFormat attribute

The StringFormat attribute is applied to the display of digital text.

The StringFormat property using the Binding object, you can bind the properties object into a text string format:

<TextBox Margin="5" Grid.Row="2" Grid.Column="1"
 Text="{Binding Path=UnitCost, StringFormat={}{0:C}}">
</TextBox>

The StrngFormat value pairs of braces, escape sequences, the expression StringFormat value is not a markup extension.

By the way, only the StringFormat values in curly braces {} at the beginning, only need to escape sequences.

The WPF list control also has support for string formatting list items. To use it, you simply set the ItemStringFormat attribute list (defined in ItemsControl). This is an example of a list of products, price:

<ListBox Name="lstProducts" DisplayMemberPath="UnitCost" ItemStringFormat="{}{0:C}">
</ListBox>

The value converter

Value conversion converter for source data and target. It happens to be displayed before conversion in the source data, as well as in the case of two binding, just value before being applied to transform the source data in the target.

Create value converter, takes 4 steps:

  1. Create a class that implements the IValueConverter interface
  2. Add the characteristics of ValueConversion attribute to the class declaration, specifying the target data type.
  3. Implementation of Convert () method, this method changes the data, from the original format to display format.
  4. Implementation of ConvertBack () method, this method of reverse change value, from the display format to a native format.

This is the price of the complete value converter class:

[ValueConversion(typeof(decimal), typeof(string))]
public class PriceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var price = (decimal) value;
        return price.ToString("C", culture);
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var price = value.ToString();
        
        decimal result;
        if (Decimal.TryParse(price, NumberStyles.Any, culture, out result))
        {
            return result;
        }
        return value;
    }
}

Application of converter, creating a PriceConverter instance, assign it to the converter attribute bound objects:

<TextBlock Margin="7" Grid.Row="2">Unit Cost:</TextBlock>
<TextBox Margin="5" Grid.Row="2" Grid.Column="1">
  <TextBox.Text>
    <Binding Path="UnitCost">
      <Binding.Converter>
        <local:PriceConverter></local:PriceConverter>
      </Binding.Converter>
    </Binding>
  </TextBox.Text>
</TextBox>

In many cases, the same converter is used for multiple binding. In this case, an instance is meaningless to create converter for each binding. But, in the Resources collection object creation of converter, as shown below:

<Window.Resources>
  <local:PriceConverter x:Key="PriceConverter"></local:PriceConverter>
</Window.Resources>

Then reference resources:

<TextBox Margin="5" Grid.Row="2" Grid.Column="1"
 Text="{Binding Path=UnitCost, Converter={StaticResource PriceConverter}}">
</TextBox>

To create an object with a value converter

Value converter is the bridge linking the data class and window display.

This is the realization of the file path to the BitmapImage object class:

public class ImagePathConverter : IValueConverter
{
    private string imageDirectory = Directory.GetCurrentDirectory();

    public string ImageDirectory
    {
        get { return imageDirectory; }
        set { imageDirectory = value; }
    }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var imagePath = Path.Combine(ImageDirectory, (string)value);
        return new BitmapImage(new Uri(imagePath));
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException("The method or operation is not implemented.");
    }
}

In order to use this converter, add it to the resource:

<Window.Resources>
  <local:ImagePathConverter x:Key="ImagePathConverter"></local:ImagePathConverter>
</Window.Resources>

Create a binding expression using the converter:

<Image Margin="5" Grid.Row="2" Grid.Column="1" Stretch="None"
 HorizontalAlignment="Left" Source=
 "{Binding Path=ProductImagePath, Converter={StaticResource ImagePathConverter}}">
</Image>

Note, Image.Source attribute to a ImageSource object, the BitmapImage class is derived from the ImageSource class.

To apply conditional formatting

For example, you want to tag at the project, give them a different background color:

public class PriceToBackgroundConverter : IValueConverter
{
    public decimal MinimumPriceToHighlight {get; set;}

    public Brush HighlightBrush{get; set;}

    public Brush DefaultBrush{get; set;}

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var price = (decimal)value;
        if (price >= MinimumPriceToHighlight)
            return HighlightBrush;
        else
            return DefaultBrush;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

The definition of the resource object:

<local:PriceToBackgroundConverter x:Key="PriceToBackgroundConverter"
  DefaultBrush="{x:Null}" HighlightBrush="Orange" MinimumPriceToHighlight="50">
</local:PriceToBackgroundConverter>

Application of converter in the elements of value:

<Border Background=
 "{Binding Path=UnitCost, Converter={StaticResource PriceToBackgroundConverter}}"
 ... >

The valuation of multiple attributes

This process is irreversible, it is only a plurality of values into a result. Not the results into multiple attributes.

With the first method for multiple attribute is the use of MultiBinding. The following example:

<TextBlock>
  <TextBlock.Text>
    <MultiBinding StringFormat="{1}, {0}">
      <Binding Path="FirstName"></Binding>
      <Binding Path="LastName"></Binding>
    </MultiBinding>
  </TextBlock.Text>
</TextBlock>

The second method is to use a value converter, is the realization of the IMultiValueConverter.

UnitCost and UnitsInStock uses the MultiBinding property of the source object, and use a value converter with them:

<TextBlock>Total Stock Value: </TextBlock>
<TextBox>
  <TextBox.Text>
    <MultiBinding Converter="{StaticResource ValueInStockConverter}">
      <Binding Path="UnitCost"></Binding>
      <Binding Path="UnitsInStock"></Binding>
    </MultiBinding>
  </TextBox.Text>
</TextBox>

Note that the Convert () method of values is an array of objects, according to their order in MultiBinding placed these values. In the previous example, the first UnitCost, then UnitsInStock.

public class ValueInStockConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var unitCost = (decimal)values[0];
        var unitsInStock = (int)values[1];

        return unitCost * unitsInStock;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

List controls

The base class for all the list control is ItemsControl.

ItemsControl

Properties related to format the ItemsControl class

The bound data source name Description ItemsSource (you want to display in the collection or DataView list). DisplayMemberPath properties that you want to display for each data item. For a more sophisticated representation or as a combination of attributes, instead of using ItemTemplate. ItemStringFormat.NET format string, if set, will be used to format the text of each project. Usually, this technique is used to convert a number or date value to an appropriate display representation, exactly as the Binding.StringFormat attribute. The attribute ItemContainerStyle a style allows you to set the wrap each item container. Container depends on the type of list. (for example, for ListBox it is ListBoxItem, ComboBox class for ComboBoxItem). When the list is filled, the wrapper objects are automatically created. ItemContainerStyleSelector StyleSelector. Use the code to select a style for each item in the list wrapper. This allows you to project different lists of different styles. You must create a custom StyleSelector. AlternationCount in your data set the number of alternate. For example, 2 alternating 2 line styles, 3 alternating 3 line style, etc. ItemTemplate is a template, extracts data from a bound object you, and align it to the control is combined with appropriate. ItemTemplateSelector DataTemplateSelector, Use the code to select a template for each list item. This allows you to different projects are different templates. You must create a custom DataTemplateSelector class. ItemsPanel definition was created to hold the list items. All items are added to the container packing. Usually, the VirtualizingStackPanel comes with a vertical (from top to bottom) direction is used. If you use the GroupStyle packet, this is a style that define each group should be formatted. When using the packet, the project packing (ListBoxItem, ComboBoxItem, etc.) are added that each group in GroupItem packaging, and these groups are then added to the list. GroupStyleSelector StyleSelector. Use the code to select a style for each group. This allows you to different styles for different groups. You must create a custom StyleSelector.

The ItemsControl class the next class is the Selector class, it adds a set of attributes that describe the item selected.

Add the Selector class includes attribute: SelectedItem, SelectedIndex, SelectedValue, SelectedValuePath.

Note, the Selector class does not support multi. ListBox choose to support through the SelectionMode and SelectedItems properties.

List style

ItemContainerStyle

If ItemContainerStyle is set, when a project is created, the style will be passed down to each item in the list control. In the list box control situation, each project is a ListBoxItem object. (in a combo box, it is ComboBoxItem, and so on.) Therefore, you use the ListBox.ItemContainerStyle attribute any attribute is used to set the style of each ListBoxItem object.

<ListBox Name="lstProducts" Margin="5" DisplayMemberPath="ModelName">
  <ListBox.ItemContainerStyle>
    <Style>
      <Setter Property="ListBoxItem.Background" Value="LightSteelBlue" />
      <Setter Property="ListBoxItem.Margin" Value="5" />
      <Setter Property="ListBoxItem.Padding" Value="5" />
    </Style>
  </ListBox.ItemContainerStyle>
</ListBox>

With a trigger style, the style of the trigger is to meet a prerequisite conditions, set the control property:

<ListBox Name="lstProducts" Margin="5" DisplayMemberPath="ModelName">
  <ListBox.ItemContainerStyle>
    <Style TargetType="{x:Type ListBoxItem}">
      <Setter Property="Background" Value="LightSteelBlue" />
      <Setter Property="Margin" Value="5" />
      <Setter Property="Padding" Value="5" />

      <Style.Triggers>
        <Trigger Property="IsSelected" Value="True">
          <Setter Property="Background" Value="DarkRed" />
          <Setter Property="Foreground" Value="White" />
          <Setter Property="BorderBrush" Value="Black" />
          <Setter Property="BorderThickness" Value="1" />
        </Trigger>
      </Style.Triggers>
    </Style>
  </ListBox.ItemContainerStyle>
</ListBox>

List with a check box or radio button box

The basic technique is to modify the representatives of each list item container control template. Do not modify the ListBox.Template property, because it is a list box template. You need to modify the ListBoxItem.Template attribute. Here is a radio button template:

<Window.Resources>
  <Style x:Key="RadioButtonListStyle" TargetType="{x:Type ListBox}">
    <Setter Property="ItemContainerStyle">
      <Setter.Value>
        <Style TargetType="{x:Type ListBoxItem}" >
          <Setter Property="Margin" Value="2" />
          <Setter Property="Template">
            <Setter.Value>
              <ControlTemplate TargetType="{x:Type ListBoxItem}">
                <RadioButton Focusable="False"
                 IsChecked="{Binding Path=IsSelected, Mode=TwoWay,
                             RelativeSource={RelativeSource TemplatedParent} }">
                  <ContentPresenter></ContentPresenter>
                </RadioButton>
              </ControlTemplate>
            </Setter.Value>
          </Setter>
        </Style>
      </Setter.Value>
    </Setter>
  </Style>
</Window.Resources>

Set list box style directly:

<ListBox Style="{StaticResource RadioButtonListStyle}" Name="lstProducts"
 DisplayMemberPath="ModelName">

Check box style list box and method for building a single selection list is basically the same. Just allow list box check:

<Style x:Key="CheckBoxListStyle" TargetType="{x:Type ListBox}">
  <Setter Property="SelectionMode" Value="Multiple"></Setter>
  <Setter Property="ItemContainerStyle">
    <Setter.Value>
      <Style TargetType="{x:Type ListBoxItem}" >
        <Setter Property="Margin" Value="2" />
        <Setter Property="Template">
          <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
              <CheckBox Focusable="False"
               IsChecked="{Binding Path=IsSelected, Mode=TwoWay,
                          RelativeSource={RelativeSource TemplatedParent} }">
                <ContentPresenter></ContentPresenter>
              </CheckBox>
            </ControlTemplate>
          </Setter.Value>
        </Setter>
      </Style>
    </Setter.Value>
  </Setter>
</Style>

Alternating item style

AlternationCount is the number of items to form a sequence number, then later style alternate. By default, AlternationCount is set to 0, and do not use alternate format. If you set AlternationCount to 1, the list will alternate in each project, this allows you to use the even and odd formatting mode.

For each ListBoxItem a AlternationIndex attribute, allows you to decide how it in alternating item sequence number. If you set AlternationCount to 2, the first ListBoxItem to obtain a AlternationIndex of 0, second have a AlternationIndex of 1, third to obtain a 0 AlternationIndex, fourth a AlternationIndex 1, etc.

<ListBox Name="lstProducts" Margin="5" DisplayMemberPath="ModelName"
 AlternationCount="2">
  <ListBox.ItemContainerStyle>
    <Style TargetType="{x:Type ListBoxItem}">
      <Setter Property="Background" Value="LightSteelBlue" />
      <Setter Property="Margin" Value="5" />
      <Setter Property="Padding" Value="5" />
        <Style.Triggers>
          <Trigger Property="ItemsControl.AlternationIndex" Value="1">
            <Setter Property="Background" Value="LightBlue" />
          </Trigger>
          <Trigger Property="IsSelected" Value="True">
            <Setter Property="Background" Value="DarkRed" />
            <Setter Property="Foreground" Value="White" />
            <Setter Property="BorderBrush" Value="Black" />
            <Setter Property="BorderThickness" Value="1" />
          </Trigger>
        </Style.Triggers>
      </Style>
    </ListBox.ItemContainerStyle>
</ListBox>            

Mode selector

Must be realized by code, StyleSelector derived from a special class, override the SelectStyle () method, select the appropriate style.

Here is a basic selector, two styles to choose a:

public class ProductByCategoryStyleSelector : StyleSelector
{
    public override Style SelectStyle(object item, DependencyObject container)
    {
        var product = (Product)item;
        var window = Application.Current.MainWindow;
        if (product.CategoryName == "Travel")
        {
            return (Style)window.FindResource("TravelProductStyle");
        }
        else
        {
            return (Style)window.FindResource("DefaultProductStyle");
        }
    }
}

The following code uses reflection, a more general style selector:

public class SingleCriteriaHighlightStyleSelector : StyleSelector
{
    public Style DefaultStyle
    {
        get; set;
    }
    public Style HighlightStyle
    {
        get; set;
    }
    public string PropertyToEvaluate
    {
        get; set;
    }
    public string PropertyValueToHighlight
    {
        get; set;
    }
    public override Style SelectStyle(object item, DependencyObject container)
    {
        Product product = (Product)item;
        
        // Use reflection to obtain property detection
        Type type = product.GetType();
        PropertyInfo property = type.GetProperty(PropertyToEvaluate);
        
        // According to the attribute value determines whether the products should be highlighted
        if (property.GetValue(product, null).ToString() == PropertyValueToHighlight)
        {
            return HighlightStyle;
        }
        else
        {
            return DefaultStyle;
        }
    }
}

Two style used in the code definition:

<Window.Resources>
  <Style x:Key="DefaultStyle" TargetType="{x:Type ListBoxItem}">
    <Setter Property="Background" Value="LightYellow" />
    <Setter Property="Padding" Value="2" />
  </Style>

  <Style x:Key="HighlightStyle" TargetType="{x:Type ListBoxItem}">
    <Setter Property="Background" Value="LightSteelBlue" />
    <Setter Property="FontWeight" Value="Bold" />
    <Setter Property="Padding" Value="2" />
  </Style>
</Window.Resources>

An inline style selector:

<ListBox Name="lstProducts" HorizontalContentAlignment="Stretch">
  <ListBox.ItemContainerStyleSelector>
    <local:SingleCriteriaHighlightStyleSelector
      DefaultStyle="{StaticResource DefaultStyle}"
      HighlightStyle="{StaticResource HighlightStyle}"
      PropertyToEvaluate="CategoryName"
      PropertyValueToHighlight="Travel"
    >
    </local:SingleCriteriaHighlightStyleSelector>
  </ListBox.ItemContainerStyleSelector>
</ListBox>

Style selection process, when the first binding list, be executed once. If, due to modify the decision style data, style does not automatically change. Can only use the violence method forced style update:

var selector = lstProducts.ItemContainerStyleSelector;
lstProducts.ItemContainerStyleSelector = null;
lstProducts.ItemContainerStyleSelector = selector;

Data template

Each ListBoxItem can bind to a field, there is no way to combine multiple fields. If you want to display multiple fields, this requires a data template. The data template is a piece of XAML markers. It defines how to display a data binding. Two types of controls to support data template:

The list item is a content control.

The data template should include a data binding expression.

This is an example, with the boundary wrap each item a fillet, display two pieces of information, formatting highlight model and use bold number:

<ListBox Name="lstProducts" HorizontalContentAlignment="Stretch">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue" CornerRadius="4">
        <Grid Margin="3">
          <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
          </Grid.RowDefinitions>

          <TextBlock FontWeight="Bold"
           Text="{Binding Path=ModelNumber}"></TextBlock>
          <TextBlock Grid.Row="1"
           Text="{Binding Path=ModelName}"></TextBlock>
        </Grid>
      </Border>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>          

The separation and reuse the template

Extraction of an example template:

<Window.Resources>
  <DataTemplate x:Key="ProductDataTemplate">
    <Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue"
     CornerRadius="4">
      <Grid Margin="3">
        <Grid.RowDefinitions>
          <RowDefinition></RowDefinition>
          <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>

        <TextBlock FontWeight="Bold" Text="{Binding Path=ModelNumber}">
        </TextBlock>
        <TextBlock Grid.Row="1" Text="{Binding Path=ModelName}">
        </TextBlock>
      </Grid>
    </Border>
  </DataTemplate>
</Window.Resources>

Now, you can add data to the list template you, use a static resource:

<ListBox Name="lstProducts" HorizontalContentAlignment="Stretch"
 ItemTemplate="{StaticResource ProductDataTemplate}"></ListBox>

Automatically controls the reuse of different types of the same data template. Set the DataTemplate.DataType property to the type of data binding. For example, the previous example, remove Key and specify the bound Product object:

<Window.Resources>
  <DataTemplate DataType="{x:Type local:Product}">
  </DataTemplate>
</Window.Resources>

The use of more advanced template

The first example,

<Window.Resources>
  <local:ImagePathConverter x:Key="ImagePathConverter"></local:ImagePathConverter>
  <DataTemplate x:Key="ProductTemplate">
    <Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue"
     CornerRadius="4">
      <Grid Margin="3">
        <Grid.RowDefinitions>
          <RowDefinition></RowDefinition>
          <RowDefinition></RowDefinition>
          <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>

        <TextBlock FontWeight="Bold" Text="{Binding Path=ModelNumber}"></TextBlock>
        <TextBlock Grid.Row="1" Text="{Binding Path=ModelName}"></TextBlock>
        <Image Grid.Row="2" Grid.RowSpan="2" Source=
"{Binding Path=ProductImagePath, Converter={StaticResource ImagePathConverter}}">
        </Image>
      </Grid>
    </Border>
  </DataTemplate>
</Window.Resources>

Direct a template internal controls. For example, a column of type. Next to each species is a View button. You can use the button to launch another window exactly match the types of products.

Processing button click. Obviously, all the buttons will be linked to the same event handler, you define it inside the template. However, you need to decide which of a list item is clicked. One solution is to store some additional identification information in the Tag property of the button, as shown below:

<DataTemplate>
  <Grid Margin="3">
    <Grid.ColumnDefinitions>
      <ColumnDefinition></ColumnDefinition>
      <ColumnDefinition Width="Auto"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <TextBlock Text="{Binding Path=CategoryName}"></TextBlock>
    <Button Grid.Column="2" HorizontalAlignment="Right" Padding="2"
      Click="cmdView_Clicked" Tag="{Binding Path=CategoryID}">View ...</Button>
  </Grid>
</DataTemplate>

You can retrieve the Tag attribute, in an event handler:

private void cmdView_Clicked(object sender, RoutedEventArgs e)
{
    var cmd = (Button)sender;
    var categoryID = (int)cmd.Tag;
    ...
}

When you define binding, is missing the Path attribute, you can capture the whole data object:

<Button HorizontalAlignment="Right" Padding="1"
  Click="cmdView_Clicked" Tag="{Binding}">View ...</Button>

Transfer the object to update the list selection easier. Before clicking the View button, move the selection to the list by clicking on the button. As shown below:

var cmd = (Button)sender;
var product = (Product)cmd.Tag;
lstCategories.SelectedItem = product;

 

A different template

Renders list items in different ways:

Data trigger. An attribute data based on the project, a property setting template elements. For example, the CategoryName attribute of the object based on the corresponding products, you can change the package of each list item custom border background. This is an example of the scarlet letter, with the highlight in the tools directory products:

<DataTemplate x:Key="DefaultTemplate">
  <DataTemplate.Triggers>
    <DataTrigger Binding="{Binding Path=CategoryName}" Value="Tools">
     <Setter Property="ListBoxItem.Foreground" Value="Red"></Setter>
    </DataTrigger>
  </DataTemplate.Triggers>
  <Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue"
   CornerRadius="4">
    <Grid Margin="3">
      <Grid.RowDefinitions>
        <RowDefinition></RowDefinition>
        <RowDefinition></RowDefinition>
      </Grid.RowDefinitions>
      <TextBlock FontWeight="Bold"
       Text="{Binding Path=ModelNumber}"></TextBlock>
      <TextBlock Grid.Row="1"
       Text="{Binding Path=ModelName}"></TextBlock>
    </Grid>
  </Border>
</DataTemplate>

Value converter method:

<Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue" CornerRadius="4"
 Background=
 "{Binding Path=CategoryName, Converter={StaticResource CategoryToColorConverter}">

 

Template selector

You need to create a derived from the DataTemplateSelector class. Check the bound object and use your logic to select a suitable template.

public class SingleCriteriaHighlightTemplateSelector : DataTemplateSelector
{
    public DataTemplate DefaultTemplate
    {
        get; set;
    }
    public DataTemplate HighlightTemplate
    {
        get; set;
    }
    public string PropertyToEvaluate
    {
        get; set;
    }
    public string PropertyValueToHighlight
    {
        get; set;
    }
    public override DataTemplate SelectTemplate(object item,
      DependencyObject container)
    {
        Product product = (Product)item;
        // Use reflection to get the property to check.
        Type type = product.GetType();
        PropertyInfo property = type.GetProperty(PropertyToEvaluate);
        // Decide if this product should be highlighted
        // based on the property value.
        if (property.GetValue(product, null).ToString() == PropertyValueToHighlight)
        {
            return HighlightTemplate;
        }
        else
        {
            return DefaultTemplate;
        }
    }
}    

Here is the marker create two data template:

<Window.Resources>
  <DataTemplate x:Key="DefaultTemplate">
    <Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue"
      CornerRadius="4">
      <Grid Margin="3">
        <Grid.RowDefinitions>
          <RowDefinition></RowDefinition>
          <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock
         Text="{Binding Path=ModelNumber}"></TextBlock>
        <TextBlock Grid.Row="1"
         Text="{Binding Path=ModelName}"></TextBlock>
      </Grid>
    </Border>
  </DataTemplate>
  
  <DataTemplate x:Key="HighlightTemplate">
    <Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue"
     Background="LightYellow" CornerRadius="4">
      <Grid Margin="3">
        <Grid.RowDefinitions>    
          <RowDefinition></RowDefinition>
          <RowDefinition></RowDefinition>
          <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock FontWeight="Bold"
         Text="{Binding Path=ModelNumber}"></TextBlock>
        <TextBlock Grid.Row="1" FontWeight="Bold"
         Text="{Binding Path=ModelName}"></TextBlock>
        <TextBlock Grid.Row="2" FontStyle="Italic" HorizontalAlignment="Right">
         *** Great for vacations ***</TextBlock>
      </Grid>
    </Border>
  </DataTemplate>
</Window.Resources>    

Here is the application template selector:

<ListBox Name="lstProducts" HorizontalContentAlignment="Stretch">
  <ListBox.ItemTemplateSelector>
    <local:SingleCriteriaHighlightTemplateSelector
      DefaultTemplate="{StaticResource DefaultTemplate}"
      HighlightTemplate="{StaticResource HighlightTemplate}"
      PropertyToEvaluate="CategoryName"
      PropertyValueToHighlight="Travel"
    >
    </local:SingleCriteriaHighlightTemplateSelector>
  </ListBox.ItemTemplateSelector>
</ListBox>

 

Template and selection

1, change the background color of the selected project:

Use the Foreground attribute property inheritance, so you add to the template of any element automatically obtain the color white. The Background property does not use the property inheritance, but the default background color is transparent.

First of all, set the project Container style:

<ListBox Name="lstProducts" HorizontalContentAlignment="Stretch">
  <ListBox.ItemContainerStyle>
    <Style>
      <Setter Property="Control.Padding" Value="0"></Setter>
      <Style.Triggers>
        <Trigger Property="ListBoxItem.IsSelected" Value="True">
          <Setter Property="ListBoxItem.Background" Value="DarkRed" />
        </Trigger>
      </Style.Triggers>
    </Style>
  </ListBox.ItemContainerStyle>
</ListBox>

Then, modify the data template:

<DataTemplate>
  <Grid Margin="0" Background="White">
    <Border Margin="5" BorderThickness="1"
     BorderBrush="SteelBlue" CornerRadius="4"
     Background="{Binding Path=Background, RelativeSource={
                             RelativeSource
                             Mode=FindAncestor,
                             AncestorType={x:Type ListBoxItem}
                          }}" >
      <Grid Margin="3">
        <Grid.RowDefinitions>
          <RowDefinition></RowDefinition>
          <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock FontWeight="Bold" Text="{Binding Path=ModelNumber}"></TextBlock>
        <TextBlock Grid.Row="1" Text="{Binding Path=ModelName}"></TextBlock>
      </Grid>
    </Border>
  </Grid>
</DataTemplate>    

2, select the items, display the details of the project:

In the data template, use the Binding property of the current RelativeSource search ListBoxItem. If it is not selected, set the Visibility property of the extra information, you can hide it.

Using a data flip-flop, when the IsSelected property of ListBoxItem is changed, change the Visibility attribute container.

Data trigger only need to be placed in the container to be hidden within the.

This is not automatically extended simplified edition features:

<DataTemplate>
  <Border Margin="5" BorderThickness="1" BorderBrush="SteelBlue"
   CornerRadius="4">
    <StackPanel Margin="3">
      <TextBlock Text="{Binding Path=ModelName}"></TextBlock>
      <StackPanel>
        <TextBlock Margin="3" Text="{Binding Path=Description}"
         TextWrapping="Wrap" MaxWidth="250" HorizontalAlignment="Left"></TextBlock>
        <Image Source=
"{Binding Path=ProductImagePath, Converter={StaticResource ImagePathConverter}}">
        </Image>
        <Button FontWeight="Regular" HorizontalAlignment="Right" Padding="1"
         Tag="{Binding}">View Details...</Button>
      </StackPanel>
    </StackPanel>
  </Border>
</DataTemplate>

The innermost layer of the StackPanel contains only the selected items to display content, so set the container style:

<StackPanel>
  <StackPanel.Style>
    <Style>
      <Style.Triggers>
        <DataTrigger
          Binding="{Binding Path=IsSelected, RelativeSource={
                             RelativeSource
                             Mode=FindAncestor,
                             AncestorType={x:Type ListBoxItem}
                          }}"
          Value="False">
          <Setter Property="StackPanel.Visibility" Value="Collapsed" />
        </DataTrigger>
      </Style.Triggers>
    </Style>
  </StackPanel.Style>

  
</StackPanel>

In this example, you need to use DataTrigger instead of a common trigger, because you need the attribute in the ancestor elements (ListBoxItem), and the only access to it is to use a data binding expression.

Change the project layout

The use of any class derived from System.Windows.Controls.Panel, the ItemsPanelTemplate property is set to ListBox, you can change the layout of the project.

The following example, use WrapPanel to change the layout of the project:

<ListBox Margin="7,3,7,10" Name="lstProducts"
 ItemTemplate="{StaticResource ItemTemplate}"
 ScrollViewer.HorizontalScrollBarVisibility="Disabled">
  <ListBox.ItemsPanel>
    <ItemsPanelTemplate>
      <WrapPanel></WrapPanel>
    </ItemsPanelTemplate>
  </ListBox.ItemsPanel>
</ListBox>

You must also set the ScrollViewer.HorizontalScrollBarVisibility additional properties for the Disabled. This ensures that ScrollViewer never use level of scrollbar.

VirtualizingStackPanel has a better performance for a large number of data items.

The combo box

By default, ComboBox is read-only. When the IsReadOnly property is set to false and the IsEditable property is true, select the box into a text box, you can type any text.

The combo box has the auto complete function. To turn it off, set the ComboBox.IsTextSearchEnabled property to false. This property is located in the ItemsControl class.

If the IsEditable property is false (this is the default value), select the box will display the exact visual copy of a project.

An important detail is the combo box displays what as its content, not as its data source. For example, filling a combo box control with the Product object, and set the DisplayMemberPath property to ModelName. So, the combo box displays the ModelName property of each project. Even if the combo box to obtain information from a set of Product objects, you mark creation is a plain text list. It will display the current product of ModelName, and if IsEditable is true and IsReadOnly is false, it will allow you to edit the value.

If the IsEditable property is true, select box shows the representation of a it's a word for word. Simply call ToString () to the project. In general, display a class name.

Set the TextSearch.TextPath attribute specifies the affiliated selection box should use content. To correct this problem:

<ComboBox IsEditable="True" IsReadOnly="True" TextSearch.TextPath="ModelName" ...>
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Posted by Walter at November 17, 2013 - 8:48 PM