admin管理员组

文章数量:1287882

I have a ScrollViewer inside of which there's an ItemsControl element, inside of which I want to display custom views and pass data into them using bindings.

The individual items are of DeviceListItemView type, which contains DeviceListItemViewModel ViewModel property. I seem to set ItemsSource and ItemTemplate correctly since the ScrollViewer displays the correct amount of default DeviceListItemView, but I can't seem to figure out how to pass data to DeviceListItemView.ViewModel

What am I missing?

DeviceListView.xaml:

<UserControl x:Class="Escape_Room_Hub.UI.DeviceInfo.DeviceList.DeviceListView"
             xmlns=";
             xmlns:x=";
             xmlns:mc="; 
             xmlns:d="; 
             xmlns:local="clr-namespace:Escape_Room_Hub.UI.DeviceInfo.DeviceList" xmlns:devicelistitem="clr-namespace:Escape_Room_Hub.UI.DeviceInfo.DeviceListItem"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <ScrollViewer Background="#FF000096">
            <ItemsControl Name ="items">
            </ItemsControl>
        </ScrollViewer>
    </Grid>
</UserControl>

DeviceListView.xaml.cs:

public partial class DeviceListView : UserControl
{
    DeviceListViewModel viewModel = new DeviceListViewModel();

    public DeviceListView()
    {
        InitializeComponent();

        SetupBindings();
    }

    private void SetupBindings()
    {
        var itemViewFactory = new FrameworkElementFactory(typeof(DeviceListItemView));

        DataTemplate dataTemplate = new DataTemplate();
        dataTemplate.DataType = typeof(DeviceListItemViewModel);
        dataTemplate.VisualTree = itemViewFactory;

        items.ItemsSource = viewModel.items;
        items.ItemTemplate = dataTemplate;
    }
}

DeviceListViewModel.cs:

internal class DeviceListViewModel
{
    public List<DeviceListItemViewModel> items { get; set; } = new List<DeviceListItemViewModel>();

    public DeviceListViewModel()
    {
        SetupSubscriptions();
    }

    private void SetupSubscriptions()
    {
        // updates items property
    }
}

DeviceListItemView.xaml.cs:

public partial class DeviceListItemView : UserControl
{
    public DeviceListItemViewModel ViewModel = new DeviceListItemViewModel();

    public DeviceListItemView()
    {
        InitializeComponent();
    }
}

I have a ScrollViewer inside of which there's an ItemsControl element, inside of which I want to display custom views and pass data into them using bindings.

The individual items are of DeviceListItemView type, which contains DeviceListItemViewModel ViewModel property. I seem to set ItemsSource and ItemTemplate correctly since the ScrollViewer displays the correct amount of default DeviceListItemView, but I can't seem to figure out how to pass data to DeviceListItemView.ViewModel

What am I missing?

DeviceListView.xaml:

<UserControl x:Class="Escape_Room_Hub.UI.DeviceInfo.DeviceList.DeviceListView"
             xmlns="http://schemas.microsoft/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats./markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft/expression/blend/2008" 
             xmlns:local="clr-namespace:Escape_Room_Hub.UI.DeviceInfo.DeviceList" xmlns:devicelistitem="clr-namespace:Escape_Room_Hub.UI.DeviceInfo.DeviceListItem"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <ScrollViewer Background="#FF000096">
            <ItemsControl Name ="items">
            </ItemsControl>
        </ScrollViewer>
    </Grid>
</UserControl>

DeviceListView.xaml.cs:

public partial class DeviceListView : UserControl
{
    DeviceListViewModel viewModel = new DeviceListViewModel();

    public DeviceListView()
    {
        InitializeComponent();

        SetupBindings();
    }

    private void SetupBindings()
    {
        var itemViewFactory = new FrameworkElementFactory(typeof(DeviceListItemView));

        DataTemplate dataTemplate = new DataTemplate();
        dataTemplate.DataType = typeof(DeviceListItemViewModel);
        dataTemplate.VisualTree = itemViewFactory;

        items.ItemsSource = viewModel.items;
        items.ItemTemplate = dataTemplate;
    }
}

DeviceListViewModel.cs:

internal class DeviceListViewModel
{
    public List<DeviceListItemViewModel> items { get; set; } = new List<DeviceListItemViewModel>();

    public DeviceListViewModel()
    {
        SetupSubscriptions();
    }

    private void SetupSubscriptions()
    {
        // updates items property
    }
}

DeviceListItemView.xaml.cs:

public partial class DeviceListItemView : UserControl
{
    public DeviceListItemViewModel ViewModel = new DeviceListItemViewModel();

    public DeviceListItemView()
    {
        InitializeComponent();
    }
}
Share Improve this question edited Feb 24 at 18:04 marc_s 755k184 gold badges1.4k silver badges1.5k bronze badges asked Feb 24 at 17:37 mag_zbcmag_zbc 6,99214 gold badges43 silver badges64 bronze badges 1
  • This question is similar to: WPF: ItemsControl and DataContext. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. – Emperor Eto Commented Feb 24 at 22:20
Add a comment  | 

1 Answer 1

Reset to default 1

How to pass data into a particular item of ItemsControl

This is done be means of the DataContext property.

DeviceListItemView should not create a private view model instance. Instead, if you set the ItemTemplate to a DataTemplate that contains a DeviceListItemView, the DeviceListItemView's DataContext will automatically be set to the appripriate element in the ItemsSource collection.

<ItemsControl x:Name="items">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <!-- DeviceListItemView automatically gets the correct DataContext here -->
            <local:DeviceListItemView/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

The SetupBindings method would only be this:

private void SetupBindings()
{
    items.ItemsSource = viewModel.items;
}

DeviceListView should typically also not have a private view model, but in case it is kind of a "top-level" element, it might perhaps have one. It should however declare a property and bind to it:

public partial class DeviceListView : UserControl
{
    public DeviceListViewModel ViewModel { get; } = new DeviceListViewModel();

    public DeviceListView()
    {
        InitializeComponent();
    }
}

with

<ItemsControl ItemsSource="{Binding ViewModel.items,
    RelativeSource={RelativeSource AncestorType=UserControl}}">
    ...
</ItemsControl>

In the DeviceListItemView code you could do this if somehow necessary:

public partial class DeviceListItemView : UserControl
{
    private DeviceListItemViewModel viewModel;

    public DeviceListItemView()
    {
        InitializeComponent();
        DataContextChanged += (s, e) => viewModel = (DeviceListItemViewModel)DataContext;
    }
}

本文标签: cHow to pass data into a particular item of ItemsControlStack Overflow