Question

I'm trying to populate a WrapPanel with a collection of my own user controls.

Child control:

<UserControl x:Class="MyApplication.StockMappings.StockView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid Width="140" Height="80">
        <Border CornerRadius="6" BorderBrush="Black" BorderThickness="2" >
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="0.6*"/>
                    <RowDefinition Height="0.4*"/>
                </Grid.RowDefinitions>
                <TextBlock Grid.Row="0" FontSize="18" FontWeight="Bold">
                    <Label Content="{Binding Path=ID}"/>
                </TextBlock>
                <TextBlock Grid.Row="1" FontSize="12" FontWeight="Bold">
                    <Label Content="{Binding Path=StockName}"/>
                </TextBlock>
            </Grid>
        </Border>
    </Grid>
</UserControl>

cs file:

namespace MyApplication.StockMappings
{
    /// <summary>
    /// Interaction logic for StockView.xaml
    /// </summary>
    public partial class StockView : UserControl
    {
        public StockView()
        {
            InitializeComponent();
        }

        public string ID
        {
            get;
            set;
        }

        public string StockName
        {
            get;
            set;
        }
    }
}

Finally, the window with the wrap panel:

<Window x:Class="MyApplication.StockMappings.TestWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyApplication.StockMappings"
        Title="TestWindow" Height="300" Width="300">
    <Grid>
        <ItemsControl Name="Stocks" ItemsSource="{Binding AllStocks}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal"/>                        
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <local:StockView/>                                            
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>

And the C# code again:

public partial class TestWindow : Window
{
    public TestWindow()
    {
        InitializeComponent();
        var stocks = new[]
        {
            new StockView() { ID = "M0", StockName = "abc"},
            new StockView() { ID = "M1", StockName = "def"},
        };

        Stocks.DataContext = new Test()
        {
            AllStocks = stocks.ToList()
        }; 
    }
}

The Test class (the container for child data) is very simple:

public class Test
{
    public List<StockView> AllStocks
    {
        get;
        set;
    }
}

And finally, the result:

screenshot

All borders are blank. Neither ID nor StockName are displayed.

What am I doing wrong?

I have confirmed (by adding a dummy property) that StockView controls take the ID value from the Test object, not the children from the AllStocks list. But why? Have I not defined the ItemTemplate? What is it for, then?

Était-ce utile?

La solution

What you are missing is an appropriate view model item class that holds your data. That should not be confused with the view object, e.g. your StockView.

First, remove the ID and StockName properties from the StockView control. These properties will move to the data item class.

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

Second, create the view model with a StockItem data item class, and a ViewModel class that has the AllStocks property (or just call it Test as you already did).

public class StockItem
{
    public string ID { get; set; }
    public string StockName { get; set; }
}

public class ViewModel
{
    public ObservableCollection<StockItem> AllStocks { get; set; }
}

Finally, set the DataContext of your MainWindow to an instance of the ViewModel class.

public MainWindow()
{
    InitializeComponent();

    var vm = new ViewModel { AllStocks = new ObservableCollection<StockItem>() };
    vm.AllStocks.Add(new StockItem { ID = "M0", StockName = "abc" });
    vm.AllStocks.Add(new StockItem { ID = "M1", StockName = "def" });

    DataContext = vm;
}

In case the data items could be modified after being added to the AllStocks collection, the StockItem class would have to implement the INotifyPropertyChanged interface.

Autres conseils

Well, the DataContext of the StockView usercontrol should be its own code behind class.

That should make it work!

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

set datacontent of your usercontrol for example in code:

public StockView()
        {
            InitializeComponent();
            DataContext = this;
        }

DataTemplates template data, you bind to a list of controls, the ItemTemplate will be ignored as the controls can be added directly, you should get a warning about this in you output. Because of this no DataContext is set for the UserControls, you should move the properties from the UserControl to a model class and add instances of that. Also i would advise against setting the DataContext of your UserControl.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top