2009-07-15 13 views
2

Ich weiß, dass das eine ziemlich lange Frage ist, aber ich sah hier keinen anderen Weg, als meinen Code zu posten, den ich aus Gründen der Klarheit so kurz und einfach wie möglich zu halten versuchte. Natürlich Tonnen bewährten Verfahren, dies zu tun verletzt werden, ist das Beispiel, lange genug, wie es ist ..Wie listet Listview sich in WPF auf?

ich eine sehr einfache WPF-Anwendung aus, die

  • zeigt eine Liste von Personen auf der linke Seite von der Bildschirm (Format: Name und Alter zwischen())
  • zeigt alle Eigenschaften der ausgewählten Person auf der rechten Seite des Bildschirms
  • auf der rechten Seite, um die Eigenschaften bearbeiten und die gesamte Auswahl in einem msgbox
  • sehen

Im folgenden Beispiel habe ich das Alter von Bar bearbeitet. In der Liste wird das Alter jedoch nicht aktualisiert. Wenn ich die zugrunde liegende Sammlung frage, scheint sie immer noch aktualisiert worden zu sein. Wie kann ich die Liste wissen lassen?

Im Folgenden sind neben einem Screenshot, den Code und die XAML

HINWEIS: wenn das Bild nicht zeigt, versuchen Sie es in einem neuen Tab oder Fenster zu öffnen.


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(); 
     } 
    } 

Antwort

5

Es reicht nicht, eine ObservableCollection zu haben. Wenn Sie die Bindung für bestimmte Eigenschaften aktualisieren möchten, muss Ihr Personentyp INotifyPropertyChanged implementieren.

EDIT

ich gerade bemerkt habe, ist die linke List-Box nicht, weil Sie kein Datatemplate-Set für eine Person zum Gegenstand haben aktualisiert. Was Sie jetzt haben, ist eine ToString() - Implementierung, die nicht aktualisiert wird, sobald sie an die Benutzeroberfläche berichtet.

Sie brauchen etwas wie folgt aus:

<DataTemplate DataType="{x:Type local:Person}"> 
    <StackPanel Orientation="Horizontal"> 
     <TextBlock Text="{Binding Name}"/> 
     <TextBlock Text="("/> 
     <TextBlock Text="{Binding Age}"/> 
     <TextBlock Text=")"/> 
    </StackPanel> 
</DataTemplate> 
+0

oder erben von DependencyObject –

+0

Nein, das nicht genug ist, wird der Wert gebunden muss entweder ein * DependencyProperty * sein (auf einem DependencyObject) oder der besitzende Typ muss INotifyPropertyChanged (richtig) implementieren. –

+0

Ja, die Grundlagen werden leicht übersehen. Bearbeitete meine Antwort. –

1

Beispiel:

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);  
    } 
} 
+0

Ja, dies implementiert die Schnittstelle ** richtig ** :) –

+0

Nun dachte ich, ich ändere es zu DependencyObject, weil Sie es auch in XAML wie if verwenden können man würde so etwas wollen –

+0

Beachten Sie jedoch, dass das Erben von DependencyObject für diesen Fall wirklich unnötiger Overhead ist. –

2

Zuerst wird in der Erfassungsebene, die ItemsSource von Ihnen ListView eine ObservableCollection

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

Dies sollte wird die ListView t benachrichtigen Hat sich die Reihenfolge der Artikel geändert?

Zweitens auf der Artikelebene.Sie Person sollte auch implementieren INotifyPropertyChanged Schnittstelle wie in den offiziellen document und here erwähnt.

Dadurch wird der ListView mitgeteilt, dass der Inhalt eines Artikels geändert wurde.

Wenn Sie INotifyPropertyChanged implementieren,

public event PropertyChangedEventHandler PropertyChanged; 

definiert ist und zu explizit in dem Setter von Eigenschaften aufgerufen werden. Eine Sache zu beachten ist, dass Sie den Eigenschaftsnamen nicht fest codieren müssen, das Attribut CallerMemberName kann verwendet werden, um automatisch den Namen des Anrufers zu erhalten.

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(); 
      } 
     } 
    } 
}