happy holidays!
i have a tablelayoutpanel (10x10). within each cell i have a picturebox which are disabled (enabled = false).
i am trapping mouse move over the table to catch mouse movement. here is the code:
private void tableLayoutPanelTest_MouseMove(object sender, MouseEventArgs e)
{
if (!placeShip)
{
c = tableLayoutPanelTest.GetControlFromPosition(homeLastPosition.Column, homeLastPosition.Row);
if (c.GetType() == typeof(PictureBox))
{
PictureBox hover = new PictureBox();
hover = (PictureBox)(c);
hover.Image = Properties.Resources.water;
}
Point p = tableLayoutPanelTest.PointToClient(Control.MousePosition);
Control picControl = tableLayoutPanelTest.GetChildAtPoint(p);
if (picControl != null)
{
TableLayoutPanelCellPosition me = tableLayoutPanelTest.GetCellPosition(picControl);
if (picControl.GetType() == typeof(PictureBox))
{
PictureBox thisLocation = new PictureBox();
thisLocation = (PictureBox)(picControl);
thisLocation.Image = Properties.Resources.scan;
homeLastPosition = me;
}
}
}
toolTipApp.SetToolTip(tableLayoutPanelTest, tableLayoutPanelTest.GetCellPosition(c).ToString());
}
when i run this the tooTipApp starts consuming upto 56% of the CPU. so there is something wrong.
also the picturebox image changing code stops working for some reason.
any help is very welcome!
thank you.
A few thoughts:
You're creating another PictureBox called hover - why? This code doesn't seem to do anything and it's almost certainly going to slow the loop down. I think you meant to just declare hover and cast it from c, but you're actually creating a new PictureBox instance and just throwing it away.
You're also never disposing of hover, as far as I can tell - so you end up allocating tons of memory and window handles. In general you should avoid creating new objects at all inside a MouseMove handler (small ones like hit tests are sometimes OK). As with the previous point - you probably didn't mean to write the new PictureBox().
You use PointToClient(Control.MousePosition) when the MouseMove event already gives you the control-specific mouse position (e.X and e.Y). This is costing you more time than it should.
Probably the most important, you're invoking SetToolTip on every MouseMove. You should only be invoking this when the tooltip has actually changed. You need to set a flag on which cell or control the tooltip was last displayed for, check for changes, then call SetToolTip.
You will get a lot of performance back if you avoid setting the tooltip text when it hasn't changed.
Other than that I want to echo the comment. This is a lot of processing for a mouse handler.
You should be trying to do an early test to see if the mouse is still over the same thing it was on the last move, and skipping most of the code in that case.
Related
Moving Label inside parent's MouseMove-events causes trailing redraw.
I am (mis)using System.Windows.Forms.Label objects to realize
A more flexible tooltip,
Displaying a cross hair "cursor" extending to the borders of a parent control.
To that end I create the labels as members of the parent control (btw. it's a custom graphics control with mouse wheel zoom, panning etc.), add them to the parent's Controls-list, and move them inside the MouseMove-event of the parent. In order to ensure crisp movement behavior, I even call Label.Refresh when assigning the Location property of the Labels.
void GraphViewMouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (lastLocation != e.Location)
{
HasMoved = true;
if (IsPanning && e.Button == MouseButtons.Left)
{
if (isSelecting) PerformSelection(e.Location);
else PerformMove(e.Location,lastLocation);
}
lastLocation = e.Location;
if (Width > 0 && Height > 0)
{
PointD pos = CursorPosition;
// nothing interesting, basically like what is done with the crossHair's below
coordinateToolTip.Locate(lastLocation, pos.X, pos.Y);
crossHairX.Location = new Point(lastLocation.X, 0);
crossHairY.Location = new Point(0, lastLocation.Y);
crossHairX.Refresh();
crossHairY.Refresh();
}
}
}
Everything works almost fine (the labels move parallel to the mouse cursor) except that when I move the mouse a little faster, several copies of the Label(s) remain in the client area of the parent for some time like a visual echo.
That is, whereas the redraw of the labels at their new location appears to occur immediately, it seems to take some fraction of a second until the background of the parent (some complicated graphics, the complete redraw of which takes several seconds, but is definitely not redrawn during said simple mouse move) is restored to what has been behind the moving Label(s) originally. This is of course functionally irrelevant, but it simply looks ugly because it raises the impression that the code is inefficient.
Is there a way to enforce (faster) redraw of the cached background graphics? What determines the delay before the screen content behind a control is restored?
Edit: as to the suggestion of setting DoubleBuffered=true: according to MSDN this should already be true by default. Indeed it doesn't make a difference if I set it true for the Label. On the other hand, setting it false introduces additional flickering as expected. In either case the restoration of the background remains delayed, like described above.
#Hans Passant: Thanks for telling us "When you move a control, its parent's OnPaintBackground() method needs to run to over-draw the pixels that were left behind by the control in its previous position."
For me the cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED did not work.
I am creating movable, Resizable Panel and when I moved it fast from left to right it was not displayed right "the half of the panel was often gone". I did this and now it works exactly like when I move a Windows Window.
This is in the MyPanel Class which I made and Inherits from Panel
protected override void OnMove(EventArgs e)
{
this.Parent.Invalidate();
base.OnMove(e);
}
So when I move the Movable panel with the mouse it wil Invalidate the "Parrent" in my case the Form and now it moves fine.
I'm trying to move the form instead of resizing it while I'm resizing if the right button is down.
Resize event:
if (rightMouseDown)
{
this.SetDesktopLocation(MousePosition.X - this.Width, MousePosition.Y - this.Height);
this.MaximumSize = new System.Drawing.Size(this.Width, this.Height);
this.MinimumSize = new System.Drawing.Size(this.Width, this.Height);
}
Global mouse event:
bool rightMouseDown;
private void HoldMouse(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right && e.Clicks != 1)
{
rightMouseDown = true;
}
else if (e.Button == MouseButtons.Right)
{
rightMouseDown = false;
this.MaximumSize = new System.Drawing.Size(0, 0);
this.MinimumSize = new System.Drawing.Size(100, 100);
}
}
At the moment when I click the right button it freezes because the MaximumSize is constant, therefore I can't resize the form.
e.cancel would be awesome if it would work but I can't use that.
I'm able to capture the mouse events with a global one, the form events doesn't work weirdly.
I Got it working but only when the window's width is minimum and it goes to it's original width after the right mouse button goes up.
This is due to setting it to the default size. How can I keep the window in the changed size without setting it to MaximumSize 0,0?
First of all, I would suggest that you do not move the form while the right button is down, because this is highly non-standard and therefore likely to be perceived as highly peculiar by anyone trying to use your app. To be more specific, nobody is ever going to try either moving or resizing your form with the right mouse button, so:
if that's the only way you offer for moving your form, then nobody will ever be able to move your form.
if you also offer other ways of moving your form, then why bother with offering this way too?
Secondly, I would like to propose that this is most probably an X-Y problem meaning that you probably have some other issue, which you have told us nothing about, you think that you might address it by moving-by-right-click, then you discover that moving-by-right-click does not work for you, and you come here asking how to get moving-by-right-click to work. Why don't you begin by describing the real issue?
Thirdly, if you really want to proceed with moving your form by right-click, that's not how to do it.
First, you need to detect when the right mouse button is pressed. There are events for this. They work. If they don't work for you, that's not a reason to be doing other weird things instead. The statement "I'm not able to capture the mouse click with the Mouse events" is utterly bizarre, because a) it is a wrong use of the term "capture"; mouse capture is a very specific thing, (more about it later,) so please refrain from using it in other contexts, and b) if you cannot accomplish something, then that should be the subject of a stackoverflow question on its own. You cannot be trying utterly bizarre things because your attempts to do the right things failed.
So, once you have gotten detection of the right mouse click to work, then you need to set up mouse capture. (Look it up, search for "SetCapture".) That's what guarantees that you can keep receiving mouse move events, and finally a mouse-up event, even though the mouse has moved outside of your form while you were dragging it.
I have written a C# wpf program that deals with curves on a canvas.
So I load a curve (a sequence of points in a polylinesegment) and then I operate various operations on it. Each curve is put on the screen through mouse interaction and that works fine. Each curve then comes with a textblock in its center which gives out some information.
So the problem comes when I want to mouse-move the shape.
I first select the shape with the mouse (that works) and then I stick it to the cursor through the OnMouseMove event. Eventually I put it down on the with the OnMouseLeftButtonDown event.
So in short the OnMouseLeftButtonDown always works fine except when I have to move the shape AND the label. In that case I have to press several times (randomly) to fire the event.
I have then searched the part which cause the problem and that is when I move the label.
private void UpdateLabel(int index, PathInfo piToBeAdded)
{
plotCanvas.Children.Remove(names[index]);
TextBlock text = new TextBlock();
text.TextAlignment = TextAlignment.Left;
text.FontSize = 12;
text.Inlines.Add(new Run("(" + (GetPathsIndexFromId(piToBeAdded.ID) + 1) + ")ID:" + piToBeAdded.ID + " " + piToBeAdded.Name) { FontWeight = FontWeights.Bold });
Canvas.SetLeft(text, piToBeAdded.Center.X);<-----those cause the problem
Canvas.SetTop(text, piToBeAdded.Center.Y);<------those cause the problem
text.ReleaseMouseCapture();
names[index] = text;
plotCanvas.Children.Add(text);
}
NB: pathinfo is just a class storing some information among which also coordinates
Specifically it's just the Canvas.SetLeft and Canvas.SetTop that causes the OnMouseLeftButtonDown not to fire properly. I I take them off the label goes in 0,0 and the event
But what's wrong with those instructions? How can I make the OnLeftButtonDownEvent to work properly?
I hope that I have described the problem properly I've tried to put all related information.
Thanx in advance
Patrick
Typically it is better to use the MouseButtonUp event to release mouse capture as the mouse button has definitely been released at that point and the movement has stopped (which is what you are capturing).
Your issue with Canvas.SetLeft is because it can only be called on a child of the Canvas object and you are only adding text to the Children collection after calling Canvas.SetLeft.
Edit:
In answer to your comment, the Canvas.SetLeft can only be called on an existing child of the Canvas, so call Add before calling Canvas.SetLeft.
private void UpdateLabel(int index, PathInfo piToBeAdded)
{
plotCanvas.Children.Remove(names[index]);
TextBlock text = new TextBlock();
text.TextAlignment = TextAlignment.Left;
text.FontSize = 12;
text.Inlines.Add(new Run("(" + (GetPathsIndexFromId(piToBeAdded.ID) + 1) + ")ID:" + piToBeAdded.ID + " " + piToBeAdded.Name) { FontWeight = FontWeights.Bold });
plotCanvas.Children.Add(text); // <---- moved this up
Canvas.SetLeft(text, piToBeAdded.Center.X);
Canvas.SetTop(text, piToBeAdded.Center.Y);
names[index] = text;
}
On the second part of your comment, I would suggest that you attach handlers to the different draggable items and set flags for the appropriate "mode" of operation that you are working in i.e. bool variables for ShapeDragInProgress and LabelDragInProgress. That way you can then conditionally perform the correct release capture procedure based on the ButtonUp event.
Toadflakz thanx in any case I am not sure if my solution has got to do with what you suggest. In case I will give you the flag.
I noticed that the problem is related with the fact that when moving shape+textblock the mouse point is EXACTLY on the center of the shape (I did that on purpose) and therefore EXACTLY on the textblock. Therefore when I click i don't click on the canvas but on the label. This is why the canvas is not firing. I suppose that the label is firing. So in short I just moved the label some pixels away and that did the trick!!
I am writing a silly little program (as we all do from time to time) to work on some basic C# coding. As I'm sure you can see, the mouse is clicked, the picture moves until it's at the place where the mouse was clicked. This bit works fine. I then want the picturebox to move randomly, which I have also managed! The next problem I have is that when I click, instead of moving to the new mouse coordinates, the picturebox continues to move randomly. Any help on this would be much appreciated! Here is the code I think is the problem.
Many Thanks!
John
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
int destX = e.X;
int destY = e.Y;
HasArrived = false;
while (HasArrived == false)
{
moveImage(destX, destY, pictureBox1);
if (pictureBox1.Left == destX && pictureBox1.Top == destY)
{
HasArrived = true;
while (HasArrived == true)
{
randomMove(pictureBox1);
hungry1 += 1;
}
}
}
}
You are stucked in your inner loop!
Try to put the loop in a backgroundworker,
so you can recognize the new mouseclick to set HasArrived to true.
And using the negative while statement in the positive while statemant seems very bad to me,
ive never seen that before.
I dont think thiss will work fine..
It's probably because HasArrived never gets set to false anywhere? But that will also mean you will be stuck in your Move loop as well.
EDIT: If you want to only move randomly once after you move to the position you clicked at, then remove the while around the random bit, else you'll need some other variable to know when to break out of the while.
Your inner loop seems to have a problem.
HasArrived = true;
while (HasArrived == true)
{
randomMove(pictureBox1);
hungry1 += 1;
}
Unless randomMove() can modify HasArrived, your loop never set HasArrived to false, so the while condition is always true, and you will never get out of the loop.
I looks like its because once the picture moves to its destination your program is stuck in a tight while loop from which it never escapes
First of all, you should never test equality of variables with boolean expressions. This means HasArrived == false is bad and it is better to write it like this:
while (!HasArrived)
{
// ...
while (HasArrived)
{
}
}
The problem is that your program does not give back control to the form, after the mouse has been clicked once.
Let me illustrate, what your program does as soon as the mouse has been clicked:
remember new x and y coords
while the picture has not arrived, move the image
in case the picture has arrived it's destination - move it around randomly
But it ends up moving the picture around until you kill the program, because your form does still handle the "OnMouseClicked"-event. And as long as the program does not return from your event handling method, the form is unable to record any other event (typically it should freeze, ending with "Application not responding").
So from what I think your code is supposed to do you should code your program like this:
1. Create a parallel thread to move around your image randomly
2. Pause the thread as long as your mouse-click event has not yet been handled
Also your question is wrong at all. Realtime means that your program is able to give any result in a predefined time-limit. Your method does not fit this at all, beacause the time your method takes for execution depends on how fast your image moves and which distance it should move.
I've got a Windows Form that circulates through images which are displayed on the form as a slideshow. The way I'm doing this is to have a Panel control the size of the form it resides in, and add an event handler that draws an Image object that exists in memory.
void panel_Paint(object sender, PaintEventArgs e)
{
if (_bShowImage)
{
Point leftCorner = new Point((this.Bounds.Width / 2) - (_image.Width / 2), (this.Bounds.Height / 2) - (_image.Height / 2));
e.Graphics.DrawImage(_image, leftCorner);
_bShowImage = false;
}
}
When a new Image is loaded and referenced by _image, I'm forcing the Panel to redraw:
_bShowImage = true;
_panel.Refresh();
Immediately afterwards, the image is disposed and the dereferenced from the global variable:
_image.Dispose();
_image = null;
I've seen that it works for a while, say 5 iterations, then the panel_Paint() handler is not being called. I'm using 2-3 JPG's for the display and I know they're not corrupted as they are shown fine for the first x times. I've put debug lines around the Refresh() method of the panel which execute fine. It's as if the call to the handler has been dropped. Has anyone encountered this problem before?
This is so completely backwards. Either you use a paint event handler like now. It's just fine (I say it's better than a picturebox) but then you need to drop that _bShowImage and _image.Dispose stuff. You should instead dispose the _image before you power it up with a new one. But not until that.
Or, if you absolutley must dispose the _image right after it's painted, then you should instead use Panel.CreateGraphics to get a Graphichs object you can use to immediately draw the _image and drop the event.
As it stands - it is just darn confusing. Also: .Invalidate() is what you almost always want -not .Refresh(). That's just something that got stuck in many minds since the VB6 era.
Wouldn't it be smarter to put your pictures in a picture box and loop through them in that way, so that you don't force a repaint on the whole window each time?
just a thought...
Tony