2010-10-04 2 views
0

У меня возникли проблемы с получением списка для правильной привязки к коллекции.Свойство не связано правильно в ListBox DataTemplate

Я дам код рамки, а затем объясню, что я хочу, чтобы он делал.

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# Модель:

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

Моя проблема заключается в том, что любые изменения текстовых полей для строк в коллекции Bar не установлены. Когда выбранные Foo меняются на другой Foo и обратно, отображаются оригиналы Bars.

Может ли кто-нибудь сказать мне, что я делаю неправильно? Кажется, это должно быть намного проще. Благодаря!

Обновление: я изменил код в соответствии с предложением Tri Q, но изменения, внесенные в текстовое поле, не отражаются в самом свойстве. Есть идеи?

ответ

2

Ваш класс модели Foo, который я принимаю, был упрощен для этого примера, но пропущенный код может быть виновником вашей проблемы. Позволь мне объяснить.

Foo также необходимо реализовать INotifyPropertyChanged, чтобы сообщить Listbox, когда вы инициализировали коллекцию Bars, и это определенно зависит от того, когда вы ее инициализируете.

Предположим, что вы инициализируете бары в конструкторе Foo, чтобы ListSourceSource привязывался к действительной коллекции Bars.

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

Buut, если вы сделали что-то вроде этого, Listbox не будет знать, что коллекция Bars инициализирована и не будет обновлять это источник ...

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

Также здесь есть несколько вещей, которые вы может потребоваться пересмотреть в вашем XAML.

Во-первых, связывание Textbox является TwoWay по умолчанию, так что вам не нужно, чтобы установить Mode или Path.

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

Во-вторых, это не имеет смысла устанавливать Mode="TwoWay" для ItemsSource. ItemsSource = "{Binding Main.SelectedFoo.Bars , Mode = TwoWay }"

Наконец, вам не нужно устанавливать DataType для DataTemplate. DataType = "{x: Тип Система: Строка}"

+0

Удивительный ответ! Большое спасибо за дополнительную информацию. Да, как вы можете ясно видеть, я все еще изучаю WPF и MVVM, и я искренне ценю комментарии. Я попробую ваше предложение. – llaughlin

+0

Следует отметить: способ, которым я привязан к TextBox, требуется Путь, потому что режим «TwoWay» (даже по умолчанию), и я это заметил в своих комментариях XAML. Это, по-видимому, единственный способ привязать «TextBox» к текущему элементу коллекции. – llaughlin