-2

I've got a ListBox, containing Messages, which are simple (typeEnum, string) objects, and are displayed using a datatemplate:

<ListBox ItemsSource="{Binding Messages}"
                Background="DimGray" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" >
                        <TextBlock Text="{Binding MessageText}" >
                            <TextBlock.Foreground>
                                <MultiBinding Converter="{StaticResource TypeEnumToBrushConverter}" >
                                    <Binding Path="TypeEnum" />
                                    <Binding Path="MessageColours" RelativeSource="{RelativeSource AncestorType={x:Type Window}}" />
                                </MultiBinding>
                            </TextBlock.Foreground>
                        </TextBlock>
                        <Button Click="ButtonBase_OnClick1" >T</Button>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

I'm trying to set the foreground of the message to whatever the MessageColours (a collection of TypeEnum - Brush pairs), which I pass into the converter.

This works for the initial brushes I manually add to MessageColours.

I'm changing them in another window, and the collection gets updated successfully, but the ListBox message colour does not update. MessageColours was originally a ObservableCollection of MessageColourSettings (TypeEnum, Brush) but I changed it to an implementation that implements INotifyPropertyChanged so it notifies when properties in a collection item change as well, but it didn't help.

MessageColourSettings implements INotifyPropertyChanged and it is firing when the color is changed on the other window, and MessageColours collection is firing its item property changed.

When I click that button, it toggles the typeEnum from A to B and back to A, upon which the message now has the updated colour, so I think something is wrong with the multibinding.

The converter:

public class TypeEnumToBrushConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            TypeEnum typeEnum = (TypeEnum) values[0];
            var settings = values[1] as FullyObservableCollection<MessageColourSetting>;
            return settings.Single(setting => setting.TypeEnum == typeEnum).Brush;
        }
    }

Any insights would be appreciated

Kage
  • 563
  • 2
  • 10
  • 20

1 Answers1

0

I got it to work, but I'm still not sure why it is necessary, or how it is intended to work.

The implementation I'm using for MessageColours is basically an ObservableDictionary than also notifies when properties on Message are changed that I got from https://stackoverflow.com/a/32013610/211186

It was working correctly, but the MultiBinding was not responding to the notifications.

So I ended up having to do it manually.

MessageColours.ItemPropertyChanged += (sender, args) => Update(sender, args);
 private void Update(object sender, ItemPropertyChangedEventArgs args)
        {

            TypeEnum typeEnum = (sender as FullyObservableCollection<MessageColourSetting>)[args.CollectionIndex].TypeEnum;

            IEnumerable<Message> messages =
                Box.ItemContainerGenerator.Items.Select(m => m as Message).Where(m => m.TypeEnum == typeEnum);

            List<ListBoxItem> ListBoxItems = messages
                                            .Select(message => (ListBoxItem) Box.ItemContainerGenerator.ContainerFromItem(message))
                                            .ToList();

            foreach (ListBoxItem listBoxItem in ListBoxItems)
            {
                ContentPresenter presenter = FindVisualChildrenUtils.FindVisualChild<ContentPresenter>(listBoxItem);
                DataTemplate template = presenter.ContentTemplate;
                TextBlock textBlock = (TextBlock) template.FindName("messageText", presenter);
                MultiBindingExpression bindingExpression =
                    BindingOperations.GetMultiBindingExpression(textBlock, ForegroundProperty);
                bindingExpression.UpdateTarget();
            }
        }

It seems very hackish to me but this was just experimental code.

Is there an easier way to get Multibinding to update?

Kage
  • 563
  • 2
  • 10
  • 20