Как сделать так, чтобы listview обновлялся сам в wpf? - PullRequest
2 голосов
/ 15 июля 2009

Я понимаю, что это довольно длинный вопрос, но я не нашел здесь другого пути, кроме как опубликовать свой код, который я старался сделать максимально коротким и простым для ясности. Конечно, тонны лучших практик нарушаются, пример достаточно длинный, как есть.

Я сделал очень простое wpf-приложение, которое

  • показывает список людей в левой части экрана (формат: имя и возраст между ())
  • показывает все свойства выбранного человека в правой части экрана
  • справа вы можете редактировать свойства и просматривать весь выбор в сообщении

В следующем примере я отредактировал возраст Бар. Однако в списке возраст не обновляется. Если я спрашиваю основную коллекцию, она все еще кажется обновленной .. Как я могу сделать список знать?

Ниже приведены, помимо скриншота, код и XAML

ПРИМЕЧАНИЕ. Если изображение не отображается, попробуйте открыть его в новой вкладке или окне.


alt text


namespace ASAPBinding
{
    public class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }

        public override string ToString()
        {
            return String.Format("{0} ({1})",Name,Age);     
        }
    }

}

namespace ASAPBinding
{
    public class Dal
    {
        public ObservableCollection<Person> Persons { get; set; }

        public Dal()
        {
            Persons = new ObservableCollection<Person>();
            Persons.Add(new Person() {Name = "Bar", Age = 25});
            Persons.Add(new Person() {Name = "Foo", Age = 50});
        }

        public void PrintOutCollection()
        {
            MessageBox.Show(
                Persons[0].ToString() + "\n" + Persons[1].ToString()
                );
        }
    }
}

<Window x:Class="ASAPBinding.EditPersons"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ASAPBinding"
     x:Name="window1"
    Title="EditPersons" Height="300" Width="300">
    <Window.Resources>
        <local:Dal x:Key="dal"/>
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*" />
            <ColumnDefinition Width="1*" />
        </Grid.ColumnDefinitions>

        <ListBox Name="ListBox1" 
                 ItemsSource="{Binding Source={StaticResource dal}, Path=Persons, Mode=TwoWay}" 
                 Grid.Column="0"/>
        <StackPanel 
            DataContext="{Binding ElementName=ListBox1, Path=SelectedItem, Mode=TwoWay}"
            Grid.Column="1" Margin="0,0,0,108">

            <TextBox Text="{Binding Path=Name}" />
            <TextBox Text="{Binding Path=Age}"  />
            <Button Click="Button_Click">Show Collection</Button>
        </StackPanel>
    </Grid>
</Window>

public partial class EditPersons : Window
    {
        public EditPersons()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Dal dal = (Dal) window1.FindResource("dal");
            dal.PrintOutCollection();
        }
    }

Ответы [ 3 ]

5 голосов
/ 15 июля 2009

Недостаточно иметь ObservableCollection, если вы хотите обновить привязку для определенных свойств, ваш тип Person должен реализовать INotifyPropertyChanged .

EDIT

Я только что заметил, ваш левый ListBox не обновляется, потому что у вас не установлен DataTemplate для объекта Person. Теперь у вас есть реализация ToString (), которая не обновляется после отправки отчета в пользовательский интерфейс.

Вам нужно что-то вроде этого:

<DataTemplate DataType="{x:Type local:Person}">
   <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding Name}"/>
        <TextBlock Text="("/>
        <TextBlock Text="{Binding Age}"/>
        <TextBlock Text=")"/>
    </StackPanel>
</DataTemplate>
2 голосов
/ 14 марта 2015

Во-первых, на уровне коллекции, ItemsSource из вас ListView должно быть ObservableCollection

ObservableCollection<Person> Persons = new ObservableCollection<Person>();

Это уведомит ListView, что порядок предметов был изменен.

Во-вторых, на уровне предмета. Вы Person должны также реализовать интерфейс INotifyPropertyChanged, как указано в официальном документе и здесь .

Это сообщит ListView, что содержимое элемента было изменено.

При реализации INotifyPropertyChanged,

public event PropertyChangedEventHandler PropertyChanged;

определено и должно вызываться явно в наборе свойств. Стоит заметить, что вам не нужно жестко кодировать имя свойства, атрибут CallerMemberName может использоваться для автоматического получения имени вызывающего.

public class DemoCustomer : INotifyPropertyChanged
{
    // These fields hold the values for the public properties. 
    private Guid idValue = Guid.NewGuid();
    private string customerNameValue = String.Empty;
    private string phoneNumberValue = String.Empty;

    public event PropertyChangedEventHandler PropertyChanged;

    // This method is called by the Set accessor of each property. 
    // The CallerMemberName attribute that is applied to the optional propertyName 
    // parameter causes the property name of the caller to be substituted as an argument. 
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    // The constructor is private to enforce the factory pattern. 
    private DemoCustomer()
    {
        customerNameValue = "Customer";
        phoneNumberValue = "(312)555-0100";
    }

    // This is the public factory method. 
    public static DemoCustomer CreateNewCustomer()
    {
        return new DemoCustomer();
    }

    // This property represents an ID, suitable 
    // for use as a primary key in a database. 
    public Guid ID
    {
        get
        {
            return this.idValue;
        }
    }

    public string CustomerName
    {
        get
        {
            return this.customerNameValue;
        }

        set
        {
            if (value != this.customerNameValue)
            {
                this.customerNameValue = value;
                NotifyPropertyChanged();
            }
        }
    }

    public string PhoneNumber
    {
        get
        {
            return this.phoneNumberValue;
        }

        set
        {
            if (value != this.phoneNumberValue)
            {
                this.phoneNumberValue = value;
                NotifyPropertyChanged();
            }
        }
    }
}
1 голос
/ 15 июля 2009

пример:

public class Person : DependencyObject
{
    public static readonly DependencyProperty NameProperty = DependencyProperty.Register(
        "Name",
        typeof(string),
        typeof(Person)
    );

    public static readonly DependencyProperty AgeProperty = DependencyProperty.Register(
        "Age",
        typeof(int),
        typeof(Person)
    );

    public string Name
    {
        get { return (string)GetValue(NameProperty); }
        set { SetValue(NameProperty, value); }
    }

    public int Age
    {
        get { return (int)GetValue(AgeProperty ); }
        set { SetValue(AgeProperty , value); }
    }

    public override string ToString()
    {
            return String.Format("{0} ({1})",Name,Age);     
    }
}
...