Search code examples
c#wpf

In WPF how to free drawing large collection of shapes in canvas without grid


I have a need to allow users to click on the canvas and create each time a rectangle and 8 textboxes to input the dimension of the rectangle. The boxes will be positioned relatively to the rectangle borders. So, there will be an array of List where each item in the list is a collection of those boxes. The canvas can let me draw a rectangle normally. The only suitable way I found to collect those dynamically is the "Grid" like so:

var g1 = new Grid();
g1.Width = 100;
g1.Height = 100;
Canvas.SetLeft(g1, 10);
Canvas.SetTop(g1, 10);

Rectangle rectangle = new Rectangle
{
    Width = 20,
    Height = 30,
    Fill = red,
    StrokeThickness = 2,
    Stroke = Brushes.Black
};
Canvas.SetLeft(rectangle, 10);
Canvas.SetTop(rectangle, 10);
    
g.Children.Add(rectangle);
LayoutCanvas.Children.Add(g);

But if I now need to add the boxes, all boxes go to the center of the grid.

var txt = new TextBox();
txt.Text = "New Text1";
txt.Width = 30;
txt.Height = 30;
Canvas.SetLeft(txt, 20);
Canvas.SetTop(txt, 30);
g1.Children.Add(txt);

To fix that positioning I would have to use:

g1.ColumnDefinitions.Add(new ColumnDefinition());
g1.RowDefinitions.Add(new RowDefinition());
g1.RowDefinitions.Add(new RowDefinition());
g1.RowDefinitions.Add(new RowDefinition());

But my app doesn't need this organization of rows and columns in a grid and need it to be fully shapes and fully dynamical, preferably without even a XAML. I tried the other objects like StackPanel but they are all not much different.

SOLUTION: I have this code that works now:

Grid DynamicGrid = new Grid();
DynamicGrid.HorizontalAlignment = HorizontalAlignment.Left;
DynamicGrid.VerticalAlignment = VerticalAlignment.Stretch;
DynamicGrid.ShowGridLines = true;
DynamicGrid.Background = new SolidColorBrush(Colors.LightSteelBlue);

var c1 = new Canvas();
var c2 = new Canvas();

//--------------------- Canvas1 Children --------------------->
Rectangle c1Rectangle = new Rectangle
{
    Width = 100,
    Height = 100,
    Fill = new SolidColorBrush(Color.FromRgb(255, 0, 0)),
    StrokeThickness = 2,
    Stroke = Brushes.Black
};
Canvas.SetLeft(c1Rectangle, 0);
Canvas.SetTop(c1Rectangle, 0);

TextBlock c1Textbox = new TextBlock();
c1Textbox.Width = 50;
c1Textbox.Height = 50;
c1Textbox.Text = "Text1";
c1Textbox.FontSize = 14;
c1Textbox.FontWeight = FontWeights.Bold;
c1Textbox.Foreground = new SolidColorBrush(Colors.White);
c1Textbox.VerticalAlignment = VerticalAlignment.Top;
Canvas.SetLeft(c1Textbox, 10);
Canvas.SetTop(c1Textbox, 10);

//--------------------- Canvas2 Children --------------------->
Rectangle c2Rectangle = new Rectangle
{
    Width = 100,
    Height = 100,
    Fill = new SolidColorBrush(Colors.LightBlue),
    StrokeThickness = 2,
    Stroke = Brushes.Red
};
Canvas.SetLeft(c2Rectangle, 420);
Canvas.SetTop(c2Rectangle, 220);

TextBlock c2Textbox = new TextBlock();
c2Textbox.Width = 50;
c2Textbox.Height = 50;
c2Textbox.Text = "Text2";
c2Textbox.FontSize = 14;
c2Textbox.FontWeight = FontWeights.Bold;
c2Textbox.Foreground = new SolidColorBrush(Colors.Black);
c2Textbox.VerticalAlignment = VerticalAlignment.Top;
Canvas.SetLeft(c2Textbox, 440);
Canvas.SetTop(c2Textbox, 240);

c1.Children.Add(c1Rectangle);
c1.Children.Add(c1Textbox);
c2.Children.Add(c2Rectangle);
c2.Children.Add(c2Textbox);

DynamicGrid.Children.Add(c1);
DynamicGrid.Children.Add(c2);

this.Content = DynamicGrid;
this.Show();

Solution

  • Try creating a canvas per object, that should allow you to position each object freely:

    Rectangle rectangle = new Rectangle
    {
        Width = 20,
        Height = 30,
        Fill = red,
        StrokeThickness = 2,
        Stroke = Brushes.Black
    };
    
    var txt = new TextBox(){
        Text = "New Text1",
        Width = 30,
        Height = 30,
    };
    
    g1 = new Grid()
        {
        Width = 100,
        Height = 100,
                Children =
                {
                    new Canvas() { Children = { txt} },
                    new Canvas() { Children = { rectangle} }
                }
        };
    
    Canvas.SetLeft(txt, 20);
    Canvas.SetTop(txt, 30);
    Canvas.SetLeft(rectangle, 10);
    Canvas.SetTop(rectangle, 10);