2009-06-30 5 views
9

Ich versuche erfolgreich TwoWay eine ObservableCollection an TextBoxen in einem DataTemplate zu binden. Ich kann die Daten korrekt anzeigen, aber ich kann die Listendaten nicht über die Benutzeroberfläche ändern. Ich habe eine Model-Klasse namens 'model', die eine ObservableCollection namens 'List' enthält. Die Klasse implementiert die INotifyPropertyChanged-Schnittstelle. Hier ist das Xaml für die Shell. Die Datacontext für Window1 des Gitters wird auf "theGrid.DataContext = model"Wie kann ich eine ObservableCollection an TextBoxen in einem DataTemplate binden?

<Window x:Class="BindThat.Window1" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:local="clr-namespace:BindThat" 
Title="Window1" Height="300" Width="300"> 
<StackPanel x:Name="theGrid"> 
    <GroupBox BorderBrush="LightGreen"> 
     <GroupBox.Header> 
      <TextBlock Text="Group" /> 
     </GroupBox.Header> 
     <ItemsControl ItemsSource="{Binding Path=List}"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 
    </GroupBox> 
</StackPanel> 

Dies ist der Code für die Modellklasse ist:

class Model : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    private void NotifyPropertyChanged(string name) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, new PropertyChangedEventArgs(name)); 
    } 

    private ObservableCollection<string> _list = new ObservableCollection<string>(); 
    public ObservableCollection<string> List 
    { 
     get { return _list; } 
     set 
     { 
      _list = value; 
      NotifyPropertyChanged("List"); 
     } 
    } 

    public Model() 
    { 
     List.Add("why"); 
     List.Add("not"); 
     List.Add("these?"); 
    } 
} 

Könnte jemand raten, wenn ich werde das ist der richtige Weg?

Antwort

12

Sie benötigen eine Eigenschaft, um in zwei Richtungen zu binden, also ist String dafür nicht gut.

Wickeln Sie es in einem String-Objekt, wie folgt aus:

public class Model 
{ 
    public ObservableCollection<StringObject> List { get; private set; } 
    public Model() 
    { 
     List = new ObservableCollection<StringObject> 
        { 
         new StringObject {Value = "why"}, 
         new StringObject {Value = "not"}, 
         new StringObject {Value = "these"}, 
        }; 
    } 
} 

public class StringObject 
{ 
    public string Value { get; set; } 
} 

und binden an Value-Eigenschaft statt ""

Sie müssen auch nicht über eine Änderung der beobachtbaren Sammlung informieren. Bevor Ihr Modell also nicht über andere Eigenschaften verfügt, muss es nicht über INotifyPropertyChange verfügen. Wenn Sie möchten, dass ItemsControl auf Änderungen in den einzelnen StringObjects reagiert, sollten Sie INotifyPropertyChanged einem StringObject hinzufügen.

Und wieder zwei Art und Weise verbindlich ist standardmäßig so müssen Sie nur

<TextBox Text="{Binding Path=Value}" /> 

in Ihrer Bindung.

+0

Works for me! Vielen Dank!! – Johnathan1

+1

Ich glaube nicht, dass Sie "Path =" in der Text-Eigenschaft setzen müssen, "Text =" {Binding Value} "würde auch funktionieren – user1069816

+0

Warum eine einzelne String-Eigenschaft funktioniert, aber nicht List ? – YukiSakura

1

Ich glaube, Sie müssen Ihre Sammlung Elemente von DependencyObject für TwoWay-Bindung zu arbeiten ableiten. Etwas wie:

public class DependencyString: DependencyObject { 
    public string Value { 
     get { return (string)GetValue(ValueProperty); } 
     set { SetValue(ValueProperty, value); } 
    } 

    public static readonly DependencyProperty ValueProperty = 
     DependencyProperty.Register("Value", typeof(string), typeof(DependencyString), new UIPropertyMetadata("")); 

    public override string ToString() { 
     return Value; 
    } 

    public DependencyString(string s) { 
     this.Value = s; 
    } 
} 

public class Model { 
    private ObservableCollection<DependencyString> _list = new ObservableCollection<DependencyString>(); 
    public ObservableCollection<DependencyString> List { 
     get { return _list; } 
    } 

    public Model() { 
     List.Add(new DependencyString("why")); 
     List.Add(new DependencyString("not")); 
     List.Add(new DependencyString("these?")); 
    } 
} 

...

<StackPanel x:Name="theGrid"> 
    <GroupBox BorderBrush="LightGreen"> 
     <GroupBox.Header> 
      <TextBlock Text="Group" />   
     </GroupBox.Header> 
     <ItemsControl ItemsSource="{Binding Path=List}"> 
      <ItemsControl.ItemTemplate> 
       <DataTemplate> 
        <TextBox Text="{Binding Path=Value, Mode=TwoWay}"/> 
       </DataTemplate> 
      </ItemsControl.ItemTemplate> 
     </ItemsControl> 
    </GroupBox> 
</StackPanel> 
+0

Ich denke nicht, dass eine DependencyProperty in diesem Fall erforderlich ist. Das ist nur notwendig, wenn Sie diese Eigenschaft an etwas anderes binden möchten. – Andy

+0

Basierend auf beiden Ideen konnte ich nun eine Lösung für meine Anwendung finden. Danke für deinen Beitrag !! – Johnathan1

+0

Das hat mir sehr geholfen, danke. Der Teil, den ich vermisste war, den Mode = TwoWay einzustellen, so dass ich auf die aktualisierten Daten von der listbox.itemsSource zugreifen konnte, nachdem der Benutzer seine Änderungen vorgenommen hatte. – javram