SHEKHAR SHETE
SHEKHAR SHETE

Reputation: 6066

How to Move Rectangle next to Resized parent Rectangle in WPF using C#?

I am new to WPF. I am Adding Rectangle one after other on Canvas on Button Click. When i set the Height of Specific Rectangle from TextBox. It overlaps on Child Rectangle.

Eg:. When there are 3 Rectangles with Height=100,Width=200 & when i set Height of Second Rectangle to 150. then the Child Rectangle must appear after the Second Rectangle & must not Overlap on Third Rectangle. Is it Possible?

static int val=0;
List<UIElement> itemstoremove = new List<UIElement>();
private void BtnAdd_Click(object sender, RoutedEventArgs e)
        {

            int heigt = 0;
            int wegt = 0;
             if (!string.IsNullOrEmpty(txtHeight.Text) && !string.IsNullOrEmpty(txtWidth.Text))
            {
                heigt = int.Parse(txtHeight.Text);
                wegt = int.Parse(txtWidth.Text);
            }
            rect = new Rectangle();
            rect.Stroke = Brushes.Red;
            rect.StrokeThickness = 2;
            rect.Height = heigt;
            rect.Width = wegt;
            Canvas.SetLeft(rect, 10);
            Canvas.SetTop(rect, (rect.Height) * val);
            rect.Tag = val;
            canvasboard.Children.Add(rect);
            val = val + 1;
            //canvasboard is Canvas object
            foreach (UIElement ui in canvasboard.Children)
            {
                if (ui.GetType() == typeof(Rectangle))
                {
                    itemstoremove.Add(ui);
                }
            }
        }

For Modifying the Height & Width:

private void BtnModify_Click(object sender, RoutedEventArgs e)
        {
            int heigt = 0;
            int wegt = 0;
            if (!string.IsNullOrEmpty(txtHeight.Text) && !string.IsNullOrEmpty(txtWidth.Text))
            {
                heigt = int.Parse(txtHeight.Text);
                wegt = int.Parse(txtWidth.Text);
            }
            Rectangle rectToRemove;

            foreach (UIElement ui in itemstoremove)
            {
                if (ui.GetType() == typeof(Rectangle) && ((Rectangle)ui).Tag.ToString() == txtModifyRect.Text)
                {
                    rectToRemove = ui as Rectangle;
                    //itemstoremove.Remove(rectToRemove);
                    rectToRemove.Height = heigt;
                    rectToRemove.Width = wegt;
                    //canvasboard.Children.Remove(rectToRemove);
                    break;
                }
            }  
        }

This works fine. Just i want to Prevent Overlapping of Rectangle on one another and must appear one after the other with Auto Adjusting.

Help Appreciated!

Upvotes: 1

Views: 1081

Answers (4)

SHEKHAR SHETE
SHEKHAR SHETE

Reputation: 6066

Here is my solution:

<ScrollViewer VerticalScrollBarVisibility="Auto" Width="250" Height="500">
                <WrapPanel Name="wrapboard" Orientation="Vertical" ></WrapPanel>
     </ScrollViewer>

private void BtnAdd_Click(object sender, RoutedEventArgs e)
        {

            int heigt = 0;
            int wegt = 0;
             if (!string.IsNullOrEmpty(txtHeight.Text) && !string.IsNullOrEmpty(txtWidth.Text))
            {
                heigt = int.Parse(txtHeight.Text);
                wegt = int.Parse(txtWidth.Text);
            }
            rect = new Rectangle();
            rect.Stroke = Brushes.Red;
            rect.StrokeThickness = 2;
            rect.Height = heigt;
            rect.Width = wegt;
            rect.Tag = val;
            wrapboard.Children.Add(rect);
            val = val + 1;
            //wrapboard is WrapPanel object
            foreach (UIElement ui in wrapboard.Children)
            {
                if (ui.GetType() == typeof(Rectangle))
                {
                    itemstoremove.Add(ui);
                }
            }
        }

Upvotes: 0

quetzalcoatl
quetzalcoatl

Reputation: 33596

Does it need to be specifically Canvas..? Even in pure WPF/Desktop there are some layout components that can do exactly this for you, compeltely automatically.

The behaviour of "stacking" the components one after another is exactly what StackPanel class/component/control does:

<StackPanel x:Name="stacker" Orientation="Vertical">
    <Rectangle Width="30" Height="100" Background="Green" />
    <Rectangle Width="10" Height="50" Background="Blue" />
    <Rectangle Width="20" Height="80" Background="Red" Margin="5,10" />
</StackPanel>

Note how the rectangles are positioned one under another according to their different Heights. Note that I didn't have to specify Orientation=Vertical, because the StackPanel's default behaviour is to position them like that. You can switch it to Horizontally if you like, and then it will stack them according to their Widths. You can also fine-tune and add some extra spacing with i.e. Margin as in the third rectange.

If you need to do it by code, then StackPanel named stacker is perfectly suited for it: you can add new elements to it dynamically and it will lay out the added ones just like it does with children defined in XAML: one under another.

With StackPanel you get even more! The StackPanel observes its children. If at some time after placing the children you change the heights/widths of the children, then StackPanel will instantly adjust the positions so none of them overlap (if you make the children smaller, it will squeeze them, if you make children large - it will expand etc).

If you really-really want to use Canvas, then at the moment of adding-a-new-item, you will have to loop over the all existing-items stored in canvas, sum up their heights, and set the Y position of the new item to exactly that sum. That way, it will appear exactly under the last element. That's the most obvious way of doing that. However, it will not account for margins, spacings, and a few other fine details that may happen to be on your Canvas.

A better but less obvious way is to not sum up the heights again and again, but instead to position the new item according to the bottommost existing item. After all, all old items are positioned properly, right? But, what does bottommost mean? Sometimes, a tall item positioned near the top may span further downwards than a not-so-tall item sitting at the center. Therefore, the bottommost does not mean the item with maximum Y position but means the item that has a furthest Bottom, so the maximum Y+Height. So:

  • loop through existing items
  • find an item whose Size.Height+Position.Bottom is the largest
  • set the Position.Y of the new item to that value

Now, you'll get roughly the same thing as StackPanel does - but it will be only a one-time effect. If you later modify the heights of some elements, you will have to recalculate all position and adjust all positions.. (*)

EDIT: (*) I dont remember exactly, but if X/Y/Bottom properties are DependencyProperty, then you may even try to use Bindings for controlling the positions automatically. Tha might be quite fun thing to do on the Canvas. Just try binding the Y of a latter element to a Bottom of the former and it should all magically layout out them selves and even automatically update when moved/resized. Still, please try StackPanel first!

Upvotes: 1

shmuelpro
shmuelpro

Reputation: 31

In order to get the effect that you want you must change the Canvas to a WrapPanel. That way when you add the children to the canvas you don't add the location, it arranges the objects on its own. Basically you want this

      <ScrollViewer VerticalScrollBarVisibility="Auto" >
        <WrapPanel Name="objList" > </WrapPanel>
      </ScrollViewer>

The ScrollViewer will let you scroll if you have more objects than the window can contain.

Upvotes: 1

Anderung
Anderung

Reputation: 31

Sounds like you want a StackPanel for your rectangles. You can have the StackPanel nested in your Grid.

Upvotes: 0

Related Questions