0

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>
Community
  • 1
  • 1
mkjeldsen
  • 2,053
  • 3
  • 23
  • 28

2 Answers2

1

Unless there's a technical reason why it's not possible, I'd suggest that you bind to a ViewModel rather than binding to elements in the XAML tree. With a ViewModel you could bind itemList.SelectedItem to a property on the ViewModel (for example, call it CurrentItem). Also, bind your Image's Source to another ViewModel property (maybe named StatusImageSource). Then, when a user changes the selected value in itemList, you can check for equality to the label's value in CurrentItem.Set, and update the StatusImageSource value appropriately.

Whyaduck
  • 124
  • 5
  • I'm upvoting this because it's good practice but I'm not going to accept it because a solution exists to the problem as stated. – mkjeldsen Dec 09 '12 at 23:54
0

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.

mkjeldsen
  • 2,053
  • 3
  • 23
  • 28