0

I draw a rectangle panel with every button click. After that add a line on the edge of the rectangle and text on the center. But when I drag a panel move over other panel. The panel string will change. Please advice. I can't upload an image. How can I upload an image like that can show my problem more clearly.

This link http://i3.photobucket.com/albums/y53/ctkhai/1-4.png show the gui of my software. The upper left and lower left are picturebox. User add a "box" when click on button once and the "box" will show on upper left. User can drag the box to lower right and arrange all the box.

now the problem is when user drag the new added "box" and move over some other "box", the text i draw on previous box will change. like this http://i3.photobucket.com/albums/y53/ctkhai/2-4.png.

Update: I try to create a class for the tag. but it won't work, the number change again. It is the way I create class for the tag and read is wrong? code like below

Product _box = new Product();
List<Panel>product = new List<Panel>();

public  class Product
{        
    public  float X { set; get; }     //box coordinate
    public  float Y { set; get; }     //box coordinate
    public  int rotate { set; get; }
    public  int entryP { set; get; }
    public  int boxName { set; get; }

}

private void button_RecAdd_Click(object sender, EventArgs e)
{


   locX = pictureBox_conveyor.Left + (pictureBox_conveyor.Width / 2 - box_y / 2);
   locY = pictureBox_conveyor.Top + (pictureBox_conveyor.Height / 2 - box_x / 2);

   _box.boxName = panelBoxNo;
   _box.entryP = 1;
   _box.rotate = 0;
   _box.X = locX;
   _box.Y = locY;


   Panel box = new Panel();
   box.Location = new Point(locX, locY);
   box.Name = "box" + panelBoxNo;
   box.Tag = _box;  
   box.Size = new Size(box_y, box_x);
   box.BackColor = boxColor;
   pbW = box.Width;
   pbH = box.Height;
   box.MouseDown += panelBox_MouseDown;
   box.MouseMove += panelBox_MouseMove;               


   box.Paint += new PaintEventHandler((s, m) =>
   {

       Graphics g = m.Graphics;
       g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;]

       Product b = box.Tag as Product;

       string text = b.boxName.ToString();

       SizeF textSize = m.Graphics.MeasureString(text, Font);
       PointF locationToDraw = new PointF();
       locationToDraw.X = (pbW / 2) - (textSize.Width / 2);
       locationToDraw.Y = (pbH / 2) - (textSize.Height / 2);

       g.DrawString(text, Font, Brushes.Black, locationToDraw);
       g.DrawRectangle(new Pen(Color.Black), 0, 0, pbW - 1, pbH - 1);
       g.DrawLine(drawLine, 0, 0, 0, pbH);                      

   });

   product.Add(box);
   panel_pelletLayout.Controls.Add(box);
   box.BringToFront();
   label_boxNo.Text = panelBoxNo.ToString();
   panelBoxNo++;

}

private void panelBox_MouseDown(object sender, MouseEventArgs e)
    {
        Panel p = sender as Panel;

        if (e.Button == MouseButtons.Left)
        {
            xPos = e.X;
            yPos = e.Y;

            if (p != null)
            {
                activePnlBox = p.Name;                                     
                textBox_selectedName.Text = p.Name;                    
                textBox_selectedX.Text = p.Left.ToString();
                textBox_selectedY.Text = p.Top.ToString();

            }
        }
    }

private void panelBox_MouseMove(object sender, MouseEventArgs e)
    {
        Panel p = sender as Panel;      

        if (p != null)
        {

            if (e.Button == MouseButtons.Left)
            {

                p.Left = ((e.X + p.Left - (p.Width / 2)) / gripGap) * gripGap;                    
                p.Top = ((e.Y + p.Top - (p.Height / 2)) / gripGap) * gripGap;

                textBox_selectedX.Text = p.Left.ToString();
                textBox_selectedY.Text = p.Top.ToString();

            }        
        }
    }
John Saunders
  • 160,644
  • 26
  • 247
  • 397
TK.C
  • 13
  • 4
  • Upload the image somewhere else and share the link, I'll add it in your post. – Sriram Sakthivel Dec 24 '14 at 14:58
  • Can you post the mouseDown and mouseMove handlers too – John Dec 24 '14 at 15:15
  • Hi @TaW, But I need let user add a few box with button click then after that rearrange the box. the problem is don't know how many box user will add. – TK.C Dec 24 '14 at 15:42
  • Please change from `private Panel[] box = new Panel[30];` to `private List box = new List();` for more flexibilty! Also please edit the question to show all relevant code, eg the button click. And tell us about the two (?) pictureboxes, ok? – TaW Dec 24 '14 at 16:44
  • Never dispose an object you didn't create. Delete `m.Dispose()` – Hans Passant Dec 24 '14 at 17:09
  • I have deleted my answer since I have come to realize that I still don't understand what you are doing.. Why are you drawing on a Bitmap instead of the control with its m.Graphics object?? – TaW Dec 24 '14 at 17:26
  • I had update my question hope can explain more clearly. @Taw, I had direct draw with its m.Graphics object but still get the same result That's why I try use different Bitmap hope can freeze all drawing. This is the first time I use draw function in c#. Please advice. Thanks – TK.C Dec 25 '14 at 00:19
  • The images do help; but still, the code is incomplete at a rather important spot: Please include the button click header and its end braces, so that we can see what is part of it and what is sitting at class level! I assume the declaration of Box is outside? – TaW Dec 25 '14 at 09:30
  • `Listproduct = new List();`Why do you create a new list in the button click? Shouldn't you create the list at the program start or at some reset point? – TaW Dec 29 '14 at 19:20
  • I do create on the program start, just put the wrong place here. I update it – TK.C Dec 30 '14 at 00:02
  • I see. But now the declaration `Product _box = new Product();` is at class level? This is wrong; put it into the buttonClick. You need to put a separate _box into each Panel's Tag!! – TaW Dec 30 '14 at 01:47

3 Answers3

1

Your Paint event handler has to be responsible for drawing everything each time. Your code looks like it only draws one object.

When you drag something over your box, the box becomes invalid and needs to be painted from scratch which means it erases everything and calls your Paint handler. Your Paint event handler then just draws one object.

I suspect that what you want to do is keep a data structure of each item you draw and then have a loop in your Paint event handler that will draw all the objects you add.

Brad Rem
  • 6,036
  • 2
  • 25
  • 50
  • Hi Brad Rem, that mean I need to draw all the object when I moving the "box"? I assign the paint event to box array box[panelBoxNo].Paint += new PaintEventHandler((s, m). That mean whenever I moving a new "box" I just redraw all item inside the "box[panelBoxNo]" ? – TK.C Dec 25 '14 at 00:26
  • Or is there any method to prevent the "box" invalid when other panel over it? Thanks – TK.C Dec 25 '14 at 00:27
1

Don't use variables that you defined outside a loop, in the paint event. That might be your problem. Try to paint ((Panel)s).Name. Does this work properly?

John
  • 3,627
  • 1
  • 12
  • 13
0

Your title has got all of us confused.. You don't draw Rectangles, you create new Panels on each ButtonClick, right?

The code for the Paint event is not quite right, though. Just like in any Paint event you should use the built-in Graphics object. And as Hans has noted, you should not destroy/dispose things you didn't create.

The main problem you describe seems to be that your boxes have to paint themselves without referring to their real numbers. You should store their numbers e.g. in their Tags..

(Or you could extract it from their Names, like you do it in the MouseDown!)

box[panelBoxNo].Name = "box" + panelBoxNo;
box[panelBoxNo].Tag =  panelBoxNo;                     // < === !!
//..

box[panelBoxNo].Paint += new PaintEventHandler((s, m) =>
{
   Graphics g = m.Graphics;                            // < === !!
   g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
   string text = box[panelBoxNo].Tag.ToString();       // < ===
   SizeF textSize = g.MeasureString(text, Font);
   PointF locationToDraw = new PointF();
   locationToDraw.X = (pbW / 2) - (textSize.Width / 2);
   locationToDraw.Y = (pbH / 2) - (textSize.Height / 2);

   g.DrawString(text, Font, Brushes.Black, locationToDraw);
   g.DrawRectangle(new Pen(Color.Black), 0, 0, pbW - 1, pbH - 1);
   g.DrawLine(drawLine, 0, 0, 0, pbH);
   //  g.Dispose();                       // < === !!

  //  m.Graphics.DrawImageUnscaled(drawBox, new Point(0, 0));         // < === !!
  // m.Dispose();                       // < === !!
});

And, as I have noted you should only use arrays if you (or the code) really knows the number of elements. In your case a List<Panel> will be flexible to hold any number of elements without changing any other part of the code, except the adding. You can access the List just like an Array. (Which it is behind the scenes..)

Update: The way I see it now, your problems all are about scope in one way or another.

Scope in its most direct meaning is the part of your code where a variable is known and accessible. In a slightly broader meaning it is also about the time when it has the value your need.

Your original problem was of the latter kind: You accessed the partNo in thr Paint event of the Panels, when it had long changed to a new, probably higher value.

Your current problem is to understand the scope of the variables in the ButtonClick event.

Usually this is no problem; looking at the pairs of braces, the scope is obvious. But: Here we have a dynamically created Lambda event and this is completely out of the scope of the Click event!! Behind the scenes this Paint event is removed from the Click code, placed in a new event and replaced by a line that simply adds a delegate to the Paint handler just like any regular event.

So: nothing you declare in the ButtonClick is known in the Paint code!

To access these data you must place them in the Panel properties, in our case in the Tag and access them via casting from the Sender parameter s!

So you need to change this line in the Paint event

Product b = box.Tag as Product;

to something like this:

Product b = ( (Panel) s ).Tag as Product;
TaW
  • 53,122
  • 8
  • 69
  • 111
  • Hi @TaW, Yes I change the array to List already thanks a lot. The number of box only one of the issue. I'm going to rotate the box as well so the red line of the box got the same problem also. Will try TAGs later. Thanks – TK.C Dec 25 '14 at 23:22
  • Thanks @Taw, the Tags you said is work. Now the red line of the box has the same issue when I rotate it. http://i3.photobucket.com/albums/y53/ctkhai/3-1.png, http://i3.photobucket.com/albums/y53/ctkhai/4.png. Please advice – TK.C Dec 26 '14 at 00:39
  • And about the Class "Product" it is anyway to make the class become array instead of declared all class member to array? Thanks a lot. – TK.C Dec 26 '14 at 00:40
  • If your problem is resolved: Good! If you need to put more info into the Tag you can use a structure or even a class instead of a string and put it into the Tag! - How will you rotate the Panel? they can't be rotated except by 90°. For other angles: you can only draw rotated things on the surface; if it is big enough. you have to use a tranparent apenel subclass. This is doable but not very easy and enough stuff for another question, once you get stuck with he code.. – TaW Dec 26 '14 at 02:51
  • I don't understand you last question. - You can acces the List members like an array with indices as you do now. And you can use Product.ToArray() if you need to pass it somewhere as an array. – TaW Dec 26 '14 at 02:52
  • I just simply swipe the height and width to act like panel rotate. Also draw a red line on the edge of panel to indicate the facing direction of the box. Now the problem is when box2 red line on top, box3 red line on right. when I drag box3 move over box2, the red line on top of box2 will disappear and display a new red line on right. I update my question to show the rotate code. – TK.C Dec 26 '14 at 05:21
  • For the last question i mean can i change `public static class Product` to `public static class[] Product`? – TK.C Dec 26 '14 at 05:22
  • No. a class is a class. It can contain members which are arrays or lists and you can have arrays or lists of class instances but the class itself is only a recipe for creating one such instance. - Did you create the structure (or class) for the Tag of the boxes? Instead you may create a panel subclass and add memebers, like direction, to it as needed.. – TaW Dec 26 '14 at 08:49
  • Hi @TaW,, i try create a class for tag. But it seem not work. the code I update below. Please advice. Thank you. – TK.C Dec 28 '14 at 13:39
  • Close but not quite! You have to move the casting line __into the Paint event!__ I have marked the line you need to move.. – TaW Dec 28 '14 at 16:32
  • Thanks @TaW. But after change the code still can't. Is it anything I miss? – TK.C Dec 29 '14 at 14:00
  • Well, what is happening? Compiler error? If so which? Wrong drawing? If so how? – TaW Dec 29 '14 at 16:29
  • Sorry, is the string still change when other panel move over. – TK.C Dec 30 '14 at 00:02
  • See my update about scope! (And my other comment about moving the declaration of _box intot the click event!) – TaW Dec 30 '14 at 07:22
  • Hi @TaW, I put`Product _box = new Product();` to buttonclick it work!! Very appreciate your help. :) – TK.C Dec 30 '14 at 13:58
  • I use this `Product b = box.Tag as Product;` or `Product b = ( (Panel) s ).Tag as Product;` both work when I put the declaration `Product _box = new Product();` to buttonclick – TK.C Dec 30 '14 at 13:59
  • And thank for your explanation. Make me more understand my problem. – TK.C Dec 30 '14 at 14:04