I have the following XML:
<Items>
<Item>
<Name>Item One</Name>
<MyValue>42</MyValue>
</Item>
</Items>
and XAML:
<DockPanel>
<DockPanel.Resources>
<XmlDataProvider x:Key="ItemsXml" XPath="Items/Item"
Source="Resources/Items.xml"/>
</DockPanel.Resources>
<ListBox
ItemsSource="{Binding Source={StaticResource ItemsXml}}"
DisplayMemberPath="Name" Name="itemList"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style x:Key="ValueFormat" TargetType="Label">
<Setter Property="ContentStringFormat" Value="{}/{0,3}"/>
</Style>
</Grid.Resources>
<Image Source="Icons/ConditionFalse.png" Grid.Column="0"/>
<TextBox Name="myTextBox" VerticalAlignment="Center" Grid.Column="1"/>
<Label
Name="myLabel"
DataContext="{Binding SelectedItem, ElementName=itemList}"
Style="{StaticResource ValueFormat}"
VerticalAlignment="Center"
Content="{Binding XPath=MyValue}"
Grid.Column="2"/>
</Grid>
</DockPanel>
I want to make Image.Source
depend on the condition that TextBox.Text
be equal to the reference value of Label
*. The reference value is a binding to the XML file so using that as a basis for comparison is also fine. The TextBox
will have a binding to a property that doesn't exist yet so that's available as an option.
*The Label
currently uses ContentStringFormat
to alter its value. If this is problematic it can be gotten rid of.
I can use a DataTrigger
to bind directly to a property representing this condition but that feels like a hack and I would prefer to avoid that. I tried setting up a MultiDataTrigger
as shown below but firstly I couldn't get the condition working for the Label
(it did work for the TextBox
), secondly a constant value is no good in my case. It also doesn't have an "else" clause or a default value for when the condition evaluates to false but if the check can be made in the first place I'm hoping that will be a non-issue.
<Image Grid.Column="0">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Value="/ 42"
Binding="{Binding Text, ElementName=myTextBox}"/>
<Condition Value="/ 42"
Binding="{Binding Content, ElementName=myLabel}"/>
</MultiDataTrigger.Conditions>
<Setter Property="Source" Value="Icons/ConditionTrue.png"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
Harry's comment got me in the right direction.
XAML:
<Grid.Resources>
<local:MyImageConverter x:Key="MyImageConverter"/>
</Grid.Resources>
<Image Grid.Column="0" Grid.Row="0">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="Icons/ConditionFalse.png"/>
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding
Converter="{StaticResource MyImageConverter}">
<Binding ElementName="myTextBox" Path="Text"/>
<Binding ElementName="myLabel" Path="Content"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Source" Value="Icons/ConditionTrue.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
C#
public class MyImageConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
System.Xml.XmlElement labelValue = values[1] as System.Xml.XmlElement;
if (labelValue != null)
{
return ((string)values[0]) == labelValue.InnerText;
}
return false;
}
public object[] ConvertBack(object value, Type[] targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
This very quickly gets very messy. I'm using this for now but as time permits I will rework it into a ViewModel as per Whyaduck's answer.