How to set a property on WPF ComboBoxItem only when it is the selected item and it satisfies condition?

I am having trouble making a combo box item appear with the font color red only when it is the selected item AND its underlying ID matches the last ID in the ItemSource. Here's what i've got so far, this makes it red only when it is the selected item, but how do i also check to see if the underlying property "ID" matches the last entry in the ObservableCollection that contains it, ie only when SelectedItem's ID== Collection[Length-1].ID?

<ComboBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"
            ItemsSource="{Binding BillingCycles}" SelectedItem="{Binding SelectedBillingCycle}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding BillingCycleDescription}" >
                <TextBlock.Foreground>
                    <MultiBinding Converter="{StaticResource IsCurrentCycleColorConverter}">
                        <Binding Path="ItemsSource" RelativeSource="{RelativeSource AncestorType={x:Type ComboBox}}"/>
                        <Binding Path="SelectedItem" RelativeSource="{RelativeSource AncestorType={x:Type ComboBox}}"/>
                        <Binding RelativeSource="{RelativeSource AncestorType={x:Type ComboBoxItem}}"/>
                    </MultiBinding>
                </TextBlock.Foreground>
            </TextBlock>
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

So, if SelectedBillingCycle.ID != BillingCycles[BillingCycle.Length-1].ID, the text should appear red. I'm not sure how I'd refer to the BillingCycles[BillingCycles.Length-1] to compare it to.

///EDIT:

Changed the XAML, this is getting closer but changes ALL the comboBoxItems, not just the selectedItem. I'm thinking I need to use a template selector of some kind or rethink the XAML completely.

Upvotes: 1

Views: 1399

Answers (2)

I ended up having to use a DataTemplateSelector class to differentiate between the SelectedItem and other comboboxItems:

public class ComboBoxItemTemplateSelector : DataTemplateSelector
{
    public DataTemplate SelectedTemplate { get; set; }
    public DataTemplate DropDownTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        ComboBoxItem comboBoxItem = container.GetVisualParent<ComboBoxItem>();
        if (comboBoxItem == null)
        {
            return SelectedTemplate;
        }
        return DropDownTemplate;
    }
}

and this extension class:

public static T GetVisualParent<T>(this DependencyObject child) where T : Visual
    {
        while ((child != null) && !(child is T))
        {
            child = VisualTreeHelper.GetParent(child);
        }

        return child as T;
    }

and here is the XAML:

<ComboBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2"
                  ItemsSource="{Binding BillingCycles}" SelectedItem="{Binding SelectedBillingCycle}">
            <ComboBox.ItemTemplateSelector>
                <b:ComboBoxItemTemplateSelector>
                    <b:ComboBoxItemTemplateSelector.SelectedTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding BillingCycleDescription}">
                                <TextBlock.Foreground>
                                    <MultiBinding Converter="{StaticResource IsCurrentCycleColorConverter}">
                                        <Binding Path="ItemsSource" RelativeSource="{RelativeSource AncestorType={x:Type ComboBox}}"/>
                                        <Binding Path="SelectedItem" RelativeSource="{RelativeSource AncestorType={x:Type ComboBox}}"/>
                                    </MultiBinding>
                                </TextBlock.Foreground>
                            </TextBlock>
                        </DataTemplate>
                    </b:ComboBoxItemTemplateSelector.SelectedTemplate>
                    <b:ComboBoxItemTemplateSelector.DropDownTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding BillingCycleDescription}" />
                        </DataTemplate>
                    </b:ComboBoxItemTemplateSelector.DropDownTemplate>
                </b:ComboBoxItemTemplateSelector>
            </ComboBox.ItemTemplateSelector>
        </ComboBox>

and use this converter, which i figured out using help from Ethicallogics:

public class IsCurrentCycleColorConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        SolidColorBrush color = new SolidColorBrush(Colors.Black);

        if (values != null && values.Count() == 2)
        {
            var itemsSource = values[0] as ObservableCollection<BillingCycle>;//This is the Type of you Collection binded to ItemsSource
            if (!(itemsSource != null && itemsSource.Any() && itemsSource.First() == (BillingCycle)values[1]))
            {
                color = new SolidColorBrush(Colors.DarkRed);
            }
        }

        return color;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Upvotes: 1

yo chauhan
yo chauhan

Reputation: 12305

Try this : Suppose i have a UserType class

public class UserType
{
    public string Name { get; set; }
    public string Description { get; set; }
}

and I have ObservableCollection of UserType binded to itemsSource of ComboBox

public ObservableCollection<UserType> UserTypes { get; set; }

xaml

    <Window.Resources>
    <local:ComboBoxForegroundConverter x:Key="ComboConv"/>
    <Style TargetType="{x:Type ComboBoxItem}">
        <Setter Property="Foreground">
            <Setter.Value>
                <MultiBinding Converter="{StaticResource ComboConv}">
                    <Binding Path="ItemsSource" RelativeSource="{RelativeSource AncestorType={x:Type ComboBox}}"/>
                    <Binding Path="SelectedItem" RelativeSource="{RelativeSource AncestorType={x:Type ComboBox}}"/>
                    <Binding/>
                </MultiBinding>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<StackPanel>
    <ComboBox ItemsSource="{Binding UserTypes}" DisplayMemberPath="Name"/>
</StackPanel>

MultiValueConverter

    public class ComboBoxForegroundConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values != null && values.Count() == 3)
        {
            var itemsSource = values[0] as ObservableCollection<UserType>;//This is the Type of you Collection binded to ItemsSource
            if (itemsSource != null && itemsSource.Any() && itemsSource.Last() == values[1] && values[2]==values[1])
                return new SolidColorBrush(Colors.Red);
        }

        return new SolidColorBrush(Colors.Black);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

In your case DisplayMember path will be BillingCycleDescription.

Upvotes: 1

Related Questions