Reputation: 1986
I have created a user control that has a Label and a ComboBox. It is used like this:
<cc:LabeledComboBox
HeaderLabelContent="Months"
ItemsSource="{Binding AllMonths}"
SelectedValue="{Binding SelectedMonth}"/>
And here is what the UserControl XAML looks like:
<UserControl x:Class="CustomControls.LabeledComboBox" ...>
<UserControl.Resources>
<converters:MonthEnumToTextConverter x:Key="MonthEnumToTextConverter" />
</UserControl.Resources>
<DockPanel>
<Label x:Name="LblValue" DockPanel.Dock="Top"/>
<ComboBox x:Name="LstItems">
<ComboBox.ItemTemplate>
<DataTemplate>
<!-- TODO: Fix so that the Converter can be set from outside -->
<TextBlock Text="{Binding Converter={StaticResource MonthEnumToTextConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DockPanel>
</UserControl>
In the comment above you can see my problem. The control is generic (the ComboBox can contain pretty much anything) but on the Binding inside the DataTemplate I have specified a Converter that is very specific.
How can I specify the Converter from outside the UserControl?
I'm hoping for some kind of solution using a dependency property like this:
<cc:LabeledComboBox
...
ItemConverter="{StaticResource MonthEnumToTextConverter}"/>
Upvotes: 1
Views: 8069
Reputation: 128023
You may have an internal binding converter that delegates its Convert and ConvertBack calls to one set is settable as dependency property:
<UserControl ...>
<UserControl.Resources>
<local:InternalItemConverter x:Key="InternalItemConverter"/>
</UserControl.Resources>
<DockPanel>
...
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding
Converter={StaticResource InternalItemConverter}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DockPanel>
</UserControl>
The internal converter could look like this:
class InternalItemConverter : IValueConverter
{
public LabeledComboBox LabeledComboBox { get; set; }
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
if (LabeledComboBox != null && LabeledComboBox.ItemConverter != null)
{
value = LabeledComboBox.ItemConverter.Convert(
value, targetType, parameter, culture);
}
return value;
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
if (LabeledComboBox != null && LabeledComboBox.ItemConverter != null)
{
value = LabeledComboBox.ItemConverter.ConvertBack(
value, targetType, parameter, culture);
}
return value;
}
}
And finally the dependency property code like this:
public partial class LabeledComboBox : UserControl
{
private static readonly DependencyProperty ItemConverterProperty =
DependencyProperty.Register(
"ItemConverter", typeof(IValueConverter), typeof(LabeledComboBox));
public IValueConverter ItemConverter
{
get { return (IValueConverter)GetValue(ItemConverterProperty); }
set { SetValue(ItemConverterProperty, value); }
}
public LabeledComboBox()
{
InitializeComponent();
var converter = (InternalItemConverter)Resources["InternalItemConverter"];
converter.LabeledComboBox = this;
}
}
Upvotes: 2
Reputation: 6415
EDIT: reworked to not use my personal example to avoid confusion ...
On your user control code behind you could define your dependency property.
I don't know what type your converters derive from so change 'myConverterType' to the type of converters you use.
public bool ItemConverter
{
get { return (myConverterType)GetValue(IntemConverterProperty); }
set { SetValue(ItemConverterProperty, value); }
}
public static readonly DependencyProperty ItemConverterProperty =
DependencyProperty.Register("ItemConverter", typeof(myConverterType),
typeof(LabeledComboBox), null);
In XAML you should then just be able to set the converter property as per your example. In my example it is used like this:
<cc:LabeledComboBox ItemConverter="{StaticResource theSpecificConverter}"/>
Then use this property, on your user control xaml, like this:
<ComboBox x:Name="LstItems">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={Binding ItemConverter, ElementName=UserControl}}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Upvotes: 0
Reputation: 1986
OP here. Presenting the solution that I'll use until I find something better.
I don't specify only the Converter, but the whole DataTemplate:
<cc:LabeledComboBox>
<cc:LabeledComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource MonthEnumToTextConverter}}"/>
</DataTemplate>
</cc:LabeledComboBox.ItemTemplate>
</cc:LabeledComboBox>
And here's the ItemTemplate dependency property:
public partial class LabeledComboBox : UserControl
{
public static readonly DependencyProperty ItemTemplateProperty = DependencyProperty.Register(
"ItemTemplate",
typeof(DataTemplate),
typeof(LabeledComboBox),
new PropertyMetadata(default(DataTemplate), ItemTemplateChanged));
private static void ItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var labeledComboBox = (LabeledComboBox)d;
labeledComboBox.LstItems.ItemTemplate = (DataTemplate)e.NewValue;
}
public DataTemplate ItemTemplate
{
get { return (DataTemplate)GetValue(ItemTemplateProperty); }
set { SetValue(ItemTemplateProperty, value); }
}
// ...
}
Upvotes: 0
Reputation: 1996
You can create multiple datatemplates for the the combobox items and then you can control what and how you want to display your comboxitem like below
<DataTemplate DataType="{x:Type vm:MonthDataTypeViewModel}" >
<TextBlock Text="{Binding Converter={StaticResource MonthEnumToTextConverter}}"/>
</DataTemplate>
<DataTemplate DataType={x:Type vm:OtherViewModel}">
<TextBlock Text="{Binding}"/>
</DataTemplate>
If you do not have multiple viewmodels then you can use a template selector to select different data templates based on some property in your viewmodel.
Upvotes: 1