2010-10-04 7 views
0

J'ai eu quelques problèmes pour obtenir une liste de sélection pour lier correctement une collection.La propriété n'est pas liée correctement dans ListBox DataTemplate

Je vais donner le code cadre, puis expliquer ce que je veux qu'il fasse.

XAML Markup:

<ListBox DataContext="{Binding Foos, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
         ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" 
         SelectedItem="{Binding Main.SelectedFoo, Mode=TwoWay, 
         Source={StaticResource Locator}, 
         UpdateSourceTrigger=PropertyChanged}" 
         SelectedValue="{Binding Main.SelectedFoo, Source={StaticResource Locator}}"/> 


<ListBox ItemsSource="{Binding Main.SelectedFoo.Bars}" SelectedItem="{Binding Main.SelectedBar}" > 
<ListBox.ItemTemplate> 
    <DataTemplate> 
     <Grid HorizontalAlignment="Right"> 
      <!-- The binding requires "{Binding .}" because a path must be explicitly set for Two-Way binding, 
       even though {Binding .} is supposed to be identical to {Binding} --> 
       <TextBox Text="{Binding Path=. , UpdateSourceTrigger=PropertyChanged}" /> 
     </Grid> 
    </DataTemplate> 
</ListBox.ItemTemplate> 

C# ViewModel:

private ObservableCollection<Foo> _barList = new ObservableCollection<Foo>(); 
private const string BardListPN = "FooList"; 

public ObservableCollection<Foo> FooList 
{ 
    get { return _fooList; } 

    set 
    { 
     if (_fooList == value) 
     { 
      return; 
     } 

     var oldValue = _fooList; 
     _fooList = value; 

     RaisePropertyChanged(FooListPN); 
    } 
} 

private Foo _selectedFoo; 
private const string SelectedFooPN = "SelectedFoo"; 

public Foo SelectedFoo 
{ 
    get { return _selectedFoo; } 

    set 
    { 
     if (_selectedFoo == value) 
     { 
      return; 
     } 

     var oldValue = _selectedFoo; 
     _selectedFoo = value; 

     // Update bindings, no broadcast 
     RaisePropertyChanged(SelectedFooPN); 
    } 
} 

public const string SelectedBarPN = "SelectedBar"; 
private string _selectedBar = ""; 

public string SelectedBar 
{ 
    get 
    { 
     return _selectedBar; 
    } 

    set 
    { 
     if (_selectedBar == value) 
     { 
      return; 
     } 

     var oldValue = _selectedBar; 
     _selectedBar = value; 


     // Update bindings, no broadcast 
     RaisePropertyChanged(SelectedBarPN); 
    } 
} 

C# Modèle:

public class Foo 
{ 
    public ICollection<string> Bars 
    { 
     get { return _bars; } 
     set 
     { 
      _bars= value; 
      NotifyPropertyChanged("Bars"); 
      // snipped obvious INotifyPropertyChanged boilerplate code 
     } 
    } 
} 

Mon problème est que les modifications apportées aux zones de texte pour les chaînes de la collection Bar ne sont pas définies. Lorsque les Foo sélectionnés sont remplacés par des Foo et vice versa, les Bars d'origine s'affichent.

Quelqu'un pourrait-il me dire ce que je fais mal? Cela semble être beaucoup plus simple. Merci! Mise à jour: J'ai modifié le code selon la suggestion de Tri Q, mais les modifications apportées à la zone de texte ne sont pas répercutées dans la propriété elle-même. Des idées?

Répondre

2

Votre Foo modèle classe I a été simplifié pour cet exemple, mais le code omis pourrait être le coupable de votre problème. Laisse-moi expliquer.

Foo doit également implémenter INotifyPropertyChanged pour informer la Listbox lorsque vous avez initialisé la collection Bars et cela dépend très certainement du moment où vous l'initialisez.

Supposons que vous initialisiez les barres dans le constructeur de Foo provoquera la liaison de ListSource ItemsSource à une collection Bars valide.

public Foo() 
{ 
    Bars = new ObservableCollection<string>(); 
    ... 
} 

Buut si vous avez fait quelque chose comme ça, le Listbox ne saura pas que la collection Bars a été initialisé et ne sera pas le mettre à jour sa source ...

public Foo SelectedFoo 
{ 
    get { return _selectedFoo; } 

    set 
    { 
     if (_selectedFoo == value) 
     { 
      return; 
     } 

     var oldValue = _selectedFoo; 
     _selectedFoo = value; 

     // Update bindings, no broadcast 
     RaisePropertyChanged(SelectedFooPN); 

     if(_selectedFoo.Bars == null) 
     { 
      _selectedFoo.Bars = new ObservableCollection<string>(); 
      // ... 
     } 
    } 
} 

Aussi voici quelques choses que vous pourrait vouloir réviser dans votre XAML.

Tout d'abord, la liaison de la Textbox est TwoWay par défaut, de sorte que vous n'avez pas besoin de régler la Mode ou Path.

<TextBox Text="{Binding UpdateSourceTrigger=PropertyChanged}" /> 

En second lieu, cela n'a aucun sens de mettre Mode="TwoWay" pour ItemsSource. ItemsSource = "{Binding Main.SelectedFoo.Bars , mode = TwoWay }"

Enfin, vous n'avez pas besoin de mettre la DataType pour votre DataTemplate. DataType = "{x: Type Système: Chaîne}"

+0

Superbe réponse! Merci beaucoup pour l'info supplémentaire. Oui, comme vous pouvez le voir clairement, j'apprends toujours WPF et MVVM, et j'apprécie sincèrement les commentaires. Je vais essayer votre suggestion. – llaughlin

+0

Une chose à noter: la façon dont j'ai le 'TextBox' lié, un chemin est nécessaire parce que le mode est' TwoWay' (même par défaut), et je l'ai noté dans mes commentaires XAML. Cela semble être le seul moyen d'avoir le 'TextBox' lié à l'élément actuel dans la collection. – llaughlin