Question Can a wrap panels children be aligned with each other?

glasswizzard

Well-known member
Joined
Nov 22, 2019
Messages
126
Programming Experience
Beginner
I have a wrappanel with several labels the user can enable or disable (collapse or make visible). When it wraps around, the labels on the second row aren't aligned with the labels above (because they aren't the same length).
Is there a way to get them to align with each other, so their centers are lined up?
 
I just don't see how any wrapping could come of it since it's in a grid.
It doesn't. Just to note, when I answer a post, I answer a post based on the questions asked and I only deal with specifics to that question. You asked how to approach this, and I gave you an example for that using a grid. Your code however, is using multiple grids. That's not how I was recommending you to go about this. If you read back, I suggested using one grid, and gave you a clue to using the code behind file to add your column/row definitions and then add your labels or textboxes to that grid as I have done in the Xaml. Except you would be doing this in your code behind file as I suggested somewhere above. I also said I wasn't writing this out for you as its a lot of work, and something I'd generally charge for, as its also time-consuming, but I did explain how to approach it, did I not?

Again, in your code, you are using multiple grids, which clarifies my statement above that you are lacking the experience in Xmal to achieve this yourself, but my best advice is to continue plugging away at it, and keep sharing your progress, and If I see something you've written, which I can improve, then I will, and I don't mind helping you. Nor are you being a pain in the ass either. That's what you are here for; to learn from the people who contribute to the forum, to your questions. And that is why we are here, to try and give you the best guidance possible, but what you're asking for is not something I would write example code for unless I was being paid to do so, as time is also not on my side at the moment. With that being said. Skydiver has been tweaking what I posted, and I think he has wrote some bits and bobs which you might find useful.

If you wanted to be lazy, you could use one Wrappanel with the grid and let that take care of the sizing, but my theory is for whoever tries that; is you will likely find you may then run into alignment issues with your objects residing inside the grid.
how much more code do you think it would take to get those columns to wrap if the window size is reduced enough?
That's a rather hard question to answer. There are a variety of properties across the grid, and its sub-objects which would also need to be set too for each new object you want to add to that grid. For each new objects you want to add, you will require additional column/row definitions, and then adding your new objects, so yea, I'd imagine there would be quite a lot of extra code. lol
I feel like there is some key piece of information I'm missing that is making this seem absolutely impossible
Only thing lacking is your inexperience with Xaml. There are a number of different way to approach this, you just need to experiment some more.
running the app and confirming that the behaviour you see when resizing the window can be done with a grid alone
In your code :
C#:
        <Grid Margin="0 5" Height="75" Width="100" >
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBlock VerticalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap" Text="LoremIpsum Lorem" TextDecorations="Underline" HorizontalAlignment="Center" FontSize="14" FontFamily="Georgia"/>
            <TextBlock Grid.Row="1" VerticalAlignment="Center" Text="0" HorizontalAlignment="Center" FontSize="28"/>
        </Grid>
See how you are using the <RowDefinition Height="*"/>, well you should be using the Auto property when setting the height and width for that and the ColumnDefinition's. Especially since if you don't know the width of a label/other object, the auto property will take care of this for you. You're on the right road, you just need to use one object grid and not multiple object grids. That's why we can add columns and rows. You've just moved from using multiple stack panels to using multiple grids. Go back and read post 8 again.
 
Just wanted to chime back in to clear up something, as I got a little sidetracked above.
I have my textblocks (I've been calling them labels this whole time) each set to their appropriate row/column, this is brick wall I can't get passed, they would have to move, how could they ever move to a different cell?
Why would they have to move or move to a different grid if you were using the Auto value as I have done in the example. You're not using the same structure I was. If you are needing to add more controls, then you need to add the relevant Column/RowDefinitions in your code behind file. You're not going to be able to do that, since you haven't named any of your Grid controls. In WPF, if you don't name your controls in Xaml, you simply can't access those controls in a code behind file. So how do you expect to add new Column/Row definitions to a control you can't access? Your Grids are nameless.... <Grid Margin="0 5" Height="75" Width="100" >. Read my comments above regarding using one Grid.
I understand what you've written, I just don't see how any wrapping could come of it since it's in a grid.
You would start off with a basic layout. One Grid - multiple Column/Row definitions. Add a basic number of controls for each definition. When your user uses your form, and they add controls to your form, you need to add the relevant Column/Row definitions first, then add your new controls to that grid.
It doesn't. Just to note, when I answer a post, I answer a post based on the questions asked and I only deal with specifics to that question. You asked how to approach this, and I gave you an example for that using a grid.
What I meant for the above quote is that your question has gone from "How do I do what I want in a grid instead of a wrap panel" (as per my suggestion), to "how to handle the grid when the window resizes". Your recent problem with resizing the window is a different problem, and one which should be addressed on a new topic when you reach that stage. First, stick with this topic and write the Xaml as I have shown and explained to you. Give your Grid a name, and then write your C# code in your Code behind file for adding the functionality of adding new controls to your grid. Once you reach this point, you will probably end up using a wrap panel around all of the grids items, (that is of course if you don't want to bother writing a lengthy class based on the Window Size from the Windows SizeChangedEventArgs to structure your grids elements based on the window size and how many items are in your grid.) Yes this would be avoided by using a wrap panel. That being said, I'd prefer to write an explicit class to handle the grids elements when the Window changes size. I don't think there are many programmers who would do it my way, so I am not advising that you do either, as the simpler thing at that point would be to use a wrap panel and be done with it. As an added exercise, you could always remove your wrap panel and write an explicit class to handle your Grids elements when your window size changes.

Look at my Grid layout compared to yours :
C#:
    <Grid x:Name="MyGrid" Width="Auto">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Label Margin="5,5,5,5" Content="Row AAAAA Column A" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center"></Label>
        <Label Margin="5,5,5,5" Content="Row A Column B" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"></Label>
        <Label Margin="5,5,5,5" Content="Row A Column CCCCCCC" Grid.Row="0" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"></Label>
        <Label Margin="5,5,5,5" Content="Row B Column A" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center"></Label>
        <Label Margin="5,5,5,5" Content="Row B Column B" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"></Label>
        <Label Margin="5,5,5,5" Content="Row B Column C" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"></Label>
    </Grid>
I have removed some of your Grids as they are repetitive. But the structure of yours is not the same as mine. Notice you've gone and replaced your numerous stack panels with a number of Grids. You only need one Grid, and with that grid, you need to add your Column/Row Definitions and other controls from within your C# code behind file. Compare the structures :
C#:
    <WrapPanel  Grid.Row="3"
Grid.Column="1"
Grid.ColumnSpan="3"
                    HorizontalAlignment="Center">
        <Grid Margin="0 5" Height="75" Width="100">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBlock VerticalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap" Text="LoremIpsum" TextDecorations="Underline" HorizontalAlignment="Center" FontSize="14" FontFamily="Georgia"/>
            <TextBlock Grid.Row="1" VerticalAlignment="Center" Text="0" HorizontalAlignment="Center" FontSize="28"/>
        </Grid>
        <Grid Margin="0 5" Height="75" Width="100" >
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBlock VerticalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap" Text="LoremIpsum Lorem" TextDecorations="Underline" HorizontalAlignment="Center" FontSize="14" FontFamily="Georgia"/>
            <TextBlock Grid.Row="1" VerticalAlignment="Center" Text="0" HorizontalAlignment="Center" FontSize="28"/>
        </Grid>
    </WrapPanel>
If you structure your Xaml as I have, then in your C# code behind, you can access all of your Column Definitions like so :
C#:
            foreach (ColumnDefinition c in MyGrid.ColumnDefinitions)
            {

            }
And Add additional Column/Row definitions like so (where MyGrid was the name of my Grid) :
C#:
            var CD = new ColumnDefinition();
            CD.Width = GridLength.Auto;
            MyGrid.ColumnDefinitions.Add(CD);
Then later, adding your number of additional controls which require your Columns/Rows to equate to the amount of controls you added, in which case now turn out to be text boxes.

Look back on post 4 to see how far you've already come. you started out using all the wrong controls, and now that you are using the correct controls, you just need to learn the correct order and usage of your Xaml syntax based on what I've tried to explain to you already. Hope you found this more helpful and easier to understand now that I've had more time to properly explain it to you. If you have any questions, let us know.

Edit : => Try this, as it is as close as I can get to showing you how to do what I have described. Except, instead of using values for the width and height for Column/Rows, use Auto instead.
 
Last edited:
I have a wrappanel with several labels the user can enable or disable (collapse or make visible). When it wraps around, the labels on the second row aren't aligned with the labels above (because they aren't the same length).
Is there a way to get them to align with each other, so their centers are lined up?
Yes.

So I have the following:
Screenshot_1.png


Which is laid out using the WrapPanel in the following XAML:
MainWindow.xaml:
<Window x:Class="WpfTiles.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfTiles"
        mc:Ignorable="d"
        Title="Color Swatches" Height="450" Width="810">
    <ScrollViewer>
        <ItemsControl ItemsSource="{Binding Colors}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical"
                                Margin="5">
                        <Border Height="60">
                            <TextBlock Text="{Binding Name}"
                                       FontSize="16"
                                       TextWrapping="Wrap"
                                       TextAlignment="Center"
                                       VerticalAlignment="Bottom"/>
                        </Border>
                        <Rectangle Fill="{Binding Brush}"
                                   Height="40"/>
                        <TextBlock Text="{Binding HexValue}"
                                   FontSize="10"
                                   TextAlignment="Center"/>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
</Window>

To get this layout:
Screenshot_3.png


I just had to set the ItemWidth attribute of the WrapPanel:
MainWindow.xaml:
<Window x:Class="WpfTiles.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfTiles"
        mc:Ignorable="d"
        Title="Color Swatches" Height="450" Width="810">
    <ScrollViewer>
        <ItemsControl ItemsSource="{Binding Colors}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel ItemWidth="110"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Vertical"
                                Margin="5">
                        <Border Height="60">
                            <TextBlock Text="{Binding Name}"
                                       FontSize="16"
                                       TextWrapping="Wrap"
                                       TextAlignment="Center"
                                       VerticalAlignment="Bottom"/>
                        </Border>
                        <Rectangle Fill="{Binding Brush}"
                                   Height="40"/>
                        <TextBlock Text="{Binding HexValue}"
                                   FontSize="10"
                                   TextAlignment="Center"/>
                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </ScrollViewer>
</Window>

(An alternate solution is to set the Width of the StackPanel. Use 100 for Width, instead of the 110 for ItemWidth. 110 == 100 + 5 + 5.)

Resizing the main window works out of the box with no extra code behind having to be written. Just grab the window edges and resize.

A quick not about the use of Border around the TextBlock that holds the color name. That is to force the text to be flushed bottom within the StackPanel. There is a way to redo StackPanel using a Grid and just row definitions and the existing textboxes and rectangles, but I was trying to stay focused on the creation and alignment of the tiles.

The code-behind I have is to setup the DataContext:
MainWindow.xaml.cs:
using System.Windows;

namespace WpfTiles
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = new MainWindowViewModel();
        }
    }
}

And here's the other source files related:
MainWindowViewModel.cs:
using System.Collections.ObjectModel;
using System.Linq;

namespace WpfTiles
{
    class MainWindowViewModel
    {
        public ObservableCollection<ColorInfo> Colors { get; }

        public MainWindowViewModel()
        {
            Colors = new ObservableCollection<ColorInfo>(ColorInfo.EnumerateAllColors().OrderBy(c => c.Name));
        }
    }
}

ColorInfo.cs:
using System.Collections.Generic;
using System.Text;
using System.Windows.Media;

namespace WpfTiles
{
    class ColorInfo
    {
        public string Name { get; }
        public Color Color { get; }
        public string HexValue => Color.ToString();
        public Brush Brush => new SolidColorBrush(Color);

        public ColorInfo(string name, Color color)
        {
            Name = GetTitleName(name);
            Color = color;
        }

        public override string ToString() => $"{Name}";

        string GetTitleName(string name)
        {
            var sb = new StringBuilder(name.Length + 3);
            foreach(var ch in name)
            {
                if (char.IsUpper(ch) && sb.Length > 0)
                    sb.Append(' ');
                sb.Append(ch);
            }
            return sb.ToString();
        }

        public static IEnumerable<ColorInfo> EnumerateAllColors()
        {
            foreach (var property in typeof(Colors).GetProperties())
            {
                var color = (Color) property.GetValue(null, null);
                yield return new ColorInfo(property.Name, color);
            }
        }
    }
}
 
Thanks for sticking with me guys, I will get this. I got sick of google preventing me from logging in so I made an actual account for this site.

Skydiver, thanks your example it's very useful for me to examine (I also learned a new thing or two from your C# code, I never knew about 'yield' and using methods in foreach loops!)

I'm going to take this one single step at a time, I'll redo the example code I gave in the way shown by Sheepings then take the next step from there. Attempting this has made me need to ask the question of whether I should be using labels or textblocks? Or doesn't it matter, (I did say labels for a while in this thread, I'm used to calling them that, but I was using textblocks). I got the impression that you should always use a textblock unless you specifically need the functionality of a label (is that right?), so I'm curious to know if I should be using one over the other?

My next question is also about labels vs textblocks. I took your example and the first thing I did was add another column and some more rows so it looks like this:

Annotation 2020-01-10 132530.jpg


Then I changed the labels to textblocks and to my surprise it actually resized things to look like this:

Annotation 2020-01-10 132619.jpg


I'm just curious as to why the size changed, it looks like the margin isn't being applied? Is that normal behavior? I've used margins with textblocks before and not had any issues.
 
Here it is.

C#:
<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Width="452" Height="290"  WindowStartupLocation="CenterScreen" >


    <Grid x:Name="MyGrid" Width="Auto">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        
        <TextBlock Margin="5,5,5,5" Text="Row A Column A" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row A Column B" Grid.Row="0" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row A Column C" Grid.Row="0" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row A Column D" Grid.Row="0" Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row B Column A" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row B Column B" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row B Column C" Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row B Column D" Grid.Row="1" Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row C Column A" Grid.Row="2" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row C Column B" Grid.Row="2" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row C Column C" Grid.Row="2" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row C Column D" Grid.Row="2" Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row D Column A" Grid.Row="3" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row D Column B" Grid.Row="3" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row D Column C" Grid.Row="3" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row D Column D" Grid.Row="3" Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row E Column A" Grid.Row="4" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row E Column B" Grid.Row="4" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row E Column C" Grid.Row="4" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row E Column D" Grid.Row="4" Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row F Column A" Grid.Row="5" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row F Column B" Grid.Row="5" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row F Column C" Grid.Row="5" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row F Column D" Grid.Row="5" Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row G Column A" Grid.Row="6" Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row G Column B" Grid.Row="6" Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row G Column C" Grid.Row="6" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
        <TextBlock Margin="5,5,5,5" Text="Row G Column D" Grid.Row="6" Grid.Column="3" HorizontalAlignment="Center" VerticalAlignment="Center"></TextBlock>
    </Grid>
</window>
 
Busy with other stuff right now, but this may help get the brain cells working as to the differences:

And from the always awesome Josh Smith:
 
I'm just curious as to why the size changed, it looks like the margin isn't being applied? Is that normal behavior? I've used margins with textblocks before and not had any issues.
The margin is being applied. If it wasn't I wouldn't have given it to you with margin parameters. Yes, its normal behaviour. They are not the same controls, as Skydiver's first link points out as I also hinted at previously in this topic about element fragments. Text blocks are like Grid.ColumnDefinitions in that they are fragment elements of the Grid control, hence why we can add Column/RowDefinitions in our Xaml code, because they too are just like the Textblocks. As I said here :
Grid.ColumnDefinition is a sub fragment of the Grid Control.
Same applies for these element fragments you are using. They are just elements of another control. Margin properties work on both the control you were using; and the text block element.
 
Busy with other stuff right now, but this may help get the brain cells working as to the differences:

And from the always awesome Josh Smith:

Thanks for those links, I understand the differences much more now.

Edit: Skydiver, you used a wrappanel in your example, but Sheepings is saying you should be using a grid? I don't get it :( In Sheepings example the grid definitions are set to auto, but the wrapping is not there, which is why I thought a wrap panel is the thing to use.

Why would they have to move or move to a different grid if you were using the Auto value as I have done in the example.

I'd just like to get a little more clarification on this point, as I understand it so far they (except for the first one) would have to move to another cell either way at some point, for example, using only two labels, assuming the user has collapsed the others, like so:

Annotation 2020-01-11 142406.jpg


Reducing the width of the window would cause the second label to move to another grid cell like so:

Annotation 2020-01-11 142635.jpg


"Row A Column B" is obviously the wrong text for the cell it's in but the point is that it moved within the grid. So when you ask "why would they have to move" this is what comes up in my mind, to me that is them moving to a different cell, but maybe I'm just using the wrong terminology or something?

So in the first image, the rows have been reduced to one, and the columns to two, done in the code behind (not actually of course, I'm talking theoretically here) then the resize of the window would make a new row, move the label to that new cell, then remove the second column, right? Second edit: This is what skydivers code does, but I can't see the difference between it and your example that causes it to occur, he didn't even use a grid. I thought I understood this a bit better, but it seems I'm still struggling to grasp this one small thing.
 
Last edited:
My response in post #19 was just to address your original question whether there was an easy way to get the WrapPanel to line up the items vertically. I also took advantage of the behavior of a TextBox in a Border to force the text to the bottom of the border area, and thereby force everything below it to line up horizontally as you were seeking to do with your numbers below your text. The resizing behavior just came for free.

All of that was like driving a car with automatic transmission. @Sheepings will teach us how to drive using manual transmission. We've been discussing offline, and I have a parallel set of queries as you do. He's gracious enough to share his knowledge.
 
Your recent problem with resizing the window is a different problem, and one which should be addressed on a new topic when you reach that stage. First, stick with this topic and write the Xaml as I have shown and explained to you. Give your Grid a name, and then write your C# code in your Code behind file for adding the functionality of adding new controls to your grid. Once you reach this point, you will probably end up using a wrap panel around all of the grids items, (that is of course if you don't want to bother writing a lengthy class based on the Window Size from the Windows SizeChangedEventArgs to structure your grids elements based on the window size and how many items are in your grid.) Yes this would be avoided by using a wrap panel. That being said, I'd prefer to write an explicit class to handle the grids elements when the Window changes size. I don't think there are many programmers who would do it my way, so I am not advising that you do either, as the simpler thing at that point would be to use a wrap panel and be done with it. As an added exercise, you could always remove your wrap panel and write an explicit class to handle your Grids elements when your window size changes.
You are already using the correct structure. You're using a grid, which is the correct control to use. But I am trying to get you to avoid using the wrap panel too, as you will learn much more by writing your Xaml code, but more importantly by writing your own code behind file which holds all your logic for controlling how new items are added and removed. Including what happens when you resize your window. This is all doable by writing a class, and manipulating your grids behaviour from your code behind file. Providing you name your items wrote-out in your Xaml markup, you can manipulate them just as efficiently in your code behind file.

What Skydiver has done, is used a ItemsControl, and while there is nothing wrong with his example, its not how I would have gone about it either. And the reasons for that is because it does what I didn't want to do. See here : WPF ItemsControl Example As data templates generally require devs to use some kind of "layout" panel for putting data on it.

The ItemsControl already uses a stack panel inherently. :) Anyway, myself and Skydiver did discuss this between ourselves prior to Skydiver writing out his own example for you. He thought the grid was rather complicated without using a stack or wrap panel, hence the use of his data template with the ItemsControl as a method to avoid using the Grid control.

But if you want to learn, then you should make it a challenge to write out a class and try to understand how you can construct that class to control the flow of your grids elements just as you would if you were writing it in Xaml. One of the main reasons I am advising you with this route is because you will learn more about xaml than you ever will by also allowing a simple control to do it for you.

Anyhow, after Skydiver messaged. He asked me some very genuine questions on how to actually approach some of the issues he was facing. With his permission, I am sharing some of that chat log here, since he and I also might help you to understand some of the approaches to take based on some of the questions you are likely both asking.

Please keep in mind, I am not a master in WPF, but I do work on this platform for work and for customer and personal projects, so I am familiar with finding my way around and I do know some neat tricks, but there may be a point where some of the other devs on this topic might say stop. Hold on; why not just let him use the stack and wrap panels? Well he can use them if he so wishes, but one of the main reasons I advised our OP to take this route; was to learn Xaml, and to learn how to manipulate the structure they started out with in the Xaml and to learn to do this by making a managed class for handling the different variables involved with both the Grid definitions, and the windows actual width, especially when being resized.

Here are Skydiver's questions in quotes (discussed off the board) :

Well how to determine how many column and row definitions have to be created.
By determining if a control resides in that column. When a user of your program adds more controls, you add more definitions. If your window is running out of space for new definitions, you will have no choice but to eventually add (I may stand corrected), but to add a ItemsControl which allows us to use vertical and horizontal scroll bars unless I am mistaken another control, but I'm pretty certain I am not mistaken.

Well how to determine how many column and row definitions have to be created.
By creating the amount you start off with in your Xaml, you count how many you have, keep track of this in a class and then when a user adds new controls you can add the new column/row definitions, and add the new controls or fragments to that column and again, keep track of this in a class.

Remember the main reason I wanted him to do it this way was to learn Xaml mostly, and because the grid is the correct way to do it.

If you say, that "no man, you just hardcode those definitions into the XAML", then how do you deal with more items being added, or the window being resized?
That is what a class is for. That is what looping your grid column's is for, and collecting the actualwidth from. Like :

C#:
            double t_width = default;
            foreach (ColumnDefinition cd in MyGrid.ColumnDefinitions)
            {
                t_width += cd.ActualWidth;
            }
            Console.WriteLine($"Total Width is @ { t_width }");
Essentially you end up with a total size of all column's which lets you set the overall size of the Window. At this point, I should also point out that you do not get the width of the controls or "fragment" controls in the same way as in windows. While WPF does have a width property, this is not what you want to use as this property is actually used for binding. Width is also a DependencyProperty so it does allow binding and as I said above and it must be set on the UI thread also. It is also worth mentioning that this will not give you the width unless measure is called. You can read more on that one here : UIElement.Measure(Size) Method (System.Windows)

Chicken and egg problem: MyGrid.ColumnDefinitions will start out empty since I don't know how many columns will fit it the Window.
Not really a problem. If there are none, then you start with none.
If I make it non-empty by hardcoding columns, I need to make sure that the number of columns and items will exceed the size of the Window.
E.g. Even if I define 100 columns, but there is a single item, what is the appropriate width? And conversely, if I define only 9 columns, but the user sets up 100 narrow fixed width items?
Width of what? The Definitions? The definitions would only be set to Auto. Nothing else. I already answered that above, by using a managed class to mange adding new definitions for each item needing to be added to the grid. By using the Auto property, the grids definitions will handle the size of the child controls proportionately.

I then went on to make the point :
Btw, don't be alluded to believe that i am condemning your suggestion to use the controls to simplify how this should be done. As we all know it can be done with those panels. But It's more fun and challenging to use a Grid to build up that structure and then at a later point add a managed class to control how items are added to it.

I hope I've not missed any of the questions or missed applying an answer. I'm happy to answer any questions or provide some basic sample code providing I'm not already occupied with other work/chores. Remember, I'm not smarties, and I don't have all the answers. (y)

Edit : I apologise for some of the minor typos. I can't be arsed to fix them all as I've been wring this post on and off all day and finally found time to post it.
 
Last edited:
If your window is running out of space for new definitions, you will have no choice but to eventually add (I may stand corrected), but to add a ItemsControl which allows us to use vertical and horizontal scroll bars unless I am mistaken another control, but I'm pretty certain I am not mistaken.
The easier way to do this is to put the Grid inside a ScrollViewer. The scrollbars will be inserted as needed (or always depending on what attributes you set). See my previous post #19 where ItemsControl is inside a ScrollViewer.
 
Width of what? The Definitions? The definitions would only be set to Auto. Nothing else. I already answered that above, by using a managed class to mange adding new definitions for each item needing to be added to the grid. By using the Auto property, the grids definitions will handle the size of the child controls proportionately.
The "widths" I'm referring to are the widths of the individual items. See the first screenshot on post #19. Notice that each swatch was just as wide as the length of the name of the color. The OP's goal was to present things in a tabular format where everything is evenly sized.
 
The "widths" I'm referring to are the widths of the individual items.
The width of each item or control will be set by the <ColumnDefinition Width="Auto" /> property, and I suspect and expect that any individual size set on any child controls will be or should be ignored with Width="Auto" set. What the auto property does, is; it chooses the correct size for the biggest element in its grid and the control with the most text will be the deciding factor for the total width of that column and all rows beneath it.
 
Back
Top Bottom