I have a form with it's FormBorderStyle set to Sizable. This creates the grip in the bottom right. The only way to resize the window is to get your mouse right exactly on the edge. I'm wondering if there is a way to change the cursor to be able to resize when the user mouses over the grip, or if I can increase the range at which it will allow you to resize on the edge so that you don't have to be so precise with your mouse location.
Here is a link to a similar SO question. This guy has no borders, so you may have to do it a little differently, but should give you a direction to go in. I will repaste his code here for completion:
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() & 0xffff, m.LParam.ToInt32() >> 16);
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);
}
}
try this
yourObject.Cursor = Cursors.SizeAll;
More on this site: MSDN
Related
So I have a resizable control which I'm using WM_NCHITTEST to resize. I have double buffered the control and I'm drawing a border. However, when resizing, I get medium flickering issues.
Here's the code for drawing:
if (_cachedPen == null)
{
_cachedPen = new Pen(BorderColor, VisibleBorderWidth);
}
Rectangle clientRectangle = ClientRectangle;
if ((BorderSides & BorderSides.Left) == BorderSides.Left)
{
e.Graphics.DrawLine(_cachedPen, clientRectangle.X, clientRectangle.Y, clientRectangle.X, clientRectangle.Height);
}
if ((BorderSides & BorderSides.Right) == BorderSides.Right)
{
e.Graphics.DrawLine(_cachedPen, clientRectangle.Width - VisibleBorderWidth, clientRectangle.Y, clientRectangle.Width - VisibleBorderWidth, clientRectangle.Height);
}
if ((BorderSides & BorderSides.Bottom) == BorderSides.Bottom)
{
e.Graphics.DrawLine(_cachedPen, clientRectangle.X, clientRectangle.Height - VisibleBorderWidth, clientRectangle.Width, clientRectangle.Height - VisibleBorderWidth);
}
if ((BorderSides & BorderSides.Top) == BorderSides.Top)
{
e.Graphics.DrawLine(_cachedPen, clientRectangle.X, clientRectangle.Y, clientRectangle.Width, clientRectangle.Y);
}
And for resizing:
case WM_NCHITTEST:
{
Point pos = PointToClient(new Point(m.LParam.ToInt32()));
if ((BorderSides & BorderSides.Left) == BorderSides.Left && pos.X < PhysicalBorderWidth)
{
m.Result = new IntPtr(HTLEFT);
}
if ((BorderSides & BorderSides.Bottom) == BorderSides.Bottom && pos.Y > Height - PhysicalBorderWidth)
{
m.Result = new IntPtr(HTBOTTOM);
}
if ((BorderSides & BorderSides.Right) == BorderSides.Right && pos.X > Width - PhysicalBorderWidth)
{
m.Result = new IntPtr(HTRIGHT);
}
if ((BorderSides & BorderSides.Top) == BorderSides.Top && pos.Y < PhysicalBorderWidth)
{
m.Result = new IntPtr(HTTOP);
}
return;
}
case 0x0014:
return;
default:
base.WndProc(ref m);
break;
Note that SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true); is on. However, when resizing, I get light-medium flickering. Is there any way to solve this?
Thanks
EDIT:
Also, just to note, it happens even when I don't cache the Pen objects.
EDIT:
Ok, so the painting is just called from OnPaint. The custom control just inherits from Control.
The definition of BorderSides is as follows:
[Flags]
internal enum BorderSides
{
None = 0,
Left = 1,
Right = 2,
Top = 4,
Bottom = 8
}
And here are the constants:
private const int WM_NCHITTEST = 0x0084;
private const int HTBOTTOM = 15;
private const int HTLEFT = 10;
private const int HTRIGHT = 11;
private const int HTTOP = 12;
EDIT:
This is for a docking control. It doesn't have a graphic background, and I intend to inherit from it. It might host controls, but not right now. I'm testing all 4 border sides, and all have slight flickering issues.
I am somewhat new to programming in general, but I am eager to learn more and I was wondering if anyone could possibly help me out with an idea.
(main goal)
I want to make a simple program that consists of a C# Windows Forms Application that displays a preset image (of 6000x6000 pixel dimensions, SizeMode set to Zoom so the entire image is visible on the form at once) in a PictureBox that will take up the entire form practically, save for a space at the bottom of the form where I want to display a TrackBar that will allow you to zoom the image in and out; as well as a horizontal scroll bar at the base of the PictureBox, and a vertical scroll bar on the right side of the PictureBox to scroll around the map when it is zoomed, and I wanted to be able to control these scroll bars by either clicking and dragging in a corresponding direction on the PictureBox (preferred but not sure if its possible) or by using the scroll wheel on the mouse (probably easier but once again not sure).
(reference)
[ Here is my form completed exactly as I described, with a 6000x6000 placement holder demo texture in a PictureBox using SizeMode Zoom, as an example - THIS HAS BEEN HANDLED, NEXT PART OF THE PROBLEM IS UPDATED BELOW:]
(addendum)
The only issue I am having is the code, as I am pretty much greenhorn in that department. I have been working to learn Visual Studio's workflow, but I really could use some help.
Thank you so much in advance for anything you can help me with.
UPDATE:
After doing research on the subject and taking time to do some thinking, I have come up with the code listed below; but my problem is that when I pan my image too far, the image is allowed to be pulled too far over, thus exposing the panel behind it when the image is panned/pulled too far over to one corner. Also, when I zoom too far out, the image is allowed to become WAY smaller than the Picturebox.
Panning issue, the grey parts of the panel are the problem
Zoom issue, the grey parts of the panel are the problem
So, my last question: How would I go about revising the code below to 'lock' the image that I am panning and zooming from being allowed to pan or zoom outside of its frame and expose the panel behind it?
public partial class ImageZoomMainForm : Form
{
Image img;
Point mouseDown;
int startx = 0;
int starty = 0;
int imgx = 0;
int imgy = 0;
bool mousepressed = false;
float zoom = 1;
public ImageZoomMainForm()
{
InitializeComponent();
string imagefilename = #"..\..\ViewPort_MAIN.tif";
img = Image.FromFile(imagefilename);
Graphics g = this.CreateGraphics();
zoom = ((float)pictureBox.Width / (float)img.Width) * (img.HorizontalResolution / g.DpiX);
pictureBox.Paint += new PaintEventHandler(imageBox_Paint);
}
private void pictureBox_MouseMove(object sender, EventArgs e)
{
MouseEventArgs mouse = e as MouseEventArgs;
if (mouse.Button == MouseButtons.Left)
{
Point mousePosNow = mouse.Location;
int deltaX = mousePosNow.X - mouseDown.X;
int deltaY = mousePosNow.Y - mouseDown.Y;
imgx = (int)(startx + (deltaX / zoom));
imgy = (int)(starty + (deltaY / zoom));
pictureBox.Refresh();
}
}
private void imageBox_MouseDown(object sender, EventArgs e)
{
MouseEventArgs mouse = e as MouseEventArgs;
if (mouse.Button == MouseButtons.Left)
{
if (!mousepressed)
{
mousepressed = true;
mouseDown = mouse.Location;
startx = imgx;
starty = imgy;
}
}
}
private void imageBox_MouseUp(object sender, EventArgs e)
{
mousepressed = false;
}
protected override void OnMouseWheel(MouseEventArgs e)
{
float oldzoom = zoom;
if (e.Delta > 0)
{
zoom += 0.1F;
}
else if (e.Delta < 0)
{
zoom = Math.Max(zoom - 0.1F, 0.01F);
}
MouseEventArgs mouse = e as MouseEventArgs;
Point mousePosNow = mouse.Location;
int x = mousePosNow.X - pictureBox.Location.X;
int y = mousePosNow.Y - pictureBox.Location.Y;
int oldimagex = (int)(x / oldzoom);
int oldimagey = (int)(y / oldzoom);
int newimagex = (int)(x / zoom);
int newimagey = (int)(y / zoom);
imgx = newimagex - oldimagex + imgx;
imgy = newimagey - oldimagey + imgy;
pictureBox.Refresh();
}
private void imageBox_Paint(object sender, PaintEventArgs e)
{
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.ScaleTransform(zoom, zoom);
e.Graphics.DrawImage(img, imgx, imgy);
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
const int WM_KEYDOWN = 0x100;
const int WM_SYSKEYDOWN = 0x104;
if ((msg.Msg == WM_KEYDOWN) || (msg.Msg == WM_SYSKEYDOWN))
{
switch (keyData)
{
case Keys.Right:
imgx -= (int)(pictureBox.Width * 0.1F / zoom);
pictureBox.Refresh();
break;
case Keys.Left:
imgx += (int)(pictureBox.Width * 0.1F / zoom);
pictureBox.Refresh();
break;
case Keys.Down:
imgy -= (int)(pictureBox.Height * 0.1F / zoom);
pictureBox.Refresh();
break;
case Keys.Up:
imgy += (int)(pictureBox.Height * 0.1F / zoom);
pictureBox.Refresh();
break;
case Keys.PageDown:
imgy -= (int)(pictureBox.Height * 0.90F / zoom);
pictureBox.Refresh();
break;
case Keys.PageUp:
imgy += (int)(pictureBox.Height * 0.90F / zoom);
pictureBox.Refresh();
break;
}
}
return base.ProcessCmdKey(ref msg, keyData);
}
private void ImageZoomMainForm_Load(object sender, EventArgs e)
{
}
}
}
A picturebox inside a Panel,
the panel should be set its AutoSroll to True,
the picturebox with SizeMode to Zoom
the trackbar change can increase and decrease the size of inside picturebox so that the outer panel will have auto scroll
dragging also possible using several mouse events of the picturebox.
I'm currently trying to add a resizable panel to my C# winforms project.
Currently i'm using this code to get what i want:
using System;
using System.Drawing;
using System.Windows.Forms;
class ResizablePanel : Panel
{
private const int grab = 16;
public ResizablePanel()
{
this.ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var rc = new Rectangle(this.ClientSize.Width - grab, this.ClientSize.Height - grab, grab, grab);
ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, rc);
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x84)
{
var pos = this.PointToClient(new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16));
if (pos.X >= this.ClientSize.Width - grab && pos.Y >= this.ClientSize.Height - grab)
m.Result = new IntPtr(17);
}
}
}
Its working fine but now i would like to limit a few things.
I dont want the panel to be smaller than 420x236.
I tried to set the MinimumSize but its ignoring that when i try to resize.
I want to keep an aspect ratio of 16:9.
How would i get that with the code above? Is there any way to do that?
Handle the WM_SIZING message, adopted from this answer.
if (m.Msg == 0x84)
{
var pos = this.PointToClient(new Point(m.LParam.ToInt32() & 0xffff, m.LParam.ToInt32() >> 16));
if (pos.X >= this.ClientSize.Width - grab && pos.Y >= this.ClientSize.Height - grab)
m.Result = new IntPtr(17);
}
else if (m.Msg == 0x216 || m.Msg == 0x214)
{
// WM_MOVING || WM_SIZING
// Keep the aspect and minimum size
RECT rc = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
int w = rc.Right - rc.Left;
int h = rc.Bottom - rc.Top;
w = w > 420 ? w : 420;
rc.Bottom = rc.Top + (int)(w * 9.0 / 16);
rc.Right = rc.Left + w;
Marshal.StructureToPtr(rc, m.LParam, false);
m.Result = (IntPtr)1;
return;
}
The RECT struct is defined as
[StructLayout(LayoutKind.Sequential)]
public struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
I also tried overriding OnResize event, which is much simpler, however, the panel is flickering when being resized.
protected override void OnResize(EventArgs eventargs)
{
base.OnResize(eventargs);
if (this.Width < 420)
this.Size = new Size(420, 236);
else
this.Size = new Size(this.Width, (int)(this.Width * 9.0 / 16));
}
Both approaches are actually the same thing, handling message queue is more low level "Win32-like" and overriding OnResize is "Windows Forms' way".
I want to have TextBox with bottom border but Graphics drawn for TextBox is distorted/broken on resize because of Color.Transparent.
Using an code I found, I was able to create a underlined TextBox (Drawn Rectangle with tranparent top, left, right). The problem is when I resize the form/window: when I resize it to smaller, then resize again to expand it, the graphics drawn is distorted. Any fix for this?
Here are photos: The second photo has been already resized smaller, then back to a larger size.
Here's the code:
[DllImport("user32")]
private static extern IntPtr GetWindowDC(IntPtr hwnd);
struct RECT {
public int left, top, right, bottom;
}
struct NCCALSIZE_PARAMS {
public RECT newWindow;
public RECT oldWindow;
public RECT clientWindow;
IntPtr windowPos;
}
float clientPadding = 0;
int actualBorderWidth = 2;
Color borderColor = Color.Black;
protected override void WndProc(ref Message m) {
//We have to change the clientsize to make room for borders
//if not, the border is limited in how thick it is.
if (m.Msg == 0x83) { //WM_NCCALCSIZE
if (m.WParam == IntPtr.Zero) {
RECT rect = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
rect.left += 2;
rect.right -= 2;
rect.top += 0;
rect.bottom -= 0;// (int)clientPadding;
Marshal.StructureToPtr(rect, m.LParam, false);
} else {
NCCALSIZE_PARAMS rects = (NCCALSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(NCCALSIZE_PARAMS));
rects.newWindow.left += (int)clientPadding;
rects.newWindow.right -= (int)clientPadding;
rects.newWindow.top += (int)clientPadding;
rects.newWindow.bottom -= 2;
Marshal.StructureToPtr(rects, m.LParam, false);
}
}
if (m.Msg == 0x85) {//WM_NCPAINT
IntPtr wDC = GetWindowDC(Handle);
using (Graphics g = Graphics.FromHdc(wDC)) {
ControlPaint.DrawBorder(g, new Rectangle(0, 0, Size.Width, Size.Height),
Color.Transparent, 1, ButtonBorderStyle.Solid,
Color.Transparent, 1, ButtonBorderStyle.Solid,
Color.Transparent, 1, ButtonBorderStyle.Solid,
borderColor, actualBorderWidth, ButtonBorderStyle.Solid);
}
return;
}
base.WndProc(ref m);
}
EDIT : I already found the issue, it's because of the Color.Transparent I fixed it by changing it to Color.White, since I have a white background. But then, that would not always be the case, how would I prevent that "Flickering/Tearing" while using Color.Transparent?
To have a TextBox with bottom border, The most simple workaround that I can offer is docking a 1 pixel height lable (or other control) to bottom of the TextBox:
using System.Drawing;
using System.Windows.Forms;
public class MyTextBox : TextBox
{
public MyTextBox()
{
BorderStyle = System.Windows.Forms.BorderStyle.None;
AutoSize = false; //Allows you to change height to have bottom padding
Controls.Add(new Label()
{ Height = 1, Dock = DockStyle.Bottom, BackColor = Color.Black });
}
}
I have a Rectangle Drawn on top of my Windows Form and I want to resize it using one of the handles provided !
Rectangle areaRect = new Rectangle(100,100, 300, 300);
Bool dragging = false;
Point ptOld = new Point(0, 0);
protected override void OnPaint(PaintEventArgs e)
{
Graphics dcPaint = e.Graphics;
dcPaint.DrawRectangle(rectPen, areaRect);
}
protected override void OnMouseDown(MouseEventArgs e)
{
ptOld = new Point(e.X, e.Y);
dragging = true;
}
protected override void OnMouseMove(MouseEventArgs e)
{
if(dragging = true)
{
Point ptNew = new Point(e.X, e.Y);
Int32 handleSelected = GetSelectedHandle(ptNew);
// Lets say I want to resize this rectangle using Handle 2 now.
if(handleSelected == 2)
{
// I am resizing this rectangle Width
areaRect.X += ptNew.X - ptOld.X;
areaRect.Width -= ptNew .X - ptOld.X;
this.Invalidate();
}
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
dragging = false;
}
It will give me an effect like this. Which is correct,
How ever I want to have a small tweak in this, I would like to change the height of this rectangle as well, When I am moving point 2, my point 7 should remain exactly as it is, Something like this...
Similarly when I moving point 4, my point 5 should intact and so on for point 7 and 2 also.
Any idea, how to proceed, because if I change the height, my point 7 location also gets changed ?
Drawing on a MouseMove like this is not going to be very smooth in WinForms.
You basically need a reference to the rectangle before you resize it.
I added the following code to keep track of the rectangle and the 8 draggable points:
private Point GetHandlePoint(int value) {
Point result = Point.Empty;
if (value == 1)
result = new Point(areaRect.Left, areaRect.Top);
else if (value == 2)
result = new Point(areaRect.Left, areaRect.Top + (areaRect.Height / 2));
else if (value == 3)
result = new Point(areaRect.Left, areaRect.Bottom);
else if (value == 4)
result = new Point(areaRect.Left + (areaRect.Width / 2), areaRect.Top);
else if (value == 5)
result = new Point(areaRect.Left + (areaRect.Width / 2), areaRect.Bottom);
else if (value == 6)
result = new Point(areaRect.Right, areaRect.Top);
else if (value == 7)
result = new Point(areaRect.Right, areaRect.Top + (areaRect.Height / 2));
else if (value == 8)
result = new Point(areaRect.Right, areaRect.Bottom);
return result;
}
private Rectangle GetHandleRect(int value) {
Point p = GetHandlePoint(value);
p.Offset(-2, -2);
return new Rectangle(p, new Size(5, 5));
}
Here is how I reworked your form code:
private Rectangle areaRect = new Rectangle(100, 100, 300, 300);
private Rectangle oldRect;
private int dragHandle = 0;
private Point dragPoint;
public Form1() {
InitializeComponent();
this.DoubleBuffered = true;
}
protected override void OnMouseDown(MouseEventArgs e) {
for (int i = 1; i < 9; i++) {
if (GetHandleRect(i).Contains(e.Location)) {
dragHandle = i;
oldRect = areaRect;
dragPoint = GetHandlePoint(i);
}
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e) {
if (dragHandle == 1) {
// to do
} else if (dragHandle == 2) {
int diff = dragPoint.X - e.Location.X;
areaRect = new Rectangle(oldRect.Left - diff, oldRect.Top, oldRect.Width + diff, oldRect.Height);
} else if (dragHandle == 7) {
int diff = dragPoint.X - e.Location.X;
areaRect = new Rectangle(oldRect.Left, oldRect.Top, oldRect.Width - diff, oldRect.Height);
}
if (dragHandle > 0)
this.Invalidate();
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e) {
dragHandle = 0;
base.OnMouseUp(e);
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.DrawRectangle(Pens.Red, areaRect);
for (int i = 1; i < 9; i++) {
e.Graphics.FillRectangle(Brushes.DarkRed, GetHandleRect(i));
}
base.OnPaint(e);
}
The posted code only does Points #2 and #7, but that should give you some logic to work with. I'm sure this code can be improved, it's just a working example.
Although this is a little old, this is the first (and only useful, as far as I found) result for this sort of a task.
I've used the above sample to enhance and implement a tested solution. In my case, I also wanted to have the rectangle to be strictly within another rectangle. Specifically, I was drawing it in a PictureBox and I wanted it to never get outside of the picture. This is what the max_width and max_height correspond to.
Note that it is a little funny sometimes - it re-sizes in the other direction when hitting minimum size in certain directions. I decided I like that behavior and that it should be a feature. :)
protected void pictureBox1_OnMouseMove(object sender, MouseEventArgs e)
{
// Where I started - where I stopped
int x_diff = dragPoint.X - e.Location.X;
int y_diff = dragPoint.Y - e.Location.Y;
// Minimum values
int small_offset = 5;
int left = small_offset;
int top = small_offset;
int width = small_offset;
int height = small_offset;
// Max values
int max_width = this.pictureBox1.Image.Width;
int max_height = this.pictureBox1.Image.Height;
if (dragHandle == 1)
{
left = Math.Max(oldRect.Left - x_diff, left);
top = Math.Max(oldRect.Top - y_diff, top);
width = Math.Min(Math.Max(oldRect.Width + x_diff, width), max_width - left - small_offset);
height = Math.Min(Math.Max(oldRect.Height + y_diff, height), max_height - top - small_offset);
}
else if (dragHandle == 2)
{
left = Math.Max(oldRect.Left - x_diff, left);
top = oldRect.Top;
width = Math.Min(Math.Max(oldRect.Width + x_diff, width), max_width - left - small_offset);
height = oldRect.Height;
}
else if (dragHandle == 3)
{
left = Math.Max(oldRect.Left - x_diff, left);
top = oldRect.Top;
width = Math.Min(Math.Max(oldRect.Width + x_diff, width), max_width - left - small_offset);
height = Math.Min(Math.Max(oldRect.Height - y_diff, height), max_height - top - small_offset);
}
else if (dragHandle == 4)
{
left = oldRect.Left;
top = Math.Max(oldRect.Top - y_diff, top);
width = oldRect.Width;
height = Math.Min(Math.Max(oldRect.Height + y_diff, height), max_height - top - small_offset);
}
else if (dragHandle == 5)
{
left = oldRect.Left;
top = oldRect.Top;
width = oldRect.Width;
height = Math.Min(Math.Max(oldRect.Height - y_diff, height), max_height - top - small_offset);
}
else if (dragHandle == 6)
{
left = oldRect.Left;
top = Math.Max(oldRect.Top - y_diff, top);
width = Math.Min(Math.Max(oldRect.Width - x_diff, width), max_width - left - small_offset);
height = Math.Min(Math.Max(oldRect.Height + y_diff, height), max_height - top - small_offset);
}
else if (dragHandle == 7)
{
left = oldRect.Left;
top = oldRect.Top;
width = Math.Min(Math.Max(oldRect.Width - x_diff, width), max_width - left - small_offset);
height = oldRect.Height;
}
else if (dragHandle == 8)
{
left = oldRect.Left;
top = oldRect.Top ;
width = Math.Min(Math.Max(oldRect.Width - x_diff, width), max_width - left - small_offset);
height = Math.Min(Math.Max(oldRect.Height - y_diff, height), max_height - top - small_offset);
}
if (dragHandle > 0)
{
areaRect = new Rectangle(left, top, width, height);
this.Invalidate();
}
}