Обновление списка WPF при изменении элемента - PullRequest
12 голосов
/ 16 февраля 2009

У меня есть WPF ListBox, и я добавил несколько объектов 'FooBar' как элементы (по коду). FooBars - это не объекты WPF, а просто тупой класс с перезаписанной функцией ToString ().

Теперь, когда я изменяю свойство, которое влияет на ToString, я хотел бы получить ListBox для обновления.

  1. Как я могу сделать это «быстро и грязно» (например, перекрасить).
  2. Являются ли свойства зависимостей таким путем?
  3. Стоит ли / всегда целесообразно создавать класс-оболочку wpf для моих FooBars?

Спасибо ...

Ответы [ 5 ]

13 голосов
/ 16 февраля 2009

Ваш тип должен реализовывать INotifyPropertyChanged, чтобы коллекция могла обнаружить изменения. Как говорит Сэм, передайте string.Empty в качестве аргумента.

Вы также должны иметь источник данных ListBox, представляющий собой коллекцию, которая предоставляет уведомление об изменении. Это делается через интерфейс INotifyCollectionChanged (или интерфейс not-so-WPF IBindingList).

Конечно, вам нужен интерфейс INotifyCollectionChanged, чтобы запускать каждый раз, когда один из элементов INotifyPropertyChanged запускает свое событие. К счастью, есть несколько типов в структуре, которые предоставляют эту логику для вас. Вероятно, наиболее подходящим является ObservableCollection<T>. Если вы свяжете свой ListBox с ObservableCollection<FooBar>, тогда цепочка событий произойдет автоматически.

В связанной заметке вам не нужно использовать метод ToString только для того, чтобы заставить WPF визуализировать объект так, как вы хотите. Вы можете использовать DataTemplate вот так:

<ListBox x:Name="listBox1">
    <ListBox.Resources>
        <DataTemplate DataType="{x:Type local:FooBar}">
            <TextBlock Text="{Binding Path=Property}"/>
        </DataTemplate>
    </ListBox.Resources>
</ListBox>

Таким образом, вы можете управлять представлением объекта, которому он принадлежит - в XAML.

РЕДАКТИРОВАТЬ 1 Я заметил ваш комментарий, что вы используете коллекцию ListBox.Items в качестве своей коллекции. Это не сделает связывание обязательным. Вам лучше делать что-то вроде:

var collection = new ObservableCollection<FooBar>();
collection.Add(fooBar1);

_listBox.ItemsSource = collection;

Я не проверял этот код на точность компиляции, но вы поняли суть.

РЕДАКТИРОВАТЬ 2 Использование DataTemplate, которое я дал выше (я отредактировал его в соответствии с вашим кодом), решает проблему.

Кажется странным, что запуск PropertyChanged не приводит к обновлению элемента списка, но использование метода ToString не подходит для работы WPF.

Используя этот DataTemplate, пользовательский интерфейс правильно связывается с точным свойством.

Некоторое время назад я задал вопрос о форматировании строки в привязке WPF . Вы можете найти это полезным.

РЕДАКТИРОВАТЬ 3 Я озадачен тем, почему это все еще не работает для вас. Вот полный исходный код окна, которое я использую.

Код позади:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;

namespace StackOverflow.ListBoxBindingExample
{
    public partial class Window1
    {
        private readonly FooBar _fooBar;

        public Window1()
        {
            InitializeComponent();

            _fooBar = new FooBar("Original value");

            listBox1.ItemsSource = new ObservableCollection<FooBar> { _fooBar };
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            _fooBar.Property = "Changed value";
        }
    }

    public sealed class FooBar : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string m_Property;

        public FooBar(string initval)
        {
            m_Property = initval;
        }

        public string Property
        {
            get { return m_Property; }
            set
            {
                m_Property = value;
                OnPropertyChanged("Property");
            }
        }

        private void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

XAML:

<Window x:Class="StackOverflow.ListBoxBindingExample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:StackOverflow.ListBoxBindingExample"
    Title="Window1" Height="300" Width="300">
    <DockPanel LastChildFill="True">
        <Button Click="button1_Click" DockPanel.Dock="Top">Click Me!</Button>
        <ListBox x:Name="listBox1">
            <ListBox.Resources>
                <DataTemplate DataType="{x:Type local:FooBar}">
                    <TextBlock Text="{Binding Path=Property}"/>
                </DataTemplate>
            </ListBox.Resources>
        </ListBox>
    </DockPanel>
</Window>
3 голосов
/ 16 февраля 2009

Правильным решением здесь является использование ObservableCollection <> для вашего свойства ListBox IetmsSource. WPF автоматически обнаружит любые изменения в содержимом этой коллекции и заставит соответствующий ListBox обновиться, чтобы отразить изменения.

Вы можете прочитать эту статью MSDN для получения дополнительной информации. Это было написано, чтобы конкретно объяснить, как справиться с этим сценарием

http://msdn.microsoft.com/en-us/magazine/dd252944.aspx?pr=blog

1 голос
/ 16 февраля 2009

Попробуйте реализовать интерфейс INotifyPropertyChanged на ваших объектах FooBar. Когда они изменяются, генерируют события PropertyChanged, передавая string.Empty в качестве имени свойства. Это должно сработать.

0 голосов
/ 17 февраля 2009

Вот код C #, который у меня работает для этого:

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace ListboxOfFoobar
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ObservableCollection<FooBar> all = (ObservableCollection<FooBar>)FindResource("foobars");
            all[0].P1 = all[0].P1 + "1";
        }
    }
    public class FooBar : INotifyPropertyChanged
    {
        public FooBar(string a1, string a2, string a3, string a4)
        {
            P1 = a1;
            P2 = a2;
            P3 = a3;
            P4 = a4;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        private String p1;
        public string P1
        {
            get { return p1; }
            set
            {
                if (value != this.p1)
                {
                    this.p1 = value;
                    NotifyPropertyChanged("P1");
                }
            }
        }
        private String p2;
        public string P2
        {
            get { return p2; }
            set
            {
                if (value != this.p2)
                {
                    this.p2 = value;
                    NotifyPropertyChanged("P2");
                }
            }
        }
        private String p3;
        public string P3
        {
            get { return p3; }
            set
            {
                if (value != this.p3)
                {
                    this.p3 = value;
                    NotifyPropertyChanged("P3");
                }
            }
        }
        private String p4;
        public string P4
        {
            get { return p4; }
            set
            {
                if (value != this.p4)
                {
                    this.p4 = value;
                    NotifyPropertyChanged("P4");
                }
            }
        }
        public string X
        {
            get { return "Foooooo"; }
        }
    }
    public class Foos : ObservableCollection<FooBar>
    {
        public Foos()
        {
            this.Add(new FooBar("a", "b", "c", "d"));
            this.Add(new FooBar("e", "f", "g", "h"));
            this.Add(new FooBar("i", "j", "k", "l"));
            this.Add(new FooBar("m", "n", "o", "p"));
        }
    }
}

Вот XAML:

<Window x:Class="ListboxOfFoobar.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ListboxOfFoobar"
    xmlns:debug="clr-namespace:System.Diagnostics;assembly=System"

    Title="Window1" Height="300" Width="300"        
        >
    <Window.Resources>
        <local:Foos x:Key="foobars" />
        <DataTemplate x:Key="itemTemplate">
            <StackPanel Orientation="Horizontal">
                <TextBlock MinWidth="80" Text="{Binding Path=P1}"/>
                <TextBlock MinWidth="80" Text="{Binding Path=P2}"/>
                <TextBlock MinWidth="80" Text="{Binding Path=P3}"/>
                <TextBlock MinWidth="80" Text="{Binding Path=P4}"/>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>

    <DockPanel>
        <ListBox DockPanel.Dock="Top"
         ItemsSource="{StaticResource foobars}"
         ItemTemplate="{StaticResource itemTemplate}" Height="229" />
        <Button  Content="Modify FooBar" Click="Button_Click" DockPanel.Dock="Bottom" />
    </DockPanel>
</Window>

Нажатие кнопки вызывает обновление первого свойства первого FooBar и его отображение в ListBox.

0 голосов
/ 16 февраля 2009

Если объект коллекции, который вы используете для хранения предметов, является коллекцией observablecollection <>, то это обрабатывается для вас.

То есть, если коллекция будет изменена, любые элементы управления, связанные с ней, будут обновлены, и наоборот.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...