2009-02-16 20 views
11

Ich habe eine WPF-ListBox, und ich habe einige "FooBar" -Objekte als Elemente (nach Code) hinzugefügt. FooBars sind keine WPF-Objekte, sondern nur Dummies mit einer überschriebenen ToString() - Funktion.WPF-Liste wird aktualisiert, wenn sich das Element ändert

Jetzt, wenn ich eine Eigenschaft ändern, die den ToString beeinflusst, möchte ich die ListBox zu aktualisieren.

  1. Wie kann ich dies 'schnell und schmutzig' (wie Repaint).
  2. Sind Abhängigkeitseigenschaften der Weg, um dies zu gehen?
  3. Lohnt es sich/immer ratsam, eine WPF-Wrapper-Klasse für meine FooBars zu erstellen?

Dank ...

+0

Ich habe meine Antwort aktualisiert, um zu zeigen, wie Sie das zum Laufen bringen können. Scheint, dass das DataTemplate der richtige Weg ist. –

Antwort

12

Ihr Typ sollte INotifyPropertyChanged implementieren, damit eine Sammlung die Änderungen erkennen kann. Wie Sam sagt, übergeben Sie string.Empty als Argument.

Sie müssen die ListBox Datenquelle eine Sammlung sein, die Änderungsbenachrichtigung bereitstellt. Dies geschieht über die Schnittstelle INotifyCollectionChanged (oder die nicht-so-WPF IBindingList Schnittstelle).

Natürlich muss die Schnittstelle INotifyCollectionChanged ausgelöst werden, wenn eines der Elemente des Elements INotifyPropertyChanged das Ereignis auslöst. Glücklicherweise gibt es ein paar Typen im Framework, die diese Logik für Sie bereitstellen. Wahrscheinlich am besten geeignet ist ObservableCollection<T>. Wenn Sie Ihre ListBox an eine ObservableCollection<FooBar> binden, wird die Ereignisverkettung automatisch erfolgen.

Zu einem verwandten Hinweis müssen Sie keine ToString Methode nur verwenden, um WPF zu erhalten, das Objekt auf die gewünschte Weise zu rendern. Sie können eine DataTemplate wie folgt verwenden:

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

Auf diese Weise können Sie die Präsentation des Objekts steuern kann, wo er hingehört - in der XAML.

EDIT 1 Ich bemerkte Ihren Kommentar, dass Sie die ListBox.Items Sammlung als Ihre Sammlung verwenden. Dies wird nicht die erforderliche Bindung machen. Sie sind besser dran, etwas wie:

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

_listBox.ItemsSource = collection; 

Ich habe diesen Code für die Kompiliergenauigkeit nicht überprüft, aber Sie erhalten den Kern.

EDIT 2 Mit der DataTemplate habe ich oben (ich habe es bearbeitet, um Ihren Code zu passen) behebt das Problem.

Es scheint seltsam, dass das Auslösen von PropertyChanged nicht zu einer Aktualisierung des Listenelements führt, aber die Verwendung der ToString-Methode ist nicht die Methode, die WPF verwenden sollte.

Mit diesem DataTemplate wird die UI korrekt an die exakte Eigenschaft gebunden.

Ich habe eine Frage hier eine Weile zurück zu tun string formatting in a WPF binding. Vielleicht finden Sie es hilfreich.

EDIT 3 Ich bin verwirrt, warum dies immer noch nicht funktioniert für Sie. Hier ist der vollständige Quellcode für das Fenster, das ich verwende.

-Code hinter:

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> 
+0

Danke für die Info, ich habe versucht, was du in deinem Edit gesagt hast, aber keine Freude. Elemente werden zum Listenfeld hinzugefügt, aber nichts aktualisiert, wenn ich feuere PropertyChanged - niemand scheint zu hören ... – Benjol

+0

Haben Ihre Elemente das INotifyPropertyChanged-Ereignis ausgelöst, wenn sich ihre Eigenschaften ändern? Können Sie einen Beispielcode in Ihre Frage eingeben? –

+0

Mein Fehler, ich habe nur den Code geändert, nicht den XAML Ich werde es morgen nochmal versuchen und dich markieren – Benjol

0

Wenn das Sammelobjekt, um die Elemente zu speichern, verwenden ein ObservableCollection <> dann das für Sie behandelt.

Wenn die Sammlung geändert wird, werden alle damit verbundenen Steuerelemente aktualisiert und umgekehrt.

+0

Nun die Listbox-Elemente ist die Sammlung: myList.Items.Add (new FooBar()); – Benjol

1

Versuchen Sie, die INotifyPropertyChanged-Schnittstelle auf Ihren FooBar-Objekten zu implementieren. Wenn sie sich ändern, werden PropertyChanged-Ereignisse ausgelöst, wobei string.Empty als Eigenschaftsname übergeben wird. Das sollte den Trick machen.

+0

Ja, aber nein. Niemand scheint bei meinem Event angemeldet zu sein :( – Benjol

+0

Es ist nicht genug. Sie sollten auch ListBox sagen, von welcher Eigenschaft Ihres Objekts es eine Zeichenfolge zum Anzeigen bekommen sollte. DataTemplate ist nicht die einzige Möglichkeit, dies zu tun. Der einfachste Weg ist das 'DisplayMemberPath =" PropertyName "Attribut zur ListBox hinzuzufügen. – Lu55

3

Die hier richtige Lösung ist eine ObservableCollection<> für Ihre ListBox IetmsSource Eigenschaft zu verwenden. WPF erkennt automatisch Änderungen am Inhalt dieser Sammlung und erzwingt die Aktualisierung der entsprechenden ListBox, um die Änderungen zu berücksichtigen.

Möglicherweise möchten Sie diesen MSDN-Artikel für weitere Informationen lesen. Es wurde geschrieben, um speziell zu erklären, wie man dieses Szenario für diese

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

+0

Ich stimme Ihnen hier vollkommen zu. – Blounty

0

Hier ist der C# -Code Ich habe Arbeit zu handhaben:

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

Hier ist die 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> 

Durch Drücken der Schaltfläche wird die erste Eigenschaft der ersten FooBar aktualisiert und in der ListBox angezeigt.

Verwandte Themen