Bind ItemsControl to a collection of ViewModels on a ViewModel

时间:2016-06-10 16:11:55

标签: c# wpf xaml mvvm user-controls

This is my first time posting a question. I've simplified my code as much as possible to illustrate what I'm looking for.

I have a ViewModel (outer) that contains an ObservableCollection of another ViewModel (inner). The inner ViewModel is for a UserControl. The outer ViewModel is for MainWindow. I simply want to display one UserControl for each item in the ObservableCollection. But, I'm having trouble getting the UserControl's DataContext set to the items in the ObservableCollection.

Inner ViewModel (for UserControl):

public class InnerViewModel : ViewModelBase
{
    string _text;

    public string Text
    {
        get { return _text; }
        set { SetProperty<string>(ref _text, value); }
    }

    public InnerViewModel() { }
}

Inner ViewModel (for UserControl):

public class OuterViewModel : ViewModelBase
{
    ObservableCollection<InnerViewModel> _innerViewModels;

    public ObservableCollection<InnerViewModel> InnerViewModels
    {
        get { return _innerViewModels; }
        set { SetProperty<ObservableCollection<InnerViewModel>>(ref _innerViewModels, value); }
    }

    public OuterViewModel()
    {
        _innerViewModels = new ObservableCollection<InnerViewModel>();
    }

    public void Init()
    {
        InnerViewModels.Clear();
        InnerViewModels.Add(new InnerViewModel { Text = "Item1" });
        InnerViewModels.Add(new InnerViewModel { Text = "Item2" });
    }
}

InnerControl XAML (outermost tag removed for cleanliness)

<UserControl.DataContext>
    <local:InnerViewModel />
</UserControl.DataContext>
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="50px"></ColumnDefinition>
        <ColumnDefinition ></ColumnDefinition>
        <ColumnDefinition Width="50px"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Label Content="Header"></Label>
    <Label Grid.Column="1" Content="{Binding Text}" ></Label>
    <Label Grid.Column="2" Content="Footer"></Label>
</Grid>

MainWindow XAML

<Window.DataContext>
    <local:OuterViewModel />
</Window.DataContext>
<Grid>
    <ItemsControl ItemsSource="{Binding InnerViewModels}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <local:InnerControl></local:InnerControl> <!-- HOW DO I SET THE DATACONTEXT ON THIS??? -->
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

InnerControl.cs Code:

public partial class InnerControl : UserControl
{
    public InnerControl()
    {
        InitializeComponent();
    }
}

MainWindow.cs Code:

public partial class MainWindow : Window
{
    OuterViewModel _vm;

    public MainWindow()
    {
        InitializeComponent();

        _vm = (OuterViewModel)DataContext;
        _vm.Init();
    }
}

ViewModelBase:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
    {
        if (Equals(storage, value))
        {
            return false;
        }

        storage = value;
        this.OnPropertyChanged(propertyName);
        return true;
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler eventHandler = this.PropertyChanged;
        if (eventHandler != null)
        {
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Result: Screenshot of what I get when I run

2 个答案:

答案 0 :(得分:2)

我解决了这个问题如下:

更改了MainWindow.cs以创建外部视图模型:

public partial class MainWindow : Window
{
    OuterViewModel _vm;

    public MainWindow()
    {
        InitializeComponent();

        _vm = new OuterViewModel();
        _vm.Init();
        DataContext = _vm;
    }
}

将MainWindow更改为不设置DataContext

<!-- Don't set DataContext here -->
<Grid>
    <ItemsControl ItemsSource="{Binding InnerViewModels}">
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type local:InnerViewModel}">
                <local:InnerControl DataContext="{Binding}"></local:InnerControl>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

将InnerControl XAML更改为未设置DataContext:

<!-- Don't set DataContext here -->
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="50px"></ColumnDefinition>
        <ColumnDefinition ></ColumnDefinition>
        <ColumnDefinition Width="50px"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Label Content="Header"></Label>
    <Label Grid.Column="1" Content="{Binding Text}" ></Label>
    <Label Grid.Column="2" Content="Footer"></Label>
</Grid>

答案 1 :(得分:0)

在查看内部VM时,您可以在视图(view-first)中创建视图模型,这意味着您在DataTemplate中创建的视图具有与提供的视图模型不同的视图模型由ItemsControl

你可以这样再次覆盖(不确定属性赋值顺序):

<DataTemplate>
    <local:InnerControl DataContext="{Binding}"/>
</DataTemplate>

如评论中所述,我不会在视图中创建VM,而是使用typed DataTemplates隐式创建视图。