Post 42
But before all that I want to let you all know, this is post #42 on Developers 42. For obvious reasons this is a special post on my blog. As I started out last year, I didn't have any visitors, but now I average about 1700 visits a month. A large part of these visits come trough Google, but a special thanks goes out to Dave Campbell for publishing a lot of my articles on SilverlightCream.com. Trough his great efforts of bringing us the latest and greatest on Silverlight, his website is the second greatest referrer, only topped by Google. I won't elaborate any more on this. Stats, highs and lows will all be published in October when the blog celebrates it's first year of existence.
Binding to ComboBox.SelectedItem trough a custom dependency property in a UserControl.
I ran into this building a user control based on the requirement that we need a combo box that includes an edit button next to it when needed and also provides a label when needed. Simplest way to do this (at least as I see it), is trough building two UserControls. The first combines the ComboBox with a Button and the second combines the result with a TextBlock. Today we want to focus on the first one, which I dubbed ExtendedComboBox. I wrote the following XAML as a start:
<UserControl xmlns:input="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input"
xmlns:inputToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit"
x:Class="ExtendedComboBox.ExtendedComboBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
x:Name="extendedComboBoxControl"
>
<StackPanel x:Name="stackPanel" Orientation="Horizontal" HorizontalAlignment="Stretch">
<ComboBox x:Name="comboBox"
HorizontalAlignment="Stretch"
SelectionChanged="comboBox_SelectionChanged"
>
</ComboBox>
<Button x:Name="editButton" Content="..." Padding="5,0,5,0" LayoutUpdated="editButton_LayoutUpdated"
Click="editButton_Click"/>
</StackPanel>
</UserControl>
xmlns:inputToolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit"
x:Class="ExtendedComboBox.ExtendedComboBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsm="clr-namespace:System.Windows;assembly=System.Windows"
x:Name="extendedComboBoxControl"
>
<StackPanel x:Name="stackPanel" Orientation="Horizontal" HorizontalAlignment="Stretch">
<ComboBox x:Name="comboBox"
HorizontalAlignment="Stretch"
SelectionChanged="comboBox_SelectionChanged"
>
</ComboBox>
<Button x:Name="editButton" Content="..." Padding="5,0,5,0" LayoutUpdated="editButton_LayoutUpdated"
Click="editButton_Click"/>
</StackPanel>
</UserControl>
Simple enough. All I had to do now was add some dependency properties to control some functionality and Bob's your uncle, wright? Wrong! I will spare you the details on sizing specifics and events, as this is not the focus of this article.
For databinding I figured I would need to bind to the ItemsSource and the SelectedItem properties of the ComboBox. Here is the code I initially came up with:
publicIEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set
{
SetValue(ItemsSourceProperty, value);
comboBox.ItemsSource = value;
}
}
// Using a DependencyProperty as the backing store for ItemsSource. This enables animation, styling, binding, etc...
publicstaticreadonlyDependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ExtendedComboBox), newPropertyMetadata(null));
publicobject SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set
{
SetValue(SelectedItemProperty, value);
comboBox.SelectedItem = SelectedItem;
}
}
// Using a DependencyProperty as the backing store for SelectedItem. This enables animation, styling, binding, etc...
publicstaticreadonlyDependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(ExtendedComboBox), newPropertyMetadata(null));
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set
{
SetValue(ItemsSourceProperty, value);
comboBox.ItemsSource = value;
}
}
// Using a DependencyProperty as the backing store for ItemsSource. This enables animation, styling, binding, etc...
publicstaticreadonlyDependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(ExtendedComboBox), newPropertyMetadata(null));
publicobject SelectedItem
{
get { return (object)GetValue(SelectedItemProperty); }
set
{
SetValue(SelectedItemProperty, value);
comboBox.SelectedItem = SelectedItem;
}
}
// Using a DependencyProperty as the backing store for SelectedItem. This enables animation, styling, binding, etc...
publicstaticreadonlyDependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(ExtendedComboBox), newPropertyMetadata(null));
As you can see these are standard dependency properties, only I've added a line to assign their values trough to the ComboBox. For the ItemsSource property this works like a charm, however, for the SelectedItem property it doesn't.
After struggling with this, building a complete test rig and reading trough books worth of blog posts, I finally came up with the following solution. I removed the passing trough of the value in the properties setter method and added manual binding for the property in the constructor of the ExtendedCombobox. Here is my new constructor:
public ExtendedComboBox()
{
InitializeComponent();
Binding selectedItemBinding = newBinding("SelectedItem");
selectedItemBinding.Source = this;
selectedItemBinding.Mode = BindingMode.TwoWay;
comboBox.SetBinding(ComboBox.SelectedItemProperty, selectedItemBinding);
}
{
InitializeComponent();
Binding selectedItemBinding = newBinding("SelectedItem");
selectedItemBinding.Source = this;
selectedItemBinding.Mode = BindingMode.TwoWay;
comboBox.SetBinding(ComboBox.SelectedItemProperty, selectedItemBinding);
}
This way the ExtendedComboBox becomes the source of the binding for the ComboBox.SelectedItem property. After adding this (and obviously also implementing the Equals and GetHashCode methods on the business objects involved) it now works like a charm, without having to write any extra code in the modules that use this control.
Please leave me a comment if you have any questions, remarks or if you just want to say hi.
No comments:
Post a Comment