nikotromus
nikotromus

Reputation: 1054

how to get one item source to span multiple column in a gridview

I have a single data source, and I want to display this source of data in two columns of a GridView. I add the observable collection of buttons as the item source of the parent ListBox, and all of the buttons end up in the second column. How do I get these three buttons to display in column 1, then column 2, then column 1 on the next row?

xaml:

<ListView x:Name="listBox">
     <ListView.View>
        <GridView x:Name="gridview">
             <GridViewColumn Header="col1" Width="200" />
             <GridViewColumn Header="col2" Width="200" />
        </GridView>
     </ListView.View>
</ListView>

c# code:

 private IList<Button> buttons = new ObservableCollection<Button>();

        public MainWindow() {
            InitializeComponent();

            Button b = new Button();
            b.Content = "custom Button 1 ";
            buttons.Add(b);

            b = new Button();
            b.Content = "custom button 2 ";
            buttons.Add(b);

            b = new Button();
            b.Content = "custom button 3 ";
            buttons.Add(b);

            listBox.ItemsSource = buttons;
        }

Upvotes: 0

Views: 810

Answers (2)

mm8
mm8

Reputation: 169200

  1. Create a data object (view model) that represents a row in the ListView, i.e. a type with two properties in this case:

    public class Row
    {
        public object Col1 { get; set; }
        public object Col2 { get; set; }
    }
    
  2. Transform your source collection to an IEnumerable<Row>:

    public MainWindow()
    {
        InitializeComponent();
    
        Button b = new Button();
        b.Content = "custom Button 1 ";
        buttons.Add(b);
    
        b = new Button();
        b.Content = "custom button 2 ";
        buttons.Add(b);
    
        b = new Button();
        b.Content = "custom button 3 ";
        buttons.Add(b);
    
        Row[] rows = new Row[(int)Math.Ceiling(buttons.Count / 2.0)];
        int buttonIndex = 0;
        for (int i = 0; i < rows.Length; ++i)
        {
            rows[i] = new Row();
            rows[i].Col1 = buttons[buttonIndex++];
            if (buttons.Count > buttonIndex)
                rows[i].Col2 = buttons[buttonIndex++];
        }
        listBox.ItemsSource = rows;
    }
    

    XAML:

    <ListView x:Name="listBox">
        <ListView.View>
            <GridView x:Name="gridview">
                <GridViewColumn Header="col1" Width="200">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <ContentControl Content="{Binding Col1}" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="col2" Width="200">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <ContentControl Content="{Binding Col2}" />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>
    

enter image description here

This is how an ItemsControl works, i.e. it creates a visual container (row) for each object in its ItemsSource.

Upvotes: 1

Andy
Andy

Reputation: 12276

I don't think a gridview is a great plan for this, but let's go through that first anyhow.

Rather than presenting buttons to your listview, the usual approach is to present data to your listview and let that template what you give it into a button. You could have something like this:

<Window.DataContext>
    <local:MainWIndowViewModel/>
</Window.DataContext>
<Grid>
    <ListView ItemsSource="{Binding Rows}">
        <ListView.View >
            <GridView >
                <GridViewColumn Header="Col 1" Width="100">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Button Content="{Binding Col1.Content}"
                                    Command="{Binding Col1.Command}"
                                    />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
                <GridViewColumn Header="Col 2" Width="100">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Button Content="{Binding Col2.Content}"
                                    Command="{Binding Col2.Commmand}"
                                    />
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>

Main ViewModel

class MainWIndowViewModel
{
    public ObservableCollection<RowVM> Rows { get; set; }
    = new ObservableCollection<RowVM>
    {
        new RowVM
        {
            Col1=new ButtonData{ Content="Button A" },  Col2=new ButtonData{ Content="Button B" }
        },
                    new RowVM
        {
            Col1=new ButtonData{ Content="Button C" },  Col2=new ButtonData{ Content="Button D" }
        },
                     new RowVM
        {
            Col1=new ButtonData{ Content="Button E" } 
        }
    };
}

Row and button viewmodels

public class RowVM
{
    public ButtonData Col1 { get; set; }
    public ButtonData Col2 { get; set; }
}

public class ButtonData
{
    public string Content { get; set; }
    public ICommand Command { get; set; }
}

Instead of using columns, you can make the panel that presents the data in a listbox ( or listview without any gridview ) a wrappanel and just have one collection of buttondata. https://wpf.2000things.com/2011/10/04/400-using-a-wrappanel-as-the-items-panel-for-a-listbox/

Upvotes: 0

Related Questions