How to resize label control - c#

I'm trying to allow user to resize a label (the control, not the font) before printing it.
For this I'm using a LabelPrint class and I've set AutoSize property to false. I already made a code that would adjust font size to my label size. But I can't change the label size… Thanks for help, here's the code.
class LabelPrint : Label
{
public LabelPrint()
{
this.ResizeRedraw = true;
this.AutoSize = false;
this.TextAlign = ContentAlignment.MiddleCenter;
this.Font = new Font(this.Font.FontFamily, 12);
this.SizeChanged += new EventHandler(this.SizeLabelFont);
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x84)
{ // Trap WM_NCHITTEST
var pos = this.PointToClient(new Point(m.LParam.ToInt32()));
if (pos.X >= this.Size.Width - grab && pos.Y >= this.Size.Height - grab)
m.Result = new IntPtr(17); // HT_BOTTOMRIGHT
}
}
private const int grab = 16;
public void SizeLabelFont(object sender, EventArgs e)
{
// Only bother if there's text.
string txt = this.Text;
if (txt.Length > 0)
{
int best_size = 100;
// See how much room we have, allowing a bit
// for the Label's internal margin.
int wid = this.DisplayRectangle.Width - 3;
int hgt = this.DisplayRectangle.Height - 3;
// Make a Graphics object to measure the text.
using (Graphics gr = this.CreateGraphics())
{
for (int i = 1; i <= 100; i++)
{
using (Font test_font =
new Font(this.Font.FontFamily, i))
{
// See how much space the text would
// need, specifying a maximum width.
SizeF text_size =
gr.MeasureString(txt, test_font);
if ((text_size.Width > wid) ||
(text_size.Height > hgt))
{
best_size = i - 1;
break;
}
}
}
}
// Use that font size.
this.Font = new Font(this.Font.FontFamily, best_size);
}
}
}
I tried to add this in LabelPrint class:
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
base.AutoSize = false;
}
But I still get the same problem: AutoSize is set to false so when i increase the fontsize, i can't see all my label.Text but I can't resize the label itself… I mean with the mouse and ResizeRedraw().

Related

How to delete title bar while also keeping the window resizable? [duplicate]

Does anyone know how I can resize a winform when it has no border. I don't want the default border that Windows has, so I changed the property "FormBorderStyle" to "None". This removed the border, although now it can't be resized. I've figured out how to move the form around, I just need to know how to resize it.
Some sample code that allow moving and resizing the form:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.ResizeRedraw, true);
}
private const int cGrip = 16; // Grip size
private const int cCaption = 32; // Caption bar height;
protected override void OnPaint(PaintEventArgs e) {
Rectangle rc = new Rectangle(this.ClientSize.Width - cGrip, this.ClientSize.Height - cGrip, cGrip, cGrip);
ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc);
rc = new Rectangle(0, 0, this.ClientSize.Width, cCaption);
e.Graphics.FillRectangle(Brushes.DarkBlue, rc);
}
protected override void WndProc(ref Message m) {
if (m.Msg == 0x84) { // Trap WM_NCHITTEST
Point pos = new Point(m.LParam.ToInt32());
pos = this.PointToClient(pos);
if (pos.Y < cCaption) {
m.Result = (IntPtr)2; // HTCAPTION
return;
}
if (pos.X >= this.ClientSize.Width - cGrip && pos.Y >= this.ClientSize.Height - cGrip) {
m.Result = (IntPtr)17; // HTBOTTOMRIGHT
return;
}
}
base.WndProc(ref m);
}
}
Here's a complete example of a customized form with all 8 points of resizing:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None; // no borders
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.ResizeRedraw, true); // this is to avoid visual artifacts
}
protected override void OnPaint(PaintEventArgs e) // you can safely omit this method if you want
{
e.Graphics.FillRectangle(Brushes.Green, Top);
e.Graphics.FillRectangle(Brushes.Green, Left);
e.Graphics.FillRectangle(Brushes.Green, Right);
e.Graphics.FillRectangle(Brushes.Green, Bottom);
}
private const int
HTLEFT = 10,
HTRIGHT = 11,
HTTOP = 12,
HTTOPLEFT = 13,
HTTOPRIGHT = 14,
HTBOTTOM = 15,
HTBOTTOMLEFT = 16,
HTBOTTOMRIGHT = 17;
const int _ = 10; // you can rename this variable if you like
Rectangle Top { get { return new Rectangle(0, 0, this.ClientSize.Width, _); } }
Rectangle Left { get { return new Rectangle(0, 0, _, this.ClientSize.Height); } }
Rectangle Bottom { get { return new Rectangle(0, this.ClientSize.Height - _, this.ClientSize.Width, _); } }
Rectangle Right { get { return new Rectangle(this.ClientSize.Width - _, 0, _, this.ClientSize.Height); } }
Rectangle TopLeft { get { return new Rectangle(0, 0, _, _); } }
Rectangle TopRight { get { return new Rectangle(this.ClientSize.Width - _, 0, _, _); } }
Rectangle BottomLeft { get { return new Rectangle(0, this.ClientSize.Height - _, _, _); } }
Rectangle BottomRight { get { return new Rectangle(this.ClientSize.Width - _, this.ClientSize.Height - _, _, _); } }
protected override void WndProc(ref Message message)
{
base.WndProc(ref message);
if (message.Msg == 0x84) // WM_NCHITTEST
{
var cursor = this.PointToClient(Cursor.Position);
if (TopLeft.Contains(cursor)) message.Result = (IntPtr)HTTOPLEFT;
else if (TopRight.Contains(cursor)) message.Result = (IntPtr)HTTOPRIGHT;
else if (BottomLeft.Contains(cursor)) message.Result = (IntPtr)HTBOTTOMLEFT;
else if (BottomRight.Contains(cursor)) message.Result = (IntPtr)HTBOTTOMRIGHT;
else if (Top.Contains(cursor)) message.Result = (IntPtr)HTTOP;
else if (Left.Contains(cursor)) message.Result = (IntPtr)HTLEFT;
else if (Right.Contains(cursor)) message.Result = (IntPtr)HTRIGHT;
else if (Bottom.Contains(cursor)) message.Result = (IntPtr)HTBOTTOM;
}
}}
"Sizer" is the light blue panel in the right bottom corner
int Mx;
int My;
int Sw;
int Sh;
bool mov;
void SizerMouseDown(object sender, MouseEventArgs e)
{
mov = true;
My = MousePosition.Y;
Mx = MousePosition.X;
Sw = Width;
Sh = Height;
}
void SizerMouseMove(object sender, MouseEventArgs e)
{
if (mov == true) {
Width = MousePosition.X - Mx + Sw;
Height = MousePosition.Y - My + Sh;
}
}
void SizerMouseUp(object sender, MouseEventArgs e)
{
mov = false;
}
(elaborate : in comment 2)
Resize "Form" From All place And Move it >>> Full Code <<<<<
//First u most to add this Class
class ReSize
{
private bool Above, Right, Under, Left, Right_above, Right_under, Left_under, Left_above;
int Thickness=6; //Thickness of border u can cheang it
int Area = 8; //Thickness of Angle border
/// <summary>
/// Constructor
/// </summary>
/// <param name="thickness">set thickness of form border</param>
public ReSize(int thickness)
{
Thickness = thickness;
}
/// <summary>
/// Constructor set thickness of form border=1
/// </summary>
public ReSize()
{
Thickness = 10;
}
//Get Mouse Position
public string getMosuePosition(Point mouse, Form form)
{
bool above_underArea = mouse.X > Area && mouse.X < form.ClientRectangle.Width - Area; /* |\AngleArea(Left_Above)\(=======above_underArea========)/AngleArea(Right_Above)/| */ //Area===>(==)
bool right_left_Area = mouse.Y > Area && mouse.Y < form.ClientRectangle.Height - Area;
bool _Above=mouse.Y <= Thickness; //Mouse in Above All Area
bool _Right= mouse.X >= form.ClientRectangle.Width - Thickness;
bool _Under=mouse.Y >= form.ClientRectangle.Height - Thickness;
bool _Left=mouse.X <= Thickness;
Above = _Above && (above_underArea); if (Above) return "a"; /*Mouse in Above All Area WithOut Angle Area */
Right = _Right && (right_left_Area); if (Right) return "r";
Under = _Under && (above_underArea); if (Under) return "u";
Left = _Left && (right_left_Area); if (Left) return "l";
Right_above =/*Right*/ (_Right && (!right_left_Area)) && /*Above*/ (_Above && (!above_underArea)); if (Right_above) return "ra"; /*if Mouse Right_above */
Right_under =/* Right*/((_Right) && (!right_left_Area)) && /*Under*/(_Under && (!above_underArea)); if (Right_under) return "ru"; //if Mouse Right_under
Left_under = /*Left*/((_Left) && (!right_left_Area)) && /*Under*/ (_Under && (!above_underArea)); if (Left_under) return "lu"; //if Mouse Left_under
Left_above = /*Left*/((_Left) && (!right_left_Area)) && /*Above*/(_Above && (!above_underArea)); if (Left_above) return "la"; //if Mouse Left_above
return "";
}
}
Then Form cs
public partial class FormGDI : Form
{
ReSize resize = new ReSize(); // ReSize Class "/\" To Help Resize Form <None Style>
public FormGDI()
{
InitializeComponent();
this.SetStyle(ControlStyles.ResizeRedraw, true);
}
private const int cGrip = 16; // Grip size
private const int cCaption = 32; // Caption bar height;
protected override void OnPaint(PaintEventArgs e)
{
//this if you want to draw (if)
Color theColor = Color.FromArgb(10, 20, 20, 20);
theColor = Color.DarkBlue;
int BORDER_SIZE = 4;
ControlPaint.DrawBorder(e.Graphics, ClientRectangle,
theColor, BORDER_SIZE, ButtonBorderStyle.Dashed,
theColor, BORDER_SIZE, ButtonBorderStyle.Dashed,
theColor, BORDER_SIZE, ButtonBorderStyle.Dashed,
theColor, BORDER_SIZE, ButtonBorderStyle.Dashed);
Rectangle rc = new Rectangle(this.ClientSize.Width - cGrip, this.ClientSize.Height - cGrip, cGrip, cGrip);
ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc);
rc = new Rectangle(0, 0, this.ClientSize.Width, cCaption);
e.Graphics.FillRectangle(Brushes.DarkBlue, rc);
base.OnPaint(e);
}
//set MinimumSize to Form
public override Size MinimumSize
{
get
{
return base.MinimumSize;
}
set
{
base.MinimumSize = new Size(179, 51);
}
}
//
//override WndProc
//
protected override void WndProc(ref Message m)
{
//****************************************************************************
int x = (int)(m.LParam.ToInt64() & 0xFFFF); //get x mouse position
int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16); //get y mouse position you can gave (x,y) it from "MouseEventArgs" too
Point pt = PointToClient(new Point(x, y));
if (m.Msg == 0x84)
{
switch (resize.getMosuePosition(pt, this))
{
case "l": m.Result = (IntPtr)10; return; // the Mouse on Left Form
case "r": m.Result = (IntPtr)11; return; // the Mouse on Right Form
case "a": m.Result = (IntPtr)12; return;
case "la": m.Result = (IntPtr)13; return;
case "ra": m.Result = (IntPtr)14; return;
case "u": m.Result = (IntPtr)15; return;
case "lu": m.Result = (IntPtr)16; return;
case "ru": m.Result = (IntPtr)17; return; // the Mouse on Right_Under Form
case "": m.Result = pt.Y < 32 /*mouse on title Bar*/ ? (IntPtr)2 : (IntPtr)1; return;
}
}
base.WndProc(ref m);
}
}
The simplest way is to assign mouse events to the form or the title bar, whatever you want to be hold for moving.
You can move a BorderLess Form with assigning these methods to there events as names in method.
int movX,movY;
bool isMoving;
private void onMouseDown(object sender, MouseEventArgs e)
{
// Assign this method to mouse_Down event of Form or Panel,whatever you want
isMoving = true;
movX = e.X;
movY = e.Y;
}
private void onMouseMove(object sender, MouseEventArgs e)
{
// Assign this method to Mouse_Move event of that Form or Panel
if (isMoving)
{
this.SetDesktopLocation(MousePosition.X - movX, MousePosition.Y - movY);
}
}
private void onMouseUp(object sender, MouseEventArgs e)
{
// Assign this method to Mouse_Up event of Form or Panel.
isMoving = false;
}
If you don't mind a short bar at the top, you can use a "regular" sizable form with the .ControlBox set to false and .Text (the caption bar at the top) set to an empty string.
You'll end up with a resizable form that looks like the Task Manager when you double-click on a chart.
The advantage of this method is that when you resize it from the left, the right border does not jerk. The same is also when you resize it from the top.
this.FormBorderStyle = FormBorderStyle.Sizable;
this.ControlBox = false;
this.Text = "";
(1) FormBorderStyle= Sizable or SizableToolWindows
(2) clear text in form text

how can I split a panel to clickable segments in c# winform?

I am trying to simulate a LED display board with c# . I need a control which contains 1536 clickable controls to simulate LEDs (96 in width and 16 in Height). I used a panel named pnlContainer for this and user will add 1536 tiny customized panels at runtime. These customized panels should change their color by click event at runtime. Everything works . But adding this number of tiny panels to the container takes long time ( about 10 secs). What is your suggestion to solve this issue? Any tips are appreciated.
this is my custome panel:
public partial class LedPanel : Panel
{
public LedPanel()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
if (this.BackColor == Color.Black)
{
this.BackColor = Color.Red;
}
else
{
this.BackColor = Color.Black;
}
}
}
}
and this is piece of code which adds tiny panels to the pnlContainer :
private void getPixels(Bitmap img2)
{
pnlContainer.Controls.Clear();
for (int i = 0; i < 96; i++)
{
for (int j = 0; j < 16; j++)
{
Custom_Controls.LedPanel led = new Custom_Controls.LedPanel();
led.Name = i.ToString() + j.ToString();
int lWidth = (int)(pnlContainer.Width / 96);
led.Left = i * lWidth;
led.Top = j * lWidth;
led.Width = led.Height = lWidth;
if (img2.GetPixel(i, j).R>numClear.Value)
{
led.BackColor = Color.Red;
}
else
{
led.BackColor = Color.Black;
}
led.BorderStyle = BorderStyle.FixedSingle;
pnlContainer.Controls.Add(led);
}
}
}
Is there any better approach or better control instead of panelto do this?
I agree with what #TaW recommends. Don't put 1000+ controls on a form. Use some sort of data structure, like an array to keep track of which LEDs need to be lit and then draw them in the Paint event of a Panel.
Here's an example. Put a Panel on a form and name it ledPanel. Then use code similar to the following. I just randomly set the values of the boolean array. You would need to set them appropriately in response to a click of the mouse. I didn't include that code, but basically you need to take the location of the mouse click, determine which array entry needs to be set (or unset) and then invalidate the panel so it will redraw itself.
public partial class Form1 : Form
{
//set these variables appropriately
int matrixWidth = 96;
int matrixHeight = 16;
//An array to hold which LEDs must be lit
bool[,] ledMatrix = null;
//Used to randomly populate the LED array
Random rnd = new Random();
public Form1()
{
InitializeComponent();
ledPanel.BackColor = Color.Black;
ledPanel.Resize += LedPanel_Resize;
//clear the array by initializing a new one
ledMatrix = new bool[matrixWidth, matrixHeight];
//Force the panel to repaint itself
ledPanel.Invalidate();
}
private void LedPanel_Resize(object sender, EventArgs e)
{
//If the panel resizes, then repaint.
ledPanel.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
//clear the array by initializing a new one
ledMatrix = new bool[matrixWidth, matrixHeight];
//Randomly set 250 of the 'LEDs';
for (int i = 0; i < 250; i++)
{
ledMatrix[rnd.Next(0, matrixWidth), rnd.Next(0, matrixHeight)] = true;
}
//Make the panel repaint itself
ledPanel.Invalidate();
}
private void ledPanel_Paint(object sender, PaintEventArgs e)
{
//Calculate the width and height of each LED based on the panel width
//and height and allowing for a line between each LED
int cellWidth = (ledPanel.Width - 1) / (matrixWidth + 1);
int cellHeight = (ledPanel.Height - 1) / (matrixHeight + 1);
//Loop through the boolean array and draw a filled rectangle
//for each one that is set to true
for (int i = 0; i < matrixWidth; i++)
{
for (int j = 0; j < matrixHeight; j++)
{
if (ledMatrix != null)
{
//I created a custom brush here for the 'off' LEDs because none
//of the built in colors were dark enough for me. I created it
//in a using block because custom brushes need to be disposed.
using (var b = new SolidBrush(Color.FromArgb(64, 0, 0)))
{
//Determine which brush to use depending on if the LED is lit
Brush ledBrush = ledMatrix[i, j] ? Brushes.Red : b;
//Calculate the top left corner of the rectangle to draw
var x = (i * (cellWidth + 1)) + 1;
var y = (j * (cellHeight + 1) + 1);
//Draw a filled rectangle
e.Graphics.FillRectangle(ledBrush, x, y, cellWidth, cellHeight);
}
}
}
}
}
private void ledPanel_MouseUp(object sender, MouseEventArgs e)
{
//Get the cell width and height
int cellWidth = (ledPanel.Width - 1) / (matrixWidth + 1);
int cellHeight = (ledPanel.Height - 1) / (matrixHeight + 1);
//Calculate which LED needs to be turned on or off
int x = e.Location.X / (cellWidth + 1);
int y = e.Location.Y / (cellHeight + 1);
//Toggle that LED. If it's off, then turn it on and if it's on,
//turn it off
ledMatrix[x, y] = !ledMatrix[x, y];
//Force the panel to update itself.
ledPanel.Invalidate();
}
}
I'm sure there can be many improvements to this code, but it should give you an idea on how to do it.
#Chris and #user10112654 are right.
here is a code similar to #Chris but isolates the displaying logic in a separate class. (#Chris answered your question when I was writing the code :))))
just create a 2D array to initialize the class and pass it to the Initialize method.
public class LedDisplayer
{
public LedDisplayer(Control control)
{
_control = control;
_control.MouseDown += MouseDown;
_control.Paint += Control_Paint;
// width and height of your tiny boxes
_width = 5;
_height = 5;
// margin between tiny boxes
_margin = 1;
}
private readonly Control _control;
private readonly int _width;
private readonly int _height;
private readonly int _margin;
private bool[,] _values;
// call this method first of all to initialize the Displayer
public void Initialize(bool[,] values)
{
_values = values;
_control.Invalidate();
}
private void MouseDown(object sender, MouseEventArgs e)
{
var firstIndex = e.X / OuterWidth();
var secondIndex = e.Y / OuterHeight();
_values[firstIndex, secondIndex] = !_values[firstIndex, secondIndex];
_control.Invalidate(); // you can use other overloads of Invalidate method for the blink problem
}
private void Control_Paint(object sender, PaintEventArgs e)
{
if (_values == null)
return;
e.Graphics.Clear(_control.BackColor);
for (int i = 0; i < _values.GetLength(0); i++)
for (int j = 0; j < _values.GetLength(1); j++)
Rectangle(i, j).Paint(e.Graphics);
}
private RectangleInfo Rectangle(int firstIndex, int secondIndex)
{
var x = firstIndex * OuterWidth();
var y = secondIndex * OuterHeight();
var rectangle = new Rectangle(x, y, _width, _height);
if (_values[firstIndex, secondIndex])
return new RectangleInfo(rectangle, Brushes.Red);
return new RectangleInfo(rectangle, Brushes.Black);
}
private int OuterWidth()
{
return _width + _margin;
}
private int OuterHeight()
{
return _height + _margin;
}
}
public class RectangleInfo
{
public RectangleInfo(Rectangle rectangle, Brush brush)
{
Rectangle = rectangle;
Brush = brush;
}
public Rectangle Rectangle { get; }
public Brush Brush { get; }
public void Paint(Graphics graphics)
{
graphics.FillRectangle(Brush, Rectangle);
}
}
this is how it's used in the form:
private void button2_Click(object sender, EventArgs e)
{
// define the displayer class
var displayer = new LedDisplayer(panel1);
// define the array to initilize the displayer
var display = new bool[,]
{
{true, false, false, true },
{false, true, false, false },
{false, false, true, false },
{true, false, false, false }
};
// and finally
displayer.Initialize(display);
}

Drawing background image into form overflow client area

I have a problem with drawing image on form background. I have a form where there are inserted both scrollbars (H and V). Because I need to be able display image in original size I use them for scrolling it but when I scroll to maximum right or bottom on both sides missing 7 pixels which are hidden under scrollbars. There is sample code:
private int PosX, PosY;
this.Map = new Bitmap(TestLines.Properties.Resources.mapa);
protected override void OnPaintBackground(PaintEventArgs e)
{
base.OnPaintBackground(e);
if (this.Map != null)
{
e.Graphics.DrawImageUnscaled(Map, new Point(this.PosX, this.PosY));
int MapResX = (int)((float)this.Map.Width / this.Map.HorizontalResolution * e.Graphics.DpiX);
int MapResY = (int)((float)this.Map.Height / this.Map.VerticalResolution * e.Graphics.DpiY);
if (MapResX > this.ClientSize.Width && MapResY > this.ClientSize.Height - this.toolStrip1.Height)
{
hScrollBar1.Minimum = 0;
hScrollBar1.Maximum = MapResX - this.ClientSize.Width + vScrollBar1.Width;
hScrollBar1.Visible = true;
vScrollBar1.Minimum = 0;
vScrollBar1.Maximum = MapResY - this.ClientSize.Height + toolStrip1.Height + hScrollBar1.Height;
vScrollBar1.Visible = true;
}
else if (MapResX > this.ClientSize.Width)
{
hScrollBar1.Minimum = 0;
hScrollBar1.Maximum = MapResX - this.ClientSize.Width;
hScrollBar1.Visible = true;
vScrollBar1.Visible = false;
}
else if (MapResY > this.ClientSize.Height - this.toolStrip1.Height)
{
vScrollBar1.Minimum = 0;
vScrollBar1.Maximum = MapResY - this.ClientSize.Height + toolStrip1.Height;
vScrollBar1.Visible = true;
hScrollBar1.Visible = false;
}
else
{
hScrollBar1.Visible = false;
vScrollBar1.Visible = false;
}
}
}
Note that there is also a toolstrip where i do not draw. And then simple scrollbars actions:
private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
{
this.PosX = -e.NewValue;
this.Invalidate(false);
this.Update();
}
private void vScrollBar1_Scroll(object sender, ScrollEventArgs e)
{
this.PosY = toolStrip1.Height -e.NewValue;
this.Invalidate(false);
this.Update();
}
Can you describe me why this happens ?
This is just not the right way to go about it. Create your own control instead, using Panel as the base class so you get the scrolling for free. Add a new class to your project and paste the code shown below. Compile. Drop it from the top of the toolbox onto your form, you probably want to set its Dock property to Fill. Assign the Map property, either with the designer or in your code.
using System;
using System.Drawing;
using System.Windows.Forms;
class MapPanel : Panel {
public MapPanel() {
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
private Image map;
public Image Map {
get { return map; }
set {
map = value;
this.AutoScrollMinSize = value == null ? Size.Empty : value.Size;
this.Invalidate();
}
}
protected override void OnPaintBackground(PaintEventArgs e) {
base.OnPaintBackground(e);
if (map != null) {
e.Graphics.TranslateTransform(this.AutoScrollPosition.X, this.AutoScrollPosition.Y);
e.Graphics.DrawImage(map, 0, 0);
}
}
}

Handle scrolling of a WinForms control manually

I have a control (System.Windows.Forms.ScrollableControl) which can potentially be very large. It has custom OnPaint logic. For that reason, I am using the workaround described here.
public class CustomControl : ScrollableControl
{
public CustomControl()
{
this.AutoScrollMinSize = new Size(100000, 500);
this.DoubleBuffered = true;
}
protected override void OnScroll(ScrollEventArgs se)
{
base.OnScroll(se);
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var graphics = e.Graphics;
graphics.Clear(this.BackColor);
...
}
}
The painting code mainly draws "normal" things that move when you scroll. The origin of each shape that is drawn is offsetted by this.AutoScrollPosition.
graphics.DrawRectangle(pen, 100 + this.AutoScrollPosition.X, ...);
However, the control also contains "static" elements, which are always drawn at the same position relative to the parent control. For that, I just don't use AutoScrollPosition and draw the shapes directly:
graphics.DrawRectangle(pen, 100, ...);
When the user scrolls, Windows translates the entire visible area in the direction opposite to the scrolling. Usually this makes sense, because then the scrolling seems smooth and responsive (and only the new part has to be redrawn), however the static parts are also affected by this translation (hence the this.Invalidate() in OnScroll). Until the next OnPaint call has successfully redrawn the surface, the static parts are slightly off. This causes a very noticable "shaking" effect when scrolling.
Is there a way I can create a scrollable custom control that does not have this problem with static parts?
You could do this by taking full control of scrolling. At the moment, you're just hooking in to the event to do your logic. I've faced issues with scrolling before, and the only way I've ever managed to get everything to work smoothly is by actually handling the Windows messages by overriding WndProc. For instance, I have this code to synchronize scrolling between several ListBoxes:
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
// 0x115 and 0x20a both tell the control to scroll. If either one comes
// through, you can handle the scrolling before any repaints take place
if (m.Msg == 0x115 || m.Msg == 0x20a)
{
//Do you scroll processing
}
}
Using WndProc will get you the scroll messages before anything gets repainted at all, so you can appropriately handle the static objects. I'd use this to suspend scrolling until an OnPaint occurs. It won't look as smooth, but you won't have issues with the static objects moving.
Since I really needed this, I ended up writing a Control specifically for the case when you have static graphics on a scrollable surface (whose size can be greater than 65535).
It is a regular Control with two ScrollBar controls on it, and a user-assignable Control as its Content. When the user scrolls, the container sets its Content's AutoScrollOffset accordingly. Therefore, it is possible to use controls which use the AutoScrollOffset method for drawing without changing anything. The Content's actual size is exactly the visible part of it at all times. It allows horizontal scrolling by holding down the shift key.
Usage:
var container = new ManuallyScrollableContainer();
var content = new ExampleContent();
container.Content = content;
container.TotalContentWidth = 150000;
container.TotalContentHeight = 5000;
container.Dock = DockStyle.Fill;
this.Controls.Add(container); // e.g. add to Form
Code:
It became a bit lengthy, but I could avoid ugly hacks. Should work with mono. I think it turned out pretty sane.
public class ManuallyScrollableContainer : Control
{
public ManuallyScrollableContainer()
{
InitializeControls();
}
private class UpdatingHScrollBar : HScrollBar
{
protected override void OnValueChanged(EventArgs e)
{
base.OnValueChanged(e);
// setting the scroll position programmatically shall raise Scroll
this.OnScroll(new ScrollEventArgs(ScrollEventType.EndScroll, this.Value));
}
}
private class UpdatingVScrollBar : VScrollBar
{
protected override void OnValueChanged(EventArgs e)
{
base.OnValueChanged(e);
// setting the scroll position programmatically shall raise Scroll
this.OnScroll(new ScrollEventArgs(ScrollEventType.EndScroll, this.Value));
}
}
private ScrollBar shScrollBar;
private ScrollBar svScrollBar;
public ScrollBar HScrollBar
{
get { return this.shScrollBar; }
}
public ScrollBar VScrollBar
{
get { return this.svScrollBar; }
}
private void InitializeControls()
{
this.Width = 300;
this.Height = 300;
this.shScrollBar = new UpdatingHScrollBar();
this.shScrollBar.Top = this.Height - this.shScrollBar.Height;
this.shScrollBar.Left = 0;
this.shScrollBar.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
this.svScrollBar = new UpdatingVScrollBar();
this.svScrollBar.Top = 0;
this.svScrollBar.Left = this.Width - this.svScrollBar.Width;
this.svScrollBar.Anchor = AnchorStyles.Right | AnchorStyles.Top | AnchorStyles.Bottom;
this.shScrollBar.Width = this.Width - this.svScrollBar.Width;
this.svScrollBar.Height = this.Height - this.shScrollBar.Height;
this.Controls.Add(this.shScrollBar);
this.Controls.Add(this.svScrollBar);
this.shScrollBar.Scroll += this.HandleScrollBarScroll;
this.svScrollBar.Scroll += this.HandleScrollBarScroll;
}
private Control _content;
/// <summary>
/// Specifies the control that should be displayed in this container.
/// </summary>
public Control Content
{
get { return this._content; }
set
{
if (_content != value)
{
RemoveContent();
this._content = value;
AddContent();
}
}
}
private void AddContent()
{
if (this.Content != null)
{
this.Content.Left = 0;
this.Content.Top = 0;
this.Content.Width = this.Width - this.svScrollBar.Width;
this.Content.Height = this.Height - this.shScrollBar.Height;
this.Content.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right;
this.Controls.Add(this.Content);
CalculateMinMax();
}
}
private void RemoveContent()
{
if (this.Content != null)
{
this.Controls.Remove(this.Content);
}
}
protected override void OnParentChanged(EventArgs e)
{
// mouse wheel events only arrive at the parent control
if (this.Parent != null)
{
this.Parent.MouseWheel -= this.HandleMouseWheel;
}
base.OnParentChanged(e);
if (this.Parent != null)
{
this.Parent.MouseWheel += this.HandleMouseWheel;
}
}
private void HandleMouseWheel(object sender, MouseEventArgs e)
{
this.HandleMouseWheel(e);
}
/// <summary>
/// Specifies how the control reacts to mouse wheel events.
/// Can be overridden to adjust the scroll speed with the mouse wheel.
/// </summary>
protected virtual void HandleMouseWheel(MouseEventArgs e)
{
// The scroll difference is calculated so that with the default system setting
// of 3 lines per scroll incremenet,
// one scroll will offset the scroll bar value by LargeChange / 4
// i.e. a quarter of the thumb size
ScrollBar scrollBar;
if ((Control.ModifierKeys & Keys.Shift) != 0)
{
scrollBar = this.HScrollBar;
}
else
{
scrollBar = this.VScrollBar;
}
var minimum = 0;
var maximum = scrollBar.Maximum - scrollBar.LargeChange;
if (maximum <= 0)
{
// happens when the entire area is visible
return;
}
var value = scrollBar.Value - (int)(e.Delta * scrollBar.LargeChange / (120.0 * 12.0 / SystemInformation.MouseWheelScrollLines));
scrollBar.Value = Math.Min(Math.Max(value, minimum), maximum);
}
public event ScrollEventHandler Scroll;
protected virtual void OnScroll(ScrollEventArgs e)
{
var handler = this.Scroll;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Event handler for the Scroll event of either scroll bar.
/// </summary>
private void HandleScrollBarScroll(object sender, ScrollEventArgs e)
{
OnScroll(e);
if (this.Content != null)
{
this.Content.AutoScrollOffset = new System.Drawing.Point(-this.HScrollBar.Value, -this.VScrollBar.Value);
this.Content.Invalidate();
}
}
private int _totalContentWidth;
public int TotalContentWidth
{
get { return _totalContentWidth; }
set
{
if (_totalContentWidth != value)
{
_totalContentWidth = value;
CalculateMinMax();
}
}
}
private int _totalContentHeight;
public int TotalContentHeight
{
get { return _totalContentHeight; }
set
{
if (_totalContentHeight != value)
{
_totalContentHeight = value;
CalculateMinMax();
}
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
CalculateMinMax();
}
private void CalculateMinMax()
{
if (this.Content != null)
{
// Reduced formula according to
// http://msdn.microsoft.com/en-us/library/system.windows.forms.scrollbar.maximum.aspx
// Note: The original formula is bogus.
// According to the article, LargeChange has to be known in order to calculate Maximum,
// however, that is not always possible because LargeChange cannot exceed Maximum.
// If (LargeChange) == (1 * visible part of control), the formula can be reduced to:
if (this.TotalContentWidth > this.Content.Width)
{
this.shScrollBar.Enabled = true;
this.shScrollBar.Maximum = this.TotalContentWidth;
}
else
{
this.shScrollBar.Enabled = false;
}
if (this.TotalContentHeight > this.Content.Height)
{
this.svScrollBar.Enabled = true;
this.svScrollBar.Maximum = this.TotalContentHeight;
}
else
{
this.svScrollBar.Enabled = false;
}
// this must be set after the maximum is determined
this.shScrollBar.LargeChange = this.shScrollBar.Width;
this.shScrollBar.SmallChange = this.shScrollBar.LargeChange / 10;
this.svScrollBar.LargeChange = this.svScrollBar.Height;
this.svScrollBar.SmallChange = this.svScrollBar.LargeChange / 10;
}
}
}
Example content:
public class ExampleContent : Control
{
public ExampleContent()
{
this.DoubleBuffered = true;
}
static Random random = new Random();
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var graphics = e.Graphics;
// random color to make the clip rectangle visible in an unobtrusive way
var color = Color.FromArgb(random.Next(160, 180), random.Next(160, 180), random.Next(160, 180));
graphics.Clear(color);
Debug.WriteLine(this.AutoScrollOffset.X.ToString() + ", " + this.AutoScrollOffset.Y.ToString());
CheckerboardRenderer.DrawCheckerboard(
graphics,
this.AutoScrollOffset,
e.ClipRectangle,
new Size(50, 50)
);
StaticBoxRenderer.DrawBoxes(graphics, new Point(0, this.AutoScrollOffset.Y), 100, 30);
}
}
public static class CheckerboardRenderer
{
public static void DrawCheckerboard(Graphics g, Point origin, Rectangle bounds, Size squareSize)
{
var numSquaresH = (bounds.Width + squareSize.Width - 1) / squareSize.Width + 1;
var numSquaresV = (bounds.Height + squareSize.Height - 1) / squareSize.Height + 1;
var startBoxH = (bounds.X - origin.X) / squareSize.Width;
var startBoxV = (bounds.Y - origin.Y) / squareSize.Height;
for (int i = startBoxH; i < startBoxH + numSquaresH; i++)
{
for (int j = startBoxV; j < startBoxV + numSquaresV; j++)
{
if ((i + j) % 2 == 0)
{
Random random = new Random(i * j);
var color = Color.FromArgb(random.Next(70, 95), random.Next(70, 95), random.Next(70, 95));
var brush = new SolidBrush(color);
g.FillRectangle(brush, i * squareSize.Width + origin.X, j * squareSize.Height + origin.Y, squareSize.Width, squareSize.Height);
brush.Dispose();
}
}
}
}
}
public static class StaticBoxRenderer
{
public static void DrawBoxes(Graphics g, Point origin, int boxWidth, int boxHeight)
{
int height = origin.Y;
int left = origin.X;
for (int i = 0; i < 25; i++)
{
Rectangle r = new Rectangle(left, height, boxWidth, boxHeight);
g.FillRectangle(Brushes.White, r);
g.DrawRectangle(Pens.Black, r);
height += boxHeight;
}
}
}

How to move and resize a form without a border?

Does anyone know how I can resize a winform when it has no border. I don't want the default border that Windows has, so I changed the property "FormBorderStyle" to "None". This removed the border, although now it can't be resized. I've figured out how to move the form around, I just need to know how to resize it.
Some sample code that allow moving and resizing the form:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None;
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.ResizeRedraw, true);
}
private const int cGrip = 16; // Grip size
private const int cCaption = 32; // Caption bar height;
protected override void OnPaint(PaintEventArgs e) {
Rectangle rc = new Rectangle(this.ClientSize.Width - cGrip, this.ClientSize.Height - cGrip, cGrip, cGrip);
ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc);
rc = new Rectangle(0, 0, this.ClientSize.Width, cCaption);
e.Graphics.FillRectangle(Brushes.DarkBlue, rc);
}
protected override void WndProc(ref Message m) {
if (m.Msg == 0x84) { // Trap WM_NCHITTEST
Point pos = new Point(m.LParam.ToInt32());
pos = this.PointToClient(pos);
if (pos.Y < cCaption) {
m.Result = (IntPtr)2; // HTCAPTION
return;
}
if (pos.X >= this.ClientSize.Width - cGrip && pos.Y >= this.ClientSize.Height - cGrip) {
m.Result = (IntPtr)17; // HTBOTTOMRIGHT
return;
}
}
base.WndProc(ref m);
}
}
Here's a complete example of a customized form with all 8 points of resizing:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
this.FormBorderStyle = FormBorderStyle.None; // no borders
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.ResizeRedraw, true); // this is to avoid visual artifacts
}
protected override void OnPaint(PaintEventArgs e) // you can safely omit this method if you want
{
e.Graphics.FillRectangle(Brushes.Green, Top);
e.Graphics.FillRectangle(Brushes.Green, Left);
e.Graphics.FillRectangle(Brushes.Green, Right);
e.Graphics.FillRectangle(Brushes.Green, Bottom);
}
private const int
HTLEFT = 10,
HTRIGHT = 11,
HTTOP = 12,
HTTOPLEFT = 13,
HTTOPRIGHT = 14,
HTBOTTOM = 15,
HTBOTTOMLEFT = 16,
HTBOTTOMRIGHT = 17;
const int _ = 10; // you can rename this variable if you like
Rectangle Top { get { return new Rectangle(0, 0, this.ClientSize.Width, _); } }
Rectangle Left { get { return new Rectangle(0, 0, _, this.ClientSize.Height); } }
Rectangle Bottom { get { return new Rectangle(0, this.ClientSize.Height - _, this.ClientSize.Width, _); } }
Rectangle Right { get { return new Rectangle(this.ClientSize.Width - _, 0, _, this.ClientSize.Height); } }
Rectangle TopLeft { get { return new Rectangle(0, 0, _, _); } }
Rectangle TopRight { get { return new Rectangle(this.ClientSize.Width - _, 0, _, _); } }
Rectangle BottomLeft { get { return new Rectangle(0, this.ClientSize.Height - _, _, _); } }
Rectangle BottomRight { get { return new Rectangle(this.ClientSize.Width - _, this.ClientSize.Height - _, _, _); } }
protected override void WndProc(ref Message message)
{
base.WndProc(ref message);
if (message.Msg == 0x84) // WM_NCHITTEST
{
var cursor = this.PointToClient(Cursor.Position);
if (TopLeft.Contains(cursor)) message.Result = (IntPtr)HTTOPLEFT;
else if (TopRight.Contains(cursor)) message.Result = (IntPtr)HTTOPRIGHT;
else if (BottomLeft.Contains(cursor)) message.Result = (IntPtr)HTBOTTOMLEFT;
else if (BottomRight.Contains(cursor)) message.Result = (IntPtr)HTBOTTOMRIGHT;
else if (Top.Contains(cursor)) message.Result = (IntPtr)HTTOP;
else if (Left.Contains(cursor)) message.Result = (IntPtr)HTLEFT;
else if (Right.Contains(cursor)) message.Result = (IntPtr)HTRIGHT;
else if (Bottom.Contains(cursor)) message.Result = (IntPtr)HTBOTTOM;
}
}}
"Sizer" is the light blue panel in the right bottom corner
int Mx;
int My;
int Sw;
int Sh;
bool mov;
void SizerMouseDown(object sender, MouseEventArgs e)
{
mov = true;
My = MousePosition.Y;
Mx = MousePosition.X;
Sw = Width;
Sh = Height;
}
void SizerMouseMove(object sender, MouseEventArgs e)
{
if (mov == true) {
Width = MousePosition.X - Mx + Sw;
Height = MousePosition.Y - My + Sh;
}
}
void SizerMouseUp(object sender, MouseEventArgs e)
{
mov = false;
}
(elaborate : in comment 2)
Resize "Form" From All place And Move it >>> Full Code <<<<<
//First u most to add this Class
class ReSize
{
private bool Above, Right, Under, Left, Right_above, Right_under, Left_under, Left_above;
int Thickness=6; //Thickness of border u can cheang it
int Area = 8; //Thickness of Angle border
/// <summary>
/// Constructor
/// </summary>
/// <param name="thickness">set thickness of form border</param>
public ReSize(int thickness)
{
Thickness = thickness;
}
/// <summary>
/// Constructor set thickness of form border=1
/// </summary>
public ReSize()
{
Thickness = 10;
}
//Get Mouse Position
public string getMosuePosition(Point mouse, Form form)
{
bool above_underArea = mouse.X > Area && mouse.X < form.ClientRectangle.Width - Area; /* |\AngleArea(Left_Above)\(=======above_underArea========)/AngleArea(Right_Above)/| */ //Area===>(==)
bool right_left_Area = mouse.Y > Area && mouse.Y < form.ClientRectangle.Height - Area;
bool _Above=mouse.Y <= Thickness; //Mouse in Above All Area
bool _Right= mouse.X >= form.ClientRectangle.Width - Thickness;
bool _Under=mouse.Y >= form.ClientRectangle.Height - Thickness;
bool _Left=mouse.X <= Thickness;
Above = _Above && (above_underArea); if (Above) return "a"; /*Mouse in Above All Area WithOut Angle Area */
Right = _Right && (right_left_Area); if (Right) return "r";
Under = _Under && (above_underArea); if (Under) return "u";
Left = _Left && (right_left_Area); if (Left) return "l";
Right_above =/*Right*/ (_Right && (!right_left_Area)) && /*Above*/ (_Above && (!above_underArea)); if (Right_above) return "ra"; /*if Mouse Right_above */
Right_under =/* Right*/((_Right) && (!right_left_Area)) && /*Under*/(_Under && (!above_underArea)); if (Right_under) return "ru"; //if Mouse Right_under
Left_under = /*Left*/((_Left) && (!right_left_Area)) && /*Under*/ (_Under && (!above_underArea)); if (Left_under) return "lu"; //if Mouse Left_under
Left_above = /*Left*/((_Left) && (!right_left_Area)) && /*Above*/(_Above && (!above_underArea)); if (Left_above) return "la"; //if Mouse Left_above
return "";
}
}
Then Form cs
public partial class FormGDI : Form
{
ReSize resize = new ReSize(); // ReSize Class "/\" To Help Resize Form <None Style>
public FormGDI()
{
InitializeComponent();
this.SetStyle(ControlStyles.ResizeRedraw, true);
}
private const int cGrip = 16; // Grip size
private const int cCaption = 32; // Caption bar height;
protected override void OnPaint(PaintEventArgs e)
{
//this if you want to draw (if)
Color theColor = Color.FromArgb(10, 20, 20, 20);
theColor = Color.DarkBlue;
int BORDER_SIZE = 4;
ControlPaint.DrawBorder(e.Graphics, ClientRectangle,
theColor, BORDER_SIZE, ButtonBorderStyle.Dashed,
theColor, BORDER_SIZE, ButtonBorderStyle.Dashed,
theColor, BORDER_SIZE, ButtonBorderStyle.Dashed,
theColor, BORDER_SIZE, ButtonBorderStyle.Dashed);
Rectangle rc = new Rectangle(this.ClientSize.Width - cGrip, this.ClientSize.Height - cGrip, cGrip, cGrip);
ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc);
rc = new Rectangle(0, 0, this.ClientSize.Width, cCaption);
e.Graphics.FillRectangle(Brushes.DarkBlue, rc);
base.OnPaint(e);
}
//set MinimumSize to Form
public override Size MinimumSize
{
get
{
return base.MinimumSize;
}
set
{
base.MinimumSize = new Size(179, 51);
}
}
//
//override WndProc
//
protected override void WndProc(ref Message m)
{
//****************************************************************************
int x = (int)(m.LParam.ToInt64() & 0xFFFF); //get x mouse position
int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16); //get y mouse position you can gave (x,y) it from "MouseEventArgs" too
Point pt = PointToClient(new Point(x, y));
if (m.Msg == 0x84)
{
switch (resize.getMosuePosition(pt, this))
{
case "l": m.Result = (IntPtr)10; return; // the Mouse on Left Form
case "r": m.Result = (IntPtr)11; return; // the Mouse on Right Form
case "a": m.Result = (IntPtr)12; return;
case "la": m.Result = (IntPtr)13; return;
case "ra": m.Result = (IntPtr)14; return;
case "u": m.Result = (IntPtr)15; return;
case "lu": m.Result = (IntPtr)16; return;
case "ru": m.Result = (IntPtr)17; return; // the Mouse on Right_Under Form
case "": m.Result = pt.Y < 32 /*mouse on title Bar*/ ? (IntPtr)2 : (IntPtr)1; return;
}
}
base.WndProc(ref m);
}
}
The simplest way is to assign mouse events to the form or the title bar, whatever you want to be hold for moving.
You can move a BorderLess Form with assigning these methods to there events as names in method.
int movX,movY;
bool isMoving;
private void onMouseDown(object sender, MouseEventArgs e)
{
// Assign this method to mouse_Down event of Form or Panel,whatever you want
isMoving = true;
movX = e.X;
movY = e.Y;
}
private void onMouseMove(object sender, MouseEventArgs e)
{
// Assign this method to Mouse_Move event of that Form or Panel
if (isMoving)
{
this.SetDesktopLocation(MousePosition.X - movX, MousePosition.Y - movY);
}
}
private void onMouseUp(object sender, MouseEventArgs e)
{
// Assign this method to Mouse_Up event of Form or Panel.
isMoving = false;
}
If you don't mind a short bar at the top, you can use a "regular" sizable form with the .ControlBox set to false and .Text (the caption bar at the top) set to an empty string.
You'll end up with a resizable form that looks like the Task Manager when you double-click on a chart.
The advantage of this method is that when you resize it from the left, the right border does not jerk. The same is also when you resize it from the top.
this.FormBorderStyle = FormBorderStyle.Sizable;
this.ControlBox = false;
this.Text = "";
(1) FormBorderStyle= Sizable or SizableToolWindows
(2) clear text in form text

Categories