1

I have a picture box and I draw a string on it by DrawString(). I change position of the string by scrolling a TrackBar. But I want to move the string by directly clicking on the string and then dragging. It'll be easier for any user. Can anybody help me achieve this?

Edit: I already move my pictureBox1 my mouse click:

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.DrawImage(img, 0, 0);
    e.Graphics.DrawString(str, font, new SolidBrush(color), new PointF(NinjaClass.NINJA.pointX, NinjaClass.NINJA.pointY));
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        x = e.X;
        y = e.Y;
    }
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        pictureBox1.Left += (e.X - x);
        pictureBox1.Top += (e.Y - y);
    }
}
A. K. M. Tariqul Islam
  • 2,824
  • 6
  • 31
  • 48
  • There is no simple mechanism to move pixels. You can move a Label easily. Check [this answer](http://stackoverflow.com/a/9387562/17034) for your next question. – Hans Passant Dec 01 '13 at 15:14

1 Answers1

2

Using DrawString is not very convenient for such a task, you have to save a Rectangle around the string, update that rectangle according to the mouse movement ... If we need to click exactly on the string curve to move the string, using DrawString can't help. In such a case we have to use a GraphicsPath which supports a little hittesting. However in this case we just allow user to click on the string bounds, because clicking on the string curve with small font or even normal font is not easy and very annoying indeed. Try the following code:

//your form constructor
public Form1(){
  InitializeComponent();
  //add string to the GraphicsPath, the string location is initialized with (10,10)
  gp.AddString("Your string goes here", Font.FontFamily, 
              (int)Font.Style, 20, new Point(10, 10), StringFormat.GenericDefault);
}
GraphicsPath gp = new GraphicsPath();
float dx, dy;
//the Paint event handler for your pictureBox1
private void pictureBox1_Paint(object sender, PaintEventArgs e) {
   e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;                
   gp.Transform(new Matrix(1, 0, 0, 1, dx, dy));//Translate and paint
   e.Graphics.FillPath(Brushes.Red, gp);
   gp.Transform(new Matrix(1,0,0,1,-dx,-dy));//translate back (reset to old location)
}
Point downPoint;
bool hitOn;
//MouseDown event handler for your pictureBox1
private void pictureBox1_MouseDown(object sender, MouseEventArgs e){
   if(e.Button == MouseButtons.Left){
       downPoint = e.Location; 
       if (gp.GetBounds(new Matrix(1,0,0,1,dx,dy)).Contains(e.Location)) {
         gp.Transform(new Matrix(1, 0, 0, 1, dx, dy));                
         hitOn = true;
       }
   }
}
//MouseMove event handler for your pictureBox1
private void pictureBox1_MouseMove(object sender, MouseEventArgs e) {
   if (e.Button == MouseButtons.Left) {
     if(hitOn){
       dx = e.X - downPoint.X;
       dy = e.Y - downPoint.Y;
       pictureBox1.Invalidate();
     } else {
       pictureBox1.Left += e.X - downPoint.X;
       pictureBox1.Top += e.Y - downPoint.Y;
     }
   }
}
//MouseUp event handler for your pictureBox1
private void pictureBox1_MouseUp(object sender, MouseEventArgs e) {
   hitOn = false;
}

Update: For using a transparent backColor Label: There is a note that when you drag and drop a label on a pictureBox at design time, the Parent of the label will be the pictureBox container not the PictureBox, that's by design, because PictureBox is not intended to contain any control. So you have to set the Parent using code, for the code moving the label, you do similarly to what you do with your PictureBox, the difference is the parent of PictureBox is your form while the parent of the label is your pictureBox:

public Form1(){
  InitializeComponent();
  label1.BackColor = Color.Transparent;
  label1.Parent = pictureBox1;
  //try this to prevent a little flicker, but looks like it does not help much
  typeof(Control).GetProperty("DoubleBuffered", System.Reflection.BindingFlags.NonPublic |
                             System.Reflection.BindingFlags.Instance)
                 .SetValue(pictureBox1, true, null);
}
Point lblDownPoint;
//MouseDown event handler for your label1
private void label1_MouseDown(object sender, MouseEventArgs e){
  if(e.Button == MouseButtons.Left) lblDownPoint = e.Location;
}
//MouseMove event handler for your label1
private void label1_MouseMove(object sender, MouseEventArgs e){
  if(e.Button == MouseButtons.Left) {
    label1.Left += e.X - lblDownPoint.X;
    label2.Top += e.Y - lblDownPoint.Y;
  }
}

However after trying using a transparent BackColor label instead, I can see that it's fairly worse (caused by flicker) than draw directly on the pictureBox as the previous code does. You should consider to choose between them yourself, the previous code seems a little complicated (but not really if you understand it).

King King
  • 61,710
  • 16
  • 105
  • 130
  • Please see my updated question. Your code conflicts with my code, I mean the string doesn't move correctly. Would you please update your code so that both picturebox and the string can be moved by mouse drag click on each? – A. K. M. Tariqul Islam Dec 01 '13 at 15:25
  • @CoolBrain remove all your code, just use my code, I hope you registered the event handlers correctly, only your pictureBox is involved here, no other control. – King King Dec 01 '13 at 15:36
  • But I need to let users move the pictubebox also. If user click (drag) on the string the string will move, if they click on the picturebox (outside of the string) the picturebox will move. I appriciate your code but the `PictureBox` should move because if the image is bigger than the window(even bigger than the screen) the full image need to be seen. – A. K. M. Tariqul Islam Dec 01 '13 at 15:57
  • @CoolBrain updated code for that requirement, you should copy and paste, it's simple. This code is interesting that it draws the string directly, however you should also consider the comment of Mr Hans, using a label with transparent backcolor in this case is really simple. – King King Dec 01 '13 at 16:01
  • Cool. I just modified your code to move the string with left key and move the picturebox with the right key. Also accepting your answer. But I expect you to kindly add another code bellow your current code which allows me displaying a transparent label with move features. – A. K. M. Tariqul Islam Dec 01 '13 at 16:23
  • 1
    @CoolBrain I added code to use a label instead, it's simpler but I've realized that it causes some flicker when you move the label, it's up to you to choose between the 2 approaches. – King King Dec 02 '13 at 05:31
  • Thank you. Ok I'll decide which one to use. – A. K. M. Tariqul Islam Dec 02 '13 at 05:41