wpfNewbie
wpfNewbie

Reputation: 11

WPF: How to dynamically create a grid with x rows and y columns with consecutive numbers

I am completely new to wpf and c#, so excuse if this is super trivial question. I am trying to create a fairly simple control.

grid

This grid will always have consecutive numbers, with a color rectangle in front of it. Clicking on the gray rectangle will change its color, and set the text to bold (I will deal with these triggers later).

For now, I just need to figure out how to create this control dynamically. When the program starts, it needs to one time create this control, and then the size won't change. I need to tell it the number of columns and rows (each column will probably always have 8 elements), and have it populate with consecutive numbers with specific font style/rectangle color.

I was experimented with creating a stackpanel UserControl for rectangle/label combo, passing the style to it, and then adding 32 of these UserControls in specific row/column in a grid. But I would need the size of that grid to be dynamic, so I need some for loop in the code I think.

Thanks!

Upvotes: 1

Views: 24132

Answers (5)

Denys Tsurkanovski
Denys Tsurkanovski

Reputation: 67

Good works for me:

     <ListView x:Name="MyListView"
              AlternationCount="2"
              ItemsSource="{Binding Entities}">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ListViewItem">
                            <Border x:Name="ItemBorder"
                                    Background="{TemplateBinding Background}"
                                    BorderBrush="WhiteSmoke"
                                    BorderThickness="1,0,1,1">
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="Auto" />
                                        <ColumnDefinition Width="10" />
                                        <ColumnDefinition Width="*" />
                                    </Grid.ColumnDefinitions>

                                    <TextBlock Text="{Binding Name}" VerticalAlignment="Center" HorizontalAlignment="Center"
                                               Width="200" TextAlignment="Center"
                                           Foreground="BlueViolet"/>

                                    <!--Columns-->
                                    <ItemsControl 
                                    Grid.Column="2"
                                    ItemsSource="{Binding PermissionColumns.Values}">
                                        <!--Definition how your item should look like-->
                                        <ItemsControl.ItemTemplate>
                                            <DataTemplate>
                                                <TextBlock Text="{Binding ResourceKey}"  Width="230"
                                                           Margin="10,0"
                                                           TextAlignment="Justify"/>
                                            </DataTemplate>
                                        </ItemsControl.ItemTemplate>

                                        <!--Definition how should look like your columns-->
                                        <ItemsControl.ItemsPanel>
                                            <ItemsPanelTemplate>
                                                <StackPanel Orientation="Horizontal" 
                                                            IsItemsHost="True"/>
                                            </ItemsPanelTemplate>
                                        </ItemsControl.ItemsPanel>
                                    </ItemsControl>
                                </Grid>
                            </Border>
                            <ControlTemplate.Triggers>
                                <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                                    <Setter Property="Background" Value="White" />
                                </Trigger>
                                <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                                    <Setter Property="Background" Value="LightGray" />
                                </Trigger>
                                <Trigger Property="IsSelected" Value="True">
                                    <Setter Property="Background" Value="LightBlue" />
                                </Trigger>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Background" Value="DeepSkyBlue" />
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListView.ItemContainerStyle>
        
        <ListView.Style>
            <Style TargetType="ListView">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="ListView">
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                </Grid.ColumnDefinitions>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="*" />
                                </Grid.RowDefinitions>

                                <!--Headers-->
                                <ItemsControl
                                    Background="Green"
                                    ItemsSource="{Binding DataContext.EntityHeaders}">
                                    <ItemsControl.ItemsPanel>
                                        <ItemsPanelTemplate>
                                            <StackPanel Orientation="Horizontal"
                                                        IsItemsHost="True"/>
                                        </ItemsPanelTemplate>
                                    </ItemsControl.ItemsPanel>
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <TextBlock Text="{Binding}" Width="230" TextAlignment="Center"
                                                       FontWeight="Bold"/>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>

                                <!--Items (** Here is items which we are defining in ItemsConainerStyle >>> above)-->
                                <ScrollViewer HorizontalScrollBarVisibility="Disabled"
                                              VerticalScrollBarVisibility="Auto"
                                              Grid.Row="1">
                                    <ItemsPresenter />
                                </ScrollViewer>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListView.Style>
    </ListView>

Upvotes: 0

Aislan Gelatti Rocha
Aislan Gelatti Rocha

Reputation: 71

The solution to me was pretty like the ones above, but I had to bind the ItemsSource in the back code.

<!-- This panel will be used to hold the items -->
<ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
        <StackPanel/>
    </ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

<!-- Each item will be drawn using this template -->
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <!--Here is the big deal I think. Desing your template according to what you need. In this case, a grid with 1 default row and column will work--> 
        <Grid>
            <!--Reading object property called "Exchange" and setting its value into a text block-->
            <TextBlock
                Margin="10, 10, 10, 40" FontSize="16" 
                HorizontalAlignment="Center"
                VerticalAlignment="Center" Foreground="GreenYellow" Text="{Binding Exchange}" />    
        </Grid>
    </DataTemplate>
</ItemsControl.ItemTemplate>

Then in the page.cs I did the binding right in the constructor. But you can bind anytime suits you

List<Bot> Bots = new()
{
    new Bot
    {
        Exchange = "Binance"
    },
    new Bot
    {
        Exchange = "Kukoin"
    }
};

ItemsControlName.ItemsSource = Bots; // I HAD to bind here. Could not make it work another way.

In my example, I have used a list of a simple class called Bot, with one single property called Exchange. Like this

public class Bot
{
   public string Exchange { get; set; }
}

Then I got it: enter image description here

Upvotes: 0

Rachel
Rachel

Reputation: 132588

I would start with an ItemsControl

You can give it a collection of items, and it will render each item however you want, displayed in any panel you want.

For example, you might have something like this

<ItemsControl ItemsSource="{Binding MyCollection}">

    <!-- This panel will be used to hold the items -->
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <UniformGrid Rows="8" Columns="8" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <!-- Each item will be drawn using this template -->
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button Text="{Binding }" Style="{StaticResource MyButtonStyle}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

The Rows and Columns property of the UniformGrid are DependencyProperties, so you could bind them to properties on the DataContext to make them dynamic.

The only problem with a UniformGrid is it only arranges items Horizontally. If you want to display them Vertically, you can either create a custom UniformGrid, or switch to a different panel such as a WrapPanel. If you are new to WPF Panels, I would recommend reading through WPF Layouts - A Quick Visual Start.

The ItemTemplate can be anything. Personally I would use a Button so you have the Click or Command behavior to handle that event, and just overwrite the Button's Template to look however you want. It is an easy task to include your Triggers in there too.

And if you wanted selection behavior, I would recommend switching from an ItemsControl to a ListBox, and overwriting that Template the same way, however it doesn't sound like you need it though, so I think an ItemsControl is better :)

Upvotes: 6

Jorgel
Jorgel

Reputation: 930

On your page you must create a "main" element, for example a Grid. Give it a name, so that we can access it by code. Here I gave it the name of root

So you will have something like

<Page
    ... >

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
          x:Name="root">

    </Grid>
</Page>

Then, on the .cs file of this page you must create a function with the code below. You can call this function on the MainPage() function.

This loop will create one Grid column with dynamic Grid rows

// Create a Grid and add it to a component of your page
Grid mainGrid = new Grid();
root.Children.Add(mainGrid); 

for (int i = 0; i < 8; i++)
{
    // I am creating a Grid with a TextBlock inside, 
    // it will have the same apperance as a Rectangle, 
    // but this way you can have a Text inside
    Grid g = new Grid();
    TextBlock tb = new TextBlock();

    tb.Text = i.ToString();
    g.Children.Add(tb);

    // Here you set the Grid properties, such as border and alignment
    // You can add other properties and events you need
    g.BorderThickness = new Thickness(1);
    g.BorderBrush = new SolidColorBrush(Colors.Black);
    g.HorizontalAlignment = HorizontalAlignment.Stretch;
    g.VerticalAlignment = VerticalAlignment.Stretch;

    // Add the newly created Grid to the outer Grid
    mainGrid.RowDefinitions.Add(new RowDefinition());
    mainGrid.Children.Add(g);

    // Set the row of the Grid. 
    Grid.SetRow(g, i);
}

I used a Grid instead of a Rectangle since Rectangles can't have Children.

It must be easy to create the other columns as well, using the same logic that I used to create the Rows.

Upvotes: 1

Michaela
Michaela

Reputation: 13

I would try using a listview and change the template to the style you want to use for your elements.

To limit the number of items in a row you can use

  <ListView.ItemsPanel>
    <ItemsPanelTemplate>
        <UniformGrid Columns="3" />
    </ItemsPanelTemplate>
  </ListView.ItemsPanel>

That way you would always get 3 elements in a row, like

123

456

To make the 3 dynamic you can databind it to some value in your codebehind / viewmodel

to dynamically create the elements within the listview you can add objects to a list/observable collection and then add those to the listview via

listviewname.ItemSource=ListName;

Or however you like. They will get arranged according to how many columns you tell the grid to have. Adding 32 items (with uniform grid of 4) leads to

1 2 3 4

5 6 7 8

9 10 11 12

...

Upvotes: 1

Related Questions