Свойство не связано правильно в ListBox DataTemplate - PullRequest
0 голосов
/ 05 октября 2010

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

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

XAML разметка:

<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, но изменения, внесенные в текстовое поле, не отражаются в самом свойстве.Есть идеи?

1 Ответ

2 голосов
/ 05 октября 2010

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

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

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

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

Но если вы сделали что-то подобное, 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: Тип системы: строка}"

...