I want to check that the ListBox
element has a specific type to assign a visual style to, but the constant check fails. Maybe I'm doing it wrong?
Problem with this line:
Condition Binding="{Binding}" Value="{x:Type econemodels:DishDTOAdvance}"
<ListBox.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding}"
Value="{x:Type econemodels:DishDTOAdvance}" />
</MultiDataTrigger.Conditions>
<Setter Property="ContentTemplate"
Value="{StaticResource DishNoImage}" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</ListBox.ItemTemplate>
CodePudding user response:
The binding fails, because it binds an instance of type DishDTOAdvance
and compares it with an instance of Type
that describes the DishDTOAdvance
type. Obviously they are different types and the condition is never true. In XAML x:Type
is like typeof()
or GetType()
in code.
The
x:Type
markup extension has a similar function to thetypeof()
operator in C# or the GetType operator in Microsoft Visual Basic. Thex:Type
markup extension supplies a from-string conversion behavior for properties that take the type Type.
That is exactly the case for a custom DataTemplateSelector
, no need for bindings.
Provides a way to choose a
DataTemplate
based on the data object and the data-bound element.
With a data template selector you can provide arbitrary logic to choose a data template for an item. In your case a switch
statement for the type is enough to choose a template that can be found through FindResource
in the resources up the visual tree. Of course you could also assign data templates trough properties if you do not want to search in all resources.
public class TypeTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var contentPresenter = (ContentPresenter)container;
switch (item)
{
case DishDTOAdvance _:
return (DataTemplate)contentPresenter.FindResource("DishNoImage");
// ...other type cases.
default:
return base.SelectTemplate(item, container);
}
}
}
Create and add an instance of the data template selector to your ListBox
. Remove the ItemTemplate
completely, it it now automatically assigned by the selector.
<ListBox ...>
<ListBox.ItemTemplateSelector>
<local:TypeTemplateSelector/>
</ListBox.ItemTemplateSelector>
<!-- ...other markup. -->
</ListBox>
The ContentControl
is redundant. However in case you need it within an item template, it works just the same. ContentControl
exposes an ContentTemplateSelector
property for the same purpose.
Bonus round: Is the trigger impossible? No. You can create a converter that returns the type.
public class TypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value?.GetType();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
Create an instance of the converter in a resource dictionary in scope.
<Window.Resources>
<local:TypeConverter x:Key="TypeConverter"/>
</Window.Resources>
Use the converter in the condition binding. Now types are compared.
<Condition Binding="{Binding Converter={StaticResource TypeConverter}}"
Value="{x:Type econemodels:DishDTOAdvance}"/>