Marvin Schuchardt
Marvin Schuchardt

Reputation: 123

Binding a list of object to a WPF listviewitem

I've the following classes in a WPF project

public class part
{
    public string number { get; set; }
    public string name { get; set; }
    public List<department> departments { get; set; }
}
public class department
{
    public string name { get; set; }
    public double hours { get; set; }
}

Each part contains a list of hours for different departments. What i'm trying to achieve is to view this in a WPF listview. My problem is that i'm not finding a good example on how i could bind the a list of objects to a listviewitem. I had a similiar case in a windows forms app. There i iterated through the objects in the list and created subitems by code. While this would also possible here by creating gridviewcolumns in code i do believe that it should also be achievable via binding or am i mistaken?

Example:

public void Test()
    {
       List<part> list_parts = new List<part>();
       List<department> list_departments = new List<department>();

        department d = new department();
        d.name = "Sawing";
        d.hours = 0.3;
        list_departments.Add(d);

        d = new department();
        d.name = "Miling";
        d.hours = 12.3;
        list_departments.Add(d);

        part Test = new part();
        Test.name = "Block";
        Test.number = "123";
        Test.departments = list_departments;
        list_parts.Add(Test);

        d = new department();
        d.name = "Sawing";
        d.hours = 1.2;
        list_departments.Add(d);

        d = new department();
        d.name = "Turning";
        d.hours = 5.8;
        list_departments.Add(d);

        d = new department();
        d.name = "Finishing";
        d.hours = 5.6;
        list_departments.Add(d);

        d = new department();
        d.name = "QA";
        d.hours = 0.5;
        list_departments.Add(d);

        Test = new part();
        Test.name = "Cylinder";
        Test.number = "234";
        list_parts.Add(Test);

        lv_parts.ItemsSource = list_parts;
    }
}

My XAML of the listview without binding for the child list

   <ListView x:Name="lv_parts" ItemsSource="{Binding list_parts}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="50px"/>
                            <RowDefinition Height="50px"/>
                        </Grid.RowDefinitions>
                        <Label Grid.Row="0" Content="{Binding number}"/>
                        <Label Grid.Row="1 " Content="{Binding name}"/>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

The expected outcome would look something like this:

enter image description here

Upvotes: 0

Views: 4878

Answers (2)

Abhishek
Abhishek

Reputation: 2945

I made changes to your xaml code. You can change the stack panel to grid and add style as per your requirements

<Grid IsSharedSizeScope="True">
        <ListView x:Name="lv_parts" ItemsSource="{Binding list_parts}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" SharedSizeGroup="Parts"/>
                            <ColumnDefinition Width="*"/>
                        </Grid.ColumnDefinitions>
                        <StackPanel Orientation="Vertical" Grid.Column="0" Background="CornflowerBlue">
                            <Label Content="{Binding number}" Foreground="AliceBlue"/>
                            <Label Content="{Binding name}" Foreground="AliceBlue"/>
                        </StackPanel>
                        
                        <ListView Grid.Row="0" Grid.Column="1" Margin="0"  ItemsSource="{Binding departments}" >
                            <ListView.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel Orientation="Horizontal"/>
                                </ItemsPanelTemplate>
                            </ListView.ItemsPanel>
                            <ListView.ItemTemplate>
                                <DataTemplate>
                                    <StackPanel Orientation="Vertical" Background="Coral">
                                        <Label Content="{Binding name}" Foreground="AliceBlue"/>
                                        <Label Content="{Binding hours}" Foreground="AliceBlue"/>
                                    </StackPanel>
                                </DataTemplate>
                            </ListView.ItemTemplate>
                        </ListView>
                    </Grid>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>

The output is as below for the data that you gave:

enter image description here

Upvotes: 1

Clemens
Clemens

Reputation: 128146

Without any visual styling like e.g. background and foreground colors, your ListView should look like shown below. It uses an ItemsControl with a horizontal StackPanel to show the Departments collection.

<ListView ItemsSource="{Binding Parts}">
    <ListView.View>
        <GridView>
            <GridViewColumn>
                <GridViewColumn.Header>
                    <TextBlock>
                        <Run Text="Part Number"/>
                        <LineBreak/>
                        <Run Text="Part Name"/>
                    </TextBlock>
                </GridViewColumn.Header>
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock>
                            <Run Text="{Binding Number}"/>
                            <LineBreak/>
                            <Run Text="{Binding Name}"/>
                        </TextBlock>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn>
                <GridViewColumn.Header>
                    <TextBlock>
                        <Run Text="Departement Name"/>
                        <LineBreak/>
                        <Run Text="Hours"/>
                    </TextBlock>
                </GridViewColumn.Header>
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <ItemsControl ItemsSource="{Binding Departments}">
                            <ItemsControl.ItemsPanel>
                                <ItemsPanelTemplate>
                                    <StackPanel Orientation="Horizontal"/>
                                </ItemsPanelTemplate>
                            </ItemsControl.ItemsPanel>
                            <ItemsControl.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock>
                                        <Run Text="{Binding Name}"/>
                                        <LineBreak/>
                                        <Run Text="{Binding Hours}"/>
                                    </TextBlock>
                                </DataTemplate>
                            </ItemsControl.ItemTemplate>
                        </ItemsControl>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView>

Note that the XAML above use a view model like shown below, with proper casing of class and property names.

public class Part
{
    public string Number { get; set; }
    public string Name { get; set; }
    public List<Department> Departments { get; set; }
}

public class Department
{
    public string Name { get; set; }
    public double Hours { get; set; }
}

public class ViewModel
{
    public ObservableCollection<Part> Parts { get; }
        = new ObservableCollection<Part>();
}

An instance of the view model would be assigned to the DataContext of the view:

public MainWindow()
{
    InitializeComponent();

    var vm = new ViewModel();
    DataContext = vm;

    vm.Parts.Add(new Part
    {
        Name = "Block",
        Number = "123",
        Departments = new List<Department>
        {
            new Department { Name = "Sawing" , Hours = 0.3 },
            new Department { Name = "Milling" , Hours = 12.3 },
        }
    });

    vm.Parts.Add(new Part
    {
        Name = "Cylinder",
        Number = "456",
        Departments = new List<Department>
        {
            new Department { Name = "Sawing" , Hours = 1.2 },
            new Department { Name = "Turning" , Hours = 5.8 },
            new Department { Name = "Finishing" , Hours = 5.6 },
            new Department { Name = "QA" , Hours = 0.5 },
        }
    });
}

Upvotes: 1

Related Questions