Конвертеры WPF и ObservableCollections

Я привязываю ObservableCollection к элементу управления, у которого есть преобразователь для изменения его видимости в зависимости от того, имеет ли коллекция какие-либо значения или нет:

Упрощенный пример:

XAML:

<Window.Resources>
    <local:MyConverter x:Key="converter"/>
</Window.Resources>

<Grid x:Name="grid">
    <Rectangle Height="100" Width="200" Fill="CornflowerBlue"
                Visibility="{Binding Converter={StaticResource converter}}"/>
    <Button Content="click" 
            HorizontalAlignment="Left" VerticalAlignment="Top" 
            Click="Button_Click"/>
</Grid>

C #:

ObservableCollection<string> strings;

public MainWindow()
{
    InitializeComponent();

    strings = new ObservableCollection<string>();
    grid.DataContext = strings;
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    strings.Add("new value");
}

Когда коллекция связана, Rectangle отображается, когда есть значения, а не когда коллекция пуста. Однако, если коллекция пуста и я добавляю значение во время выполнения, Rectangle не отображается (метод преобразователя Convert даже не запускается). Я что-то упускаю или просто пытаюсь слишком много требовать от IValueConverter?


person CatBusStop    schedule 27.06.2011    source источник
comment
Итак ... привязка к ObservableCollection ‹T› .Count заставляет конвертер работать так, как ожидалось, поэтому я предполагаю, что добавление значения в коллекцию не запускает событие PropertyChanged ... Временное решение, которое я думаю, было бы объединить collection и это метод .Count, но это звучит противно ...   -  person CatBusStop    schedule 27.06.2011


Ответы (3)


Хорошо, вот как я решил проблему с помощью MultiValueConverter

Конвертер теперь выглядит так:

public object Convert(
    object[] values, 
    Type targetType, 
    object parameter, 
    System.Globalization.CultureInfo culture)
{
    ObservableCollection<string> strings = 
        values[0] as ObservableCollection<string>;

    if (strings == null || !strings.Any())
        return Visibility.Collapsed;
    else
        return Visibility.Visible;
}

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

И теперь XAML выглядит так:

<Rectangle Height="100" Width="200" Fill="CornflowerBlue">
    <Rectangle.Visibility>
        <MultiBinding Converter="{StaticResource converter}">
            <Binding Path="."/>
            <Binding Path="Count"/>
        </MultiBinding>
    </Rectangle.Visibility>
</Rectangle>

C # остался прежним :)

person CatBusStop    schedule 27.06.2011
comment
Причина, по которой это работает, заключается в том, что свойство Count изменено и запускает конвертер. Конвертер Multi в этом случае бесполезен, то же самое, что и при использовании <Rectangle Height="100" Width="200" Fill="CornflowerBlue" Visibility={Binding Path=Count, Mode=OneWay, Converter={StaticResource CuntToVisibilityConverter}" />, где CountTovisibilityConverter реализует IValueConverter, и вам просто нужно сравнить значение (тип int) с 0, чтобы вернуть видимость. еще раз, с наилучшими пожеланиями - person daniell; 28.06.2011
comment
Следуя тому, что сказал Дэниелл, это не сработает, если вы используете ObservableCollection.SetItem, который не обновляет счетчик. - person hypehuman; 30.08.2016
comment
Конвертер из чего в видимость @daniell !? - person Matt Searles; 15.12.2016

Я думаю, что преобразователь в привязке всегда вызывается, если источник привязки был обновлен, и уведомляет об этом обновлении (как DependencyProperty или с помощью INotifyPropertyChanged). Однако ObservableCollection не вызывает событие PropertyChanged, если элемент был добавлен или удален, но вызывает событие CollectionChanged. Он вообще не вызывает никаких событий, если элемент в коллекции изменяется. Даже если сам элемент вызывает PropertyChanged, это не обновит привязку к коллекции, поскольку источником привязки является не элемент, а коллекция.

Боюсь, ваш подход не сработает. Вы можете напрямую привязаться к ObservableCollection.Count и добавить к нему соответствующий математический преобразователь для выполнения инверсии и умножения, но свойство Count не выполняет уведомление об изменении, поэтому это не вариант. Я думаю, вам придется предоставить другое свойство в вашей ViewModel или коде программной части, которое обрабатывает эти случаи ...

наилучшие пожелания,

person daniell    schedule 27.06.2011
comment
Да, я уже читал сообщение, из которого вы, похоже, заимствовали это (stackoverflow.com/questions/2816163/). MultiValueConverter выполняет свою работу в любом случае - person CatBusStop; 27.06.2011
comment
плагиат ?! рад, что вы нашли свое решение .. с наилучшими пожеланиями - person daniell; 28.06.2011

Вы должны установить DataContext после создания коллекции; вполне вероятно, что вы инициализируете коллекцию «strings» значением «null», вы устанавливаете DataContext в конструкторе на это значение (например, null), затем вы фактически создаете коллекцию - таким образом, DataContext остается нулевым.

Вы должны снова установить DataContext после создания коллекции.

person Andy    schedule 27.06.2011
comment
Установка DataContext после создания коллекции означает, что она работает только один раз, она по-прежнему не меняется, если вы добавляете значения во время выполнения. У меня есть решение с использованием MultiValueConverters, которое я опубликую через минуту :) - person CatBusStop; 27.06.2011