Связывание 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.