Scrollbar handling scroll event in winforms - c#

I am creating a form which looks like this.
Form1 has two panels:
the bottom panel (yellow)
the top panel, which is himself composed by two panels:
the left panel (red)
the right panel (green)
As the image shows, the top panel, exceeds form1's height, so I would like to use a scrollbar. I have several problems:
a) Setting auto-scroll to true in the top panel does not work.
b) If I put a button on the top panel whose location exceeds form1's height, then it scrolls, but NOT if I put it in any of its subpanels (red or green)
c) Instead of setting auto-scroll to true in the top panel, I could create my own scrollbar, but I just couldn't find a simple example handling the scroll event that moves the panel so that the view port changes as desired.
How could I properly add a scrollbar with the desired behaviour?

As LarsTech and Hans Passant suggested, AutoScrollMinSize worked, so a and b questions were solved.
Regarding question C, I thought there was a way to create a scrollbar so that it behaves like I guess 99% of scrollbars should behave (just to move a desired panel). Finally, I coded that logic myself.
I added a Scrollbar1 as a control of the basic form. initialPanelY is catched on the constructor.
This is the code that responds to the scroll event. I just move the panel panelRest2's Y location depending on the scrollbar value
using System;
.
.
using System.Windows.Forms;
namespace WindowsFormMyTests
{
public partial class Form1 : Form
{
int initialPanelY;
public Form1()
{
InitializeComponent();
initialPanelY = this.panelRest2.Location.Y;
}
private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
{
int v = (sender as VScrollBar).Value; //between 1 and 100
int hiddenPanelHeight = this.panelRest2.Size.Height - this.Size.Height;
float moveY = (float)hiddenPanelHeight * ((float)v / 100);
int newY = initialPanelY - (int)moveY;
this.panelRest2.Location = new Point(this.panelRest2.Location.X, newY);
}
}
}

Related

Dynamically resize WinForms controls when Form is resized

I have a WinForms project on which I would like all of the controls to grow proportionally along with the form as the form is resized. This is what the form looks like in normal state: Normal State Form
I have tried setting the Anchor properties to their appropriate values given the location of each control on the form, and while it does move the controls, they remain the same size. I tried using the AutoSize property, but also to no avail. Here is what the form looks like after being maximized with the Anchor properties set: Maximized Form
I also tried using a formula from Shaun Halverson to dynamically resize everything but it does not relocate the control properly, and I can't seem to figure out why. Here is the code I used to try and resize dynamically:
private void Main_Load(object sender, EventArgs e)
{
originalFormSize = new Rectangle(this.Location.X, this.Location.Y, this.Size.Width, this.Size.Height);
submitBtnOriginal = new Rectangle(submitButton.Location.X, submitButton.Location.Y, submitButton.Width, submitButton.Height);
}
private void Main_Resize(object sender, EventArgs e)
{
resizeControl(submitBtnOriginal, submitButton);
}
private void resizeControl(Rectangle r, Control c)
{
float xRatio = (float)(this.Width) / (float)(originalFormSize.Width);
float yRatio = (float)(this.Height) / (float)(originalFormSize.Height);
int newWidth = (int)(r.Width * xRatio);
int newHeight = (int)(r.Height * yRatio);
int newX = (int)(r.Width * xRatio);
int newY = (int)(r.Height * yRatio);
c.Location = new Point(newX, newY);
c.Size = new Size(newWidth, newHeight);
}
When I run this code, it moves the button to the opposite corner of the form, but it resizes it properly.
This would obviously be quite redundant given that I have to get an original size for every control I want to resize, but I would be fine with that if I could get dynamic resizing to work. I am surprised that this is not a more common problem, and I couldn't find hardly anything on this specific topic other than to use the Anchor and Dock properties. Is there an easy way to do this that I am missing? Is this a more difficult problem than it seems?
Put TextBox anchor property values as Top, Bottom, Left, Right and resize the form. That should work.

Overlapping panels with "Dock" resize functionality

I've been struggling for a couple of days with this issue, which seems like it would have an obvious solution.
How could I place a panel, which maintains position and resizes as if Docked, in the middle of the form, over other panels populating a TableLayoutPanel without messing Column/Row spans and misplacing other containers and controls?
I would like to avoid developing custom functionality and make due with the basic visual studio toolbox (extensions are welcome).
In an effort to better explain what I'm hoping to achieve, I've provided a link to an image of the goal.
Blue is the dynamically resizable, centered pop-up panel.
Green is an Image/BackgroundImage starting on cell [1,1] of the TableLayoutPanel.
Orange is a drop-down panel for a side menu, also starting on cell [1,1].
So... I found a solution.
I guess I needed to admit that I have to write the resizing function myself.
I'm new here, so I don't know if answering oneself question is canon, but here goes.
For the Orange panel, given it's Anchored Top,Left:
private double panel1WidthRatio, panel1HeightRatio; // Global variables for maintaining ratio.
private void ParentForm_ResizeBegin(object sender, EventArgs e)
{
double p1w = this.side_panel.Size.Width; // Grab the panels' dimensions
double p1h = this.side_panel.Size.Height; // as soon as the user begins to resize
double fw = this.Size.Width; // in order to store
double fh = this.Size.Height; // the Panel to Form
panel1WidthRatio = p1w / fw; // dimension ratios.
panel1HeightRatio = p1h / fh;
}
private void ParentForm_SizeChanged(object sender, EventArgs e)
{
double formWidth = this.Size.Width; // As soon as a new size is set
double formHeight = this.Size.Height; // resize the panel using the earlier ratio.
this.side_panel.Size = new Size((int)(formWidth * panel1WidthRatio), (int)(formHeight * panel1HeightRatio));
}
For the Blue panel, given no Anchors, simply replace:
this.side_panel.Size = new Size((int)(formWidth * panel1WidthRatio), (int)(formHeight * panel1HeightRatio));
with:
this.middle_panel.Size = new Size((int)(formWidth * panel2WidthRatio), (int)(formHeight * panel2HeightRatio));
this.middle_panel.Location = new System.Drawing.Point(this.Size.Width/4-9, this.Size.Height/4-24);
The constants (9,24) take into account the Form's frame and borders, so as to correctly reposition the panel in the center.

how can I draw when the form is open?

Hello it might be a silly question but i can't figure out the problem here.. here is my code to fill a form with a single block:
private void drawBackground()
{
Graphics g = genPan.CreateGraphics();
Image Block = Image.FromFile(#"C:\Users\Administrator\Desktop\movment V1\movment V1\images\BrownBlock.png");
float recWidth = Block.Width;
// rectangle width didnt change the name from previous code, it's picture width.
float recHeight = Block.Height;
// rectangle Heightdidnt change the name from previous code, it's picture Height.
float WinWidth = genPan.Width; // genPan is a panel that docked to the form
float WinHeight = genPan.Height;
float curWidth = 0; //indicates where the next block will be placed int the X axis
float curHeight = 0;//indicates where the next block will be placed int the Y axis
while ((curHeight + recHeight) <= WinHeight)
{
if (curWidth >= WinWidth / 3 || curWidth <= WinWidth / 1.5 ||
curHeight >= WinHeight / 3 || curHeight <= WinHeight / 1.5)
{
g.DrawImage(Block, curWidth, curHeight, recWidth , recHeight );
}
curWidth += recWidth;
if ((WinWidth - curWidth) < recWidth)
{
curWidth = 0;
curHeight += 50;
}
}
}
If I launch this func through a button it will work perfectly fine. But if I launch the func after the InitializeComponent(); method in the constructor OR in a FORM shown event, while the button is still on the form it will execute the func however the block backgroud wont be visible but the grey color will be. but if i remove the button the background will be visible. =\
I cant understand why is it happening, how to fix it and what am I doing wrong.. can anyone explain please..?
You can't really do that using your current logic. The problem is that the control (genPan panel in your case) has its own Paint event that when called, overwriting any graphics you used on it.
Even when you draw in button click it works only until the form is repainted e.g. try focus other window and focus your form again: you will lose what you have drawn.
Proper way to do such things is to write your own class that inherit from some basic control (Panel in your case) then overriding its OnPaint event and draw whatever you want there.
So first, have such class:
public class BlockBackgroundPanel : Panel
{
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
Image Block = Image.FromFile(#"C:\Users\Administrator\Desktop\movment V1\movment V1\images\BrownBlock.png");
float recWidth = Block.Width;
//rest of your code, replace "genPan" with "this" as you are inside the Panel
}
}
Then in your .Designer.cs file (you can open it in the Studio) change the code so that genPan will be of your new class instance:
private BlockBackgroundPanel genPan;
//...
this.genPan = new BlockBackgroundPanel ();
If you need to draw background only based on some condition/action/user interaction...
Put the call to this funciton into the forms OnPaint method and enable it only if some bollean variale equals true. And that boolean becomes true, only on button click.
Some hypothetical example:
protected override OnPaint(...) //FORMS ONPAINT OVERRIDE
{
if(needBackGround) //INITIAL VALUE AT STARTUP IS FALSE
drawBackground();
}
public void ButtonClickHandler(...)
{
needBackGround= !needBackGround; //INVERSE THE VALUE OF BOOLEAN
}
This is clearly just a sniplet to give you a hint and not a real code. There could be other problems you will need to face, like: flickering, handling resize, performance... but this is just a point to start.

Winform not scrolling when drawing objects on it

C#3.0,.net framework 3.5
I am drawing ( using the draw method in the graphics class) a lot of solid rectangles on a windows form vertically. The form starts at 500 x 500 px and the rectangles are only drawn at runtime after data is downloaded from the net -and the number of rectangles depends on the download so I do not know it upfront.
So only a few rectangles are drawn as the size of the form is fixed.
So I googled/Binged ( lest someone suggest I do that) and found a few tips but they don't work in this case -like setting the forms AutoScroll property to true or trying double buffering.I also tried to draw on a listbox control and set it's scroll property etc...but no dice.
I'm guessing there is no way to display , say 200 rectangles vertically on a windows form using draw. I need some other solution... any ideas please.
Maybe a list of pictureboxes and then populate each picturebox with the solid color ?
Thanks
You are drawing GDI+ rectangles on a form during the paint event? The form would have no idea that you are creating objects outside of the clipping space and would therefore have no idea that you need to scroll.
You would need to add a scrollbar to the form and then calculate the value\position of the scrollbar and use that to determine what portion of your rectangles to draw upon the paint event. This would involve a bit of manual effort. You could draw them all to an in-memory bitmap of the appropriate size and then just copy the portions of that to the form upon draw.
Or:
If you wanted the form to do this for you, create a custom rectangle control and place 200 of those on the form. Since they are components and have a concrete height & width, the form would then know it needed to scroll, and would do so accordingly provided that autoscroll was set.
it can be as simple as this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.DoubleBuffered = true;
this.AutoScroll = true;
for (int i = 0; i < 100; i++)
this.Controls.Add(new Rectangle() { Top = i * 120, Left = 10 });
}
}
public class Rectangle : Control
{
public Rectangle()
{
this.Width = 100;
this.Height = 100;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawRectangle(new Pen(Color.Black, 5), 0, 0, 100, 100);
}
}

Winform - determine if mouse has left user control

I am struggling with something that I think should be easily (ish). I have a windows form and a flowgridlayout panel at the bottom of the form. Inside this form I dynamically populate it with X number of User Controls. The controls are all the same type.
The goal is when the user hoovers the mouse over the user control it opens another form and positions it where the mouse is. When mouse leaves the form the opened form disappears.
This almost works great. The problem is when the User Control has anything like a label or text box inside it. It is considered to have left the UC so the form disappears.
My thought was then to use the X and Y to tell if it is inside the UC but I can not figure this out.
Can I ask:
1) What is the best approach to this?
2) How can I code it, as the UC's are dynamic I can not know exactly where they will be.
Thanks
EDIT
I am trying to figure out the mouse pointers but not getting there. The code below is within the UC SmallTagBox_MouseLeave event:
Point loc = this.Location;
Point p = this.PointToScreen(this.Location);
Point p2 = this.PointToScreen(this.Parent.Location);
Point ms = MousePosition;
Rectangle screenBounds = new Rectangle(this.PointToScreen(this.Location), this.Size);
if (!screenBounds.Contains(ms))
{
thw.Close();
thw = null;
}
loc {X = 275 Y = 3} System.Drawing.Point
p {X = 808 Y = 908} System.Drawing.Point
p {X = 808 Y = 908} System.Drawing.Point
p2 {X = 545 Y = 1542} System.Drawing.Point
ms {X = 574 Y = 914} System.Drawing.Point
screenBounds {X = 808 Y = 908 Width = 62 Height = 29} System.Drawing.Rectangle
I do not understand how p2 (its parent) can have a greater Y value relative to the screen.
Hooking all the controls MouseEnter and MouseLeave events, then figuring out if it is still inside the form is pretty painful. A simple timer can get the job done too:
public partial class Form1 : Form {
private Timer mTimer;
public Form1() {
InitializeComponent();
mTimer = new Timer();
mTimer.Interval = 200;
mTimer.Tick += mTimer_Tick;
mTimer.Enabled = true;
}
private void mTimer_Tick(object sender, EventArgs e) {
if (!this.DesktopBounds.Contains(Cursor.Position)) this.Close();
}
}
Idea 1) When the MouseLeave event fires, you can check the mouse coordinates (relative to screen) and check if they're still within the bounds of your usercontrol. If they are, it should be assumed that the mouse has to pass back through the control to get outside the bounds, and you can safely ignore the event this time.
Idea 2) Attach MouseEnter event handlers to the child controls. Then when the mouse enters one, you will know and can ignore the usercontrol's MouseLeave event. Then when the child's MouseLeave event fires, check for the usercontrol's MouseEnter again.
I think I would add an event handler for MouseLeave for every control that you have and use the Parent property to find the User Control you are after. I agree, it will be a bit painful though.
You can also loop through all the child controls (recursive) on your control, and attach a MouseEnter and MouseLeave event to them as well.
You have to do some bookkeeping if the mouse is in your control, or some child control.

Categories

Resources