Add a scroll event to repaint form - c#

I am new to C#, and I am experimenting by placing a dot where a user clicks on a form. I am working with a 300 x 200 form with a maximum size of 640 x 480. Using the form's AutoScrollMinSize property, I set this maximum size:
this.AutoScrollMinSize = new Size(640, 480);
I am able to place dots onto the form, but the dots disappear once I have scrolled out of the visible area. Since I created the scroll area manually, I am looking for a way to add a scroll event handler that will call this.Invalidate() to repaint the form whenever scrolling occurs. Any advice is greatly appreciated.
Here is what I have:
public Form1()
{
InitializeComponent();
this.AutoScrollMinSize = new Size(640, 480);
vScrollBar vScrollBar1 = new VScrollBar();
vScrollBar1.Scroll += new ScrollEventHandler(this.vScrollBar1_Scroll);
}
And here is how I'm calling the event handler:
private void vScrollBar1_Scroll(Object sender, ScrollEventArgs e)
{
this.Invalidate();
}
I got this example from the Microsoft documentation, but the drawing is still disappearing when I scroll vertically, so I know the form is not repainting.

You don't need that VScrollBar control (which you never added to the form anyway).
Just override the OnScroll method of the form:
protected override void OnScroll(ScrollEventArgs se) {
base.OnScroll(se);
this.Invalidate();
}

try to put your event in a deligate
and use
http://msdn.microsoft.com/en-us/library/system.windows.forms.splitter.aspx
http://msdn.microsoft.com/en-us/library/system.windows.forms.scrollbar.aspx
How to set scroll bar in Windows form
and set
vScrollBar.visible = true and if it is in pannel or form set AutoScroll = true

Related

Mouse event is not called when vertical or horizontal scroller appear on Windows 10

I have a System.Windows.Forms.PictureBox inside System.Windows.Forms.Panel. The Panel has:
fixed dimensions
AutoScroll=true
event handler is subscribed to MouseWheel which is used to zoom-in or zoom-out
On zoom-in/zoom-out I change the dims of the PictureBox and if it exceeds the Panel dimenstions the vertical and/or horizontal scrolls are shown since AutoScroll=true.
Now, on Windows 7 (I have Enterprise edition), once any or both scrollers appear and I continue zooming in with the mouse wheel, the subscribed event handler to MouseWheel continue to be called and the image gets bigger.
But, on Windows 10 (I have Home edition), if any of the scrollers appear, the event handler stopps to be called and the scrollers take over. Meaning the image is scrolled up/down or left/right.
The OP has confirmed in the comments that disabling the Win10 mouse setting "Scroll inactive windows when I hover over them" resolves the issue, however I believe that this can also be handled by preventing the MouseWheel event from bubbling up to the containing Panel control. Asking users to change their preferred settings to make your code function is never a desirable situation.
The following code demonstrates preventing this event bubbling. Just create a new Winform project and replace the Form1 code with this. The code creates a TextBox, and a PictureBox contained in a Panel. The purpose of the TextBox is to just to show its loss of focus when you click on the PictureBox. For Win7, click on the PictureBox to activate it and then use the mousewheel to increase/decrease the PictureBox size.
public partial class Form1 : Form
{
PictureBox pictureBox1;
Panel panel1;
public Form1()
{
InitializeComponent();
Size = new Size(500, 500);
Controls.Add(new TextBox() { TabIndex = 0, Location = new Point(350, 5)});
panel1 = new Panel() {Size = new Size(300, 300), Location = new Point(5, 5), BorderStyle = BorderStyle.FixedSingle,Parent = this, AutoScroll = true};
pictureBox1 = new PictureBox() {Size = new Size(200, 200) , Location = new Point(5,5), BorderStyle = BorderStyle.FixedSingle, Parent = panel1};
pictureBox1.Click += pictureBox1_Click;
pictureBox1.MouseWheel += pictureBox1_MouseWheel;
panel1.MouseWheel += panel1_MouseWheel;
}
private void pictureBox1_Click(object sender, EventArgs e)
{
// On Win10 with "Scroll inactive windows when I hover over them" turned on,
// this would not be needed for pictureBox1 to receive MouseWheel events
pictureBox1.Select(); // activate the control
// this makes pictureBox1 the form's ActiveControl
// you could also use:
// this.ActiveControl = pictureBox1;
}
private void pictureBox1_MouseWheel(object sender, MouseEventArgs e)
{
Rectangle r = pictureBox1.Bounds;
int sizeStep = Math.Sign(e.Delta) * 10;
r.Inflate(sizeStep, sizeStep);
r.Location = pictureBox1.Location;
pictureBox1.Bounds = r;
// e is an instance of HandledMouseEventArgs
HandledMouseEventArgs hme = (HandledMouseEventArgs)e;
// setting to true prevents the bubbling of the event to the containing control (panel1)
hme.Handled = true;
// comment out the above line to observe panel1_MouseWheel
// being called
}
private void panel1_MouseWheel(object sender, MouseEventArgs e)
{
System.Diagnostics.Debug.Print("bubbled wheel event");
}
}
reference: HandledMouseEventArgs Class

PictureBox disappears after resize my Form

I have a MainForms.cs with Ribbon, I want to put a transparent PictureBox on the top right of the ribbon (The PictureBox represent my logo).
This is what I have tried:
I put the PictureBox on the top right of the Ribbon
I set BackColor to Transparent.
I load a PNG image (Containe transparence)
I set the parent of the image to be the ribbon (and like that the PictureBox will be transparent relative to Ribbon)
Code :
InitializeComponent();
pictureBox1.Parent = ribbon1;
Until here all is working great.
My Problem :
When I resize my Form, the PictureBox disappears.
On the OnPaint fonction i reset all setting like that :
protected override void OnPaint(PaintEventArgs pe)
{
this.Activate();
pictureBox1.Visible = true;
pictureBox1.Show();
pictureBox1.BringToFront();
}
But nothing makes the Picturebox appear. Please, can you tell me what i missed.
I downloaded the DLL that you are using and created a small test example. What I noticed is the Parent property of the PictureBox was set to null. By adding the Parent back to the Picturebox in the OnPaint event I was able to get it working if the size of the Form was growing, but would disappear when the Form size was reduced. When I put the same code in the OnResize EventHandler it works like you would expect.
public partial class Form1 : Form
{
PictureBox pictureBox1 = new PictureBox();
public Form1()
{
InitializeComponent();
pictureBox1.Image = Image.FromFile(#"C:\temp\test.jpg");
pictureBox1.Parent = ribbon1;
pictureBox1.Location = new Point(this.Width-pictureBox1.Width,10);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
if (pictureBox1.Parent == null)
{
pictureBox1.Parent = ribbon1;
pictureBox1.Visible = true;
pictureBox1.Location = new Point(this.Width - pictureBox1.Width, 10);
}
}
}

Prevent drag and drop outside of the current control (TreeNodes in a TreeView)

I'm maintaining a Windows app that has multiple forms in the one window (form1, form2, form3). I'm treating the other form2 and form3 as black boxes at the moment. In form1 I have a TreeView, and I'm implementing drag and drop functionality within that TreeView.
How can I prevent a drop outside of the form1 control?
I'm implementing 3 events handlers:
private void treeView_ItemDrag (...)
{
DoDragDrop(e.Item, DragDropEffects.Move);
}
private void treeView_DragEvent (...)
{
e.Effect = DragDropEffects.Move;
}
private void treeView_DragDrop (...)
{
//the node move logic here
}
form2 and form3 have a drag and drop relationship between them, so when I drag a node from form1 into form3 by default it allows the move (bad). I want to be able to prevent this from the form1 control code.
How can I prevent a drop outside of the form1 control? I've looked at the _DragLeave event, but I'm unsure how to control the operation without the DragEventArgs.
There is this little know property in the Cursor object that can restrict the mouse movement only to a certain rectangle.
this as a global variable for Form1
Rectangle _originalClip;
this goes in your Form1_Load event
_originalClip = Cursor.Clip;
this could be in your treeView.ItemDrag, forcing the cursor inside the form1 client area
Cursor.Clip = form1.RectangleToScreen(form1.ClientRectangle);
Now you need to restore the original clip area. A good place will be in the treeView.DragDrop. But to be on the safe side put also in your Form1_Closing event
Cursor.Clip = _originalClip;
You can check if mouse drag action is going outside the allowed area, and if so, cancel the drag action.
There's a nice sample in MSDN that uses the QueryContinueDrag event for that purpose. I think you can use that on the base of your solution.
Link: DragAction Enumeration
I know this is an old topic, but since I've never found a good answer to how to prevent dragging a control outside of a panel, I thought that I'd throw in the solution that I put together. I used some the tips from above and some work of my own.
private void Form1_Load(object sender, EventArgs e)
{
_originalClip = Cursor.Clip;
}
private void pb_MouseMove(object sender, MouseEventArgs e)
{
PictureBox pb = (PictureBox)sender;
if (e.Button == MouseButtons.Left)
{
Size sz = new Size(panel1.RectangleToScreen(panel1.ClientRectangle).Width - (pb.Width), panel1.RectangleToScreen(panel1.ClientRectangle).Height - (pb.Height));
Point loc = new Point(panel1.RectangleToScreen(panel1.ClientRectangle).X + (pb.Width / 2), panel1.RectangleToScreen(panel1.ClientRectangle).Y + (pb.Height / 2));
Rectangle rct = new Rectangle(loc, sz);
Cursor.Clip = rct;
pb.Left += (e.X - x);
pb.Top += (e.Y - y);
}
}
private void pb_MouseUp(object sender, MouseEventArgs e)
{
Cursor.Clip = _originalClip;
}
What this does is uses the Cursor.Clip method along with a Rectangle object with it's size set to be the size of the panel ("panel1" in the code) containing a bunch of Pictureboxes ("pb" in the code). The new rectangle's size is set to the parent panel minus the width and height of the Picturebox and it's location set to the location of panel1 minus half of the Picturebox's width and height. This gives you a rectangle which will constrain the Picturebox from being drug outside the panel.

C#.NET MDI bugs when programmatically hiding and showing again a maximized child form and when maximized, child form's icon cannot be changed

Basically I am having two problems with C#.NET MDI. You can download VS2010 solution which reproduces bugs here.
1) When programmatically hiding and showing again a maximized child form, it is not maximized properly again and becomes neither maximized or in normal state.
childForm = new Form();
childForm.Text = "Child Form";
childForm.MdiParent = this;
...
private void showButton_Click(object sender, EventArgs e)
{
childForm.Visible = true;
}
...
private void hideButton_Click(object sender, EventArgs e)
{
childForm.Visible = false;
}
When child form is maximized, then programicaly hidden and shown again, it becomes something like this (please notice the menu bar - child form's control box appears, but child form is not maximized):
At this stage, child form cannot be moved around. However, I found a workaround for that, simply by showing and hiding a dummy child form, which forces the actual child form to become properly maximized. But this makes MDI area to flicker. Tried Invalidate, Refresh, Update methods, but they don't help. Maybe there are other workarounds to overcome this bug and not to make MDI area flicker with dummy child form?
private void workaround1Button_Click(object sender, EventArgs e)
{
dummyForm.Visible = true;
dummyForm.Visible = false;
}
2) When child form is maximized, the icon of the child form is displayed on menu bar. However, if you have to change the icon while the child form is maximized, the icon on the menu bar is not being refreshed (see the image above). I found a workaround for that too, which basically hides and shows menu bar. Icon gets refreshed, but it makes everything below menu bar to flicker. Tried Invalidate, Refresh, Update methods, but they don't help. Is there any other way to make menu bar to refresh the child form's icon?
private void workaround2Button_Click(object sender, EventArgs e)
{
menuStrip.Visible = false;
menuStrip.Visible = true;
}
Also I noticed that when parent form is in normal window state mode (not maximized) and you change the width or height of the form by 1 pixel, child form becomes maximized as it should be and child form's icon on menu bar gets refreshed properly and you don't need other workaround I described above. If I change the size of the form programicaly, form flickers by 1 pixel and I cannot do that, when parent form is maximized. Is there any way how I could invoke the repaint/refresh functionality which is called when you resize a form and which makes child form become maximized properly and the icon on the menu bar refreshed?
There's a bug in the implementation of the internal MdiControlStrip class, the control that displays the icon and the min/max/restore glyphs in the parent window. I haven't characterized it as yet, the code isn't that easy. A classic side effect of the bug is that the glyphs get doubled up, you found some other side-effects. The fix is simple though, delay creating the child windows until after the constructor is completed. Like this:
public MainForm()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e) {
childForm = new Form();
childForm.Text = "Child Form";
childForm.MdiParent = this;
dummyForm = new Form();
dummyForm.MdiParent = this;
dummyForm.WindowState = FormWindowState.Maximized;
base.OnLoad(e);
}
Have you tired using Hide/Show instead of setting visible to true/false?
Try:
private void showButton_Click(object sender, EventArgs e)
{
childForm.Show();
}
private void hideButton_Click(object sender, EventArgs e)
{
childForm.Hide();
}
How about this workaround?
private void showButton_Click(object sender, EventArgs e)
{
childForm.Visible = true;
childForm.WindowState = (FormWindowState)childForm.Tag;
}
private void hideButton_Click(object sender, EventArgs e)
{
childForm.Visible = false;
childForm.Tag = childForm.WindowState;
childForm.WindowState = FormWindowState.Normal;
}
UPDATE
I just gave you the idea how you could do. A better solution using the same idea as above would be a new base form which saves the windows state. See below. Derive your forms from FixedForm instead of Form:
public partial class FixedForm : Form
{
private FormWindowState lastWindowState;
public FixedForm()
{
InitializeComponent();
}
protected override void OnVisibleChanged(EventArgs e)
{
base.OnVisibleChanged(e);
if (Visible)
{
WindowState = lastWindowState;
}
else
{
lastWindowState = WindowState;
WindowState = FormWindowState.Normal;
}
}
}
Found a way how to come around those bugs.
First of all you need to suspend painting for a form and its children. I found a very helpful thread here, which describes how to do it.
After suspending painting, you need call UpdateBounds method of the control and increase ClientRectangle Width or Height by one and then decrease it back to the same value it was before. This invokes layout functionality which makes everything to update/repaint. Last step is to enable painting. Not very nice solution I guess, but it works.
StopDrawing();
UpdateBounds(Location.X, Location.Y, Width, Height, ClientRectangle.Width, ClientRectangle.Height + 1);
UpdateBounds(Location.X, Location.Y, Width, Height, ClientRectangle.Width, ClientRectangle.Height - 1);
StartDrawing();
I find suspending painting very helpful not only for working around those two bugs, but also in general to make GUI work more smoothly. I guess this can help to remove any kind of flickering. However, this solution requires P/Invokes, which should be avoided in general.
Why not just manually reset required icon in menuStrip items, after the creation of the window:
menuStripMain.Items[0].Image = null;

.NET C# MouseEnter listener on a Control WITH Scrollbar

As long as the mouse is over a specific control, we show some form. When the mouse leaves the control, we hide the control after a small timeout. This is standard hover behavior.
However, when a control (for example a Treeview) has a scrollbar, and the mouse is ON or OVER the scrollbar, the events don't fire ...
If we could get a reference to the scrollbar control, this would solve our problem, as we would add the same listener events to the scrollbar. However, the scrollbar isn't accessible as far as I know ...
How can we solve this problem ?
The scrollbar is in the tree view's non-client area. When the mouse moves there, it starts generating non-client messages like WM_NCMOUSEMOVE and WM_NCMOUSELEAVE. You would have to sub-class the TreeView and override WndProc() to detect these message.
This doesn't really solve your problem though, you'll still have a hard time with edge cases. A low-tech approach with a Timer always works:
private Form frmPopup;
private void treeView1_MouseEnter(object sender, EventArgs e) {
timer1.Enabled = true;
if (frmPopup == null) {
frmPopup = new Form2();
frmPopup.StartPosition = FormStartPosition.Manual;
frmPopup.Location = PointToScreen(new Point(treeView1.Right + 20, treeView1.Top));
frmPopup.FormClosed += (o, ea) => frmPopup = null;
frmPopup.Show();
}
}
private void timer1_Tick(object sender, EventArgs e) {
Rectangle rc = treeView1.RectangleToScreen(new Rectangle(0, 0, treeView1.Width, treeView1.Height));
if (!rc.Contains(Control.MousePosition)) {
timer1.Enabled = false;
if (frmPopup != null) frmPopup.Close();
}
}
I think there are several different ways to do this, but the key is your desire to have a timeout on the action. I think a combination of two techniques might work:
Put the control on a panel, docked to fill, and use the MouseEnter of the panel to turn on your behavior -- this will include the control's scrollbar. You can use the MouseLeave event of the panel as well, but you'll have to check the cursor's position to ensure it hasn't moved into the contained control. This method is mostly reliable, but moving the mouse quickly can confuse it.
If you combine this with a timer that starts when your shown/hidden control is shown and check the cursor position periodically. This will work, but your timeout before hiding the control won't necessarily be consistent (because the timer starts when they enter the control). You could stop/start the timer on mousemoves in the control to alleviate this somewhat.
I put together a project of the different methods I tried here: http://lovethedot.s3.amazonaws.com/100609StackoverflowScrollbarQuestion.zip
By docking the control you want to track in the panel, it essentially wraps it and you'll get MouseEnter at the very edge of the tracked control:
private void panel1_MouseEnter(object sender, EventArgs e)
{
this.Text = "in";
}
private void panel1_MouseLeave(object sender, EventArgs e)
{
if (!new Rectangle(new Point(0, 0), panel1.Size).Contains(panel1.PointToClient(Control.MousePosition)))
this.Text = "out";
}
You're tracking entry into the panel surrounding the control, and exit from that panel provided the cursor isn't inside the tracked control.
To get a better "leave" experience, it's combined with a Timer that checks to see where the cursor is as well:
private void listBox3_MouseEnter(object sender, EventArgs e)
{
button1.Visible = true;
visibleTimer.Stop();
visibleTimer.Start();
}
void visibleTimer_Tick(object sender, EventArgs e)
{
if (!new Rectangle(new Point(0, 0), listBox3.Size).Contains(listBox3.PointToClient(Control.MousePosition)))
{
visibleTimer.Stop();
button1.Visible = false;
}
}

Categories