Связывание ObservableCollection с WPF ListBox

У меня есть код ниже:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        ObservableCollection<int> sampleData = new ObservableCollection<int>();
        public ObservableCollection<int> SampleData
        {
            get
            {
                if (sampleData.Count <= 0)
                {
                    sampleData.Add(1);
                    sampleData.Add(2);
                    sampleData.Add(3);
                    sampleData.Add(4);
                }
                return sampleData;
            }
        }
    }

Мой xaml:

<Window x:Class="Sandbox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ListBox ItemsSource="{Binding Path=SampleData}"/>
    </Grid>
</Window>

В списке не отображаются значения в коллекции (или вообще что-либо). Может кто-нибудь указать, что моя ошибка?

Нужно ли явно устанавливать DataContext? Я думал, что если ни один из них не установлен, элемент управления будет использовать его только как DataContext.

Ответы

Ответ 1

Да, вам нужно каким-то образом установить DataContext. Он не имеет DataContext, потому что Window не имеет DataContext, если он не установлен. ListBox получит DataContext, если вы сделаете это в конструкторе.

public MainWindow() 
{ 
    InitializeComponent(); 
    this.DataContext = this;
} 

В противном случае вы можете использовать RelativeSource, ElementName и т.д. в Binding, но я думаю, вы знали, что =)

Ответ 2

Я обычно передаю viewmodel внутри конструктора и устанавливаю datacontext в переданную в viewmodel. Затем ваш ObservableCollection может быть удален из представления и помещен в viewmodel. Это отделяет ваше представление от вашей логики, а также позволяет unit test код viewmodel.

public MainWindow(SomeViewModel viewModel) 
{ 
    DataContext = viewModel;

    InitializeComponent(); 
} 

Ответ 3

Попробуйте использовать MvvM patern, поэтому в представлении вы можете определить ListBox следующим образом:

<ListBox ItemsSource="{Binding Path=Log, UpdateSourceTrigger=PropertyChanged}"/>

Тогда представление может быть без кода, связанного с источником привязки. В связанной ViewModel вы добавляете что-то вроде этого:

public class ViewModel : ViewModelBase
{
    //...
    private ObservableCollection<string> p_Log;

    /// <summary>
    /// A log of a starting process
    /// </summary>
    public ObservableCollection<string> Log
    {
        get { return p_Log; }

        set
        {
            base.RaisePropertyChangingEvent("Log");
            p_Log.Add(value.ToString());
            base.RaisePropertyChangedEvent("Log");
        }
    }
    //....
    /// <summary>
    /// Initializes this view model.
    /// </summary>
    /// <param name="mainWindowViewModel">The view model for this application main window.</param>
    private void Initialize(MainWindowViewModel mainWindowViewModel)
    {  
        //...
        p_Log = new ObservableCollection<string>();
    }

а затем события, определенные в ViewModelBase, сохраняют привязку в обновленном представлении без необходимости какого-либо кода в представлении в любое время, когда новая строка будет добавлена ​​в наблюдаемую коллекцию p_log.