wpf using grid as itemshost stacking multiple item

2019-07-18 07:21发布

I am binding an items control to a datasource and using a grid as my itemshost. I wish to have the items locate themselves into the correct cell in the grid (I can do this), and also stack themselves so they are not all on top of each other (I can't figure out how to insert the items into a stackpanel or other panel in the grid).

here is the .cs file for the two classes:

   public class listofdata
    {
        public List<data> stuff { get; set; }
        public listofdata()
        {
            stuff = new List<data>();
            stuff.Add(new data(0, 0, "zeroa"));
            stuff.Add(new data(0, 0, "zerob"));
            stuff.Add(new data(1, 0, "onea"));
            stuff.Add(new data(1, 0, "oneb"));
            stuff.Add(new data(1, 1, "twoa"));
            stuff.Add(new data(1, 1, "twob"));
        }
    }

    public class data
    {
        public int x { set; get; }
        public int y { set; get; }
        public string text { get; set; }
        public data(int x, int y, string text)
        {
            this.x = x;
            this.y = y;
            this.text = text;
        }
    }
}

Here is my XAML

   <Window x:Class="GridTester.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    xmlns:src="clr-namespace:GridTester"
    xmlns:sys="clr-namespace:System;assembly=mscorlib"

    Title="MainWindow" >
<Window.Resources>
    <DataTemplate DataType="{x:Type src:data}">
        <Button Content="{Binding text}"/>
    </DataTemplate>
    <src:listofdata x:Key="MyDataSource"> </src:listofdata>


</Window.Resources>
<ListBox Name="Main" ItemsSource="{Binding Source={StaticResource MyDataSource},Path=stuff}">
    <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
            <Grid Name="MyGrid">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>

                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </ListBox.ItemsPanel>

    <ListBox.ItemContainerStyle>
        <Style>
            <Setter Property="Grid.Column" Value="{Binding x}"/>
            <Setter Property="Grid.Row" Value="{Binding y}"/>
        </Style>
    </ListBox.ItemContainerStyle> 

</ListBox>


</Window>

My problem is that all the buttons ending with 'a' are under the buttons ending in b. I can't see how to use XAML to insert the items into a dynamically created stackpanel

I tried to create a class derived from Grid, thinking to intercept the addition of the children to add stackpanels myself and then move the children from the grid to the stackpanels, but attempting to manipulate the children in an itemshost causes an exception to be thrown.

Ultimately I just want the items in my datasource to be able to bind to a 'cell' in the grid, and if multiple items bind to the same cell, I want them to stack.

2条回答
霸刀☆藐视天下
2楼-- · 2019-07-18 07:42

You could do this at the data level like HighCore suggested, but since the current data structure already contains the necessary information, it should be possible for the ItemsControl to handle it. Consider adding a group description to the ListBox's item collection, and use a GroupStyle whose Panel is a StackPanel.

查看更多
beautiful°
3楼-- · 2019-07-18 07:54

Here is the solution using the hints from nmclean (Thanks so much) This section establishes the grouping which will be used to distribute the elements around the grid.

<CollectionViewSource Source="{Binding Source={StaticResource MyDataSource}}" x:Key="cvs">


        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="ordinal"/>


        </CollectionViewSource.GroupDescriptions>

    </CollectionViewSource>

this section is the main listbox bound to the data in the collectionviewsource, The containerstyle contains the bindings to put the groupitem into the correct cells in the grid. The grid is in the groupstyle.panel

  <ListBox  ItemsSource ="{Binding Source={StaticResource cvs}}"   >
    <ListBox.GroupStyle>
        <GroupStyle>
            <GroupStyle.ContainerStyle>
                <Style TargetType="{x:Type GroupItem}">
                    <Setter Property="Grid.Row" Value="{Binding Items[0].y,diag:PresentationTraceSources.TraceLevel=High}" />
                    <Setter Property="Grid.Column" Value="{Binding Items[0].x,diag:PresentationTraceSources.TraceLevel=High}" />


                </Style>
            </GroupStyle.ContainerStyle>
            <GroupStyle.Panel>
                <ItemsPanelTemplate>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition></RowDefinition>
                            <RowDefinition></RowDefinition>
                            <RowDefinition></RowDefinition>
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition></ColumnDefinition>
                            <ColumnDefinition></ColumnDefinition>
                            <ColumnDefinition></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                    </Grid>
                </ItemsPanelTemplate>
            </GroupStyle.Panel>
        </GroupStyle>
    </ListBox.GroupStyle>

Here is the total solution in case you need it:

<Window x:Class="GridTester.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
        xmlns:src="clr-namespace:GridTester"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"

        Title="Window1" Height="300" Width="300" Name="TOPWindow">
    <Window.Resources>

        <DataTemplate DataType="{x:Type src:data}">
            <Button Content="{Binding text}"/>
        </DataTemplate>
        <src:listofdata x:Key="MyDataSource"></src:listofdata>
        <CollectionViewSource Source="{Binding Source={StaticResource MyDataSource}}" x:Key="cvs">


            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="ordinal"/>


            </CollectionViewSource.GroupDescriptions>

        </CollectionViewSource>


    </Window.Resources>



    <ListBox  ItemsSource ="{Binding Source={StaticResource cvs}}"   >
        <ListBox.GroupStyle>
            <GroupStyle>
                <GroupStyle.ContainerStyle>
                    <Style TargetType="{x:Type GroupItem}">
                        <Setter Property="Grid.Row" Value="{Binding Items[0].y,diag:PresentationTraceSources.TraceLevel=High}" />
                        <Setter Property="Grid.Column" Value="{Binding Items[0].x,diag:PresentationTraceSources.TraceLevel=High}" />


                    </Style>
                </GroupStyle.ContainerStyle>
                <GroupStyle.Panel>
                    <ItemsPanelTemplate>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition></RowDefinition>
                                <RowDefinition></RowDefinition>
                                <RowDefinition></RowDefinition>
                            </Grid.RowDefinitions>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition></ColumnDefinition>
                                <ColumnDefinition></ColumnDefinition>
                                <ColumnDefinition></ColumnDefinition>
                            </Grid.ColumnDefinitions>
                        </Grid>
                    </ItemsPanelTemplate>
                </GroupStyle.Panel>
            </GroupStyle>
        </ListBox.GroupStyle>


    </ListBox>



</Window>

The code file looks like this now:

  public class listofdata : List<data>
{

    public listofdata()
    {

        Add(new data(0, 0, "zeroa"));
        Add(new data(0, 0, "zerob"));
        Add(new data(1, 0, "onea"));
        Add(new data(1, 0, "oneb"));
        Add(new data(1, 1, "twoa"));
        Add(new data(1, 1, "twob"));


    }

}

public class data
{
    public int x { set; get; }
    public int y { set; get; }
    public int ordinal { get { return x * 1000 + y; } }
    public string text { get; set; }
    public data(int x, int y, string text)
    {
        this.x = x;
        this.y = y;
        this.text = text;
    }

}
查看更多
登录 后发表回答