Feeds:
Posts
Comments

Posts Tagged ‘Data Binding’

Customize WPF ComboBox

There is a column in the Lines ListView of the Edit Model dialog box of the GeometryViz3D tool for the user to choose the color of the lines. Previously the colors are shown as plain text in the ComboBoxes. It will be more user friendly if the colors can be illustrated as the actual colors as shown in the following screen shot.

Here is the XAML code I use to achieve this. The key idea is customize the ItemTemplate of the ComboBoxes bound to the Color column of the Lines ListView by drawing a Rectangle.

The ItemSource property of the ComboBox is bound to the Colors property of the EditModelViewModel object, which is an array of strings that are color names, including Black, Cyan, and so on. And therefore, each item of the ComboBox is bound to the name of a color. We bind the color name to the Fill property of the Rectangle by taking advantage of the WPF’s built-in feature that converts a color name to a Brush.

The remaining issue is how to specify the size of the Rectangle. If we don’t specify, the actual height and width will both be 0. After some research using the snoop tool, I decided to determine the Height based on the FontSize of the ComboBox, and the Width based on the ActualWidth the ComboBox. Since the ComboBox is an ancestor of the items of the drop down list, we use the ComboBox as the RelativeSource. To make the Height of the ComboBox similar to the ComboBoxes in other columns, we have to adjust the value a little bit. This is where the DoubleAdditionConverter comes in. We make the Height to be FontSize + 2, and Width to be ActualWidth – 4.

Since the Height and Width of the Rectangle will not change after initialized, we set the binding Mode to OneTime.

The SelectedValue property of the ComboBox is bound to the Color property of the G3DLine object that is bound to the ListViewItem.

<Window.Resources>
  <local:DoubleAdditionConverter x:Key="doubleAdditionConverter" />
  …
  <DataTemplate x:Key="colorCellTemplate">
    <ComboBox ItemsSource="{Binding Colors}"
              SelectedValue="{Binding Color}"
              Margin="-6, 0, -6, 0" >
      <ComboBox.ItemTemplate>
        <DataTemplate>
          <Rectangle Fill="{Binding Mode=OneTime}"
                     Height="{Binding Mode=OneTime,
                       RelativeSource={RelativeSource FindAncestor,
                         AncestorType={x:Type ComboBox}},
                       Path=FontSize,
                       Converter={StaticResource doubleAdditionConverter},
                       ConverterParameter=2}"

                     Width="{Binding Mode=OneTime,
                       RelativeSource={RelativeSource FindAncestor,
                         AncestorType={x:Type ComboBox}},
                       Path=ActualWidth,
                       Converter={StaticResource doubleAdditionConverter},
                       ConverterParameter=-4}" />
        </DataTemplate>
      </ComboBox.ItemTemplate>
    </ComboBox>
  </DataTemplate>
</Window.Resources>

<ListView Margin="3"
          ItemsSource="{Binding Lines}"
          SelectedValue="{Binding SelectedLine}">
  <ListView.View>
    <GridView>
      <GridView.Columns>
        …
        <GridViewColumn Header="Color" Width="120"
                        CellTemplate="{StaticResource colorCellTemplate}" />
      </GridView.Columns>
    </GridView>
  </ListView.View>
</ListView>

Here is the source code of the DoubleAdditionConverter:

public class DoubleAdditionConverter : IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        double v = (double)value;
        double p = Double.Parse((string)parameter);
        return v + p;
    }

    public object ConvertBack(object value, Type targetType, object parameter,
        System.Globalization.CultureInfo culture)
    {
        double v = (double)value;
        double p = Double.Parse((string)parameter);
        return v - p;
    }

    #endregion
}

The entire source code can be found at the GeometryViz3D project on CodePlex.

Advertisements

Read Full Post »

Published an article on CodeProject, discussing about the issues related to binding ObservableCollection to ListView and ComboBox controls. Please read the article here.

Read Full Post »

In WPF, we can bind an ObservableCollection<T> object to the ItemsSource property of an ItemsControl, e.g. a ComboBox. When we add or remove an element from the collection, the control bound to the collection will get updated automatically. However, we modify an element of the collection, the control won’t get updated. To force the control gets updated when an element of the collection it is bound to, we need to write a class inheriting from ObservableCollection<T> and provide a method that calls the OnCollectionChanged() method, which raises the CollectionChanged event, which will caused the control bound to the collection to be refreshed.

public class ElementCollection<T> : ObservableCollection<T>
{
  public void UpdateCollection()
  {
    OnCollectionChanged(new NotifyCollectionChangedEventArgs(
        NotifyCollectionChangedAction.Reset));
  }
}

Then we change the type of the collection to ElementCollection, and call the UpdateCollection() method after we changed the value of an element.

Read Full Post »