i have used the user control. i have also set the minimum size to the user control. i have used the following codes to enable the resizing..
private const int cGrip = 16; // Grip size
private const int cCaption = 32; // Caption bar height;
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x84)
{
// Trap WM_NCHITTEST
System.Drawing.Point pos = new System.Drawing.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.MinimumSize.Width - cGrip && pos.Y >= this.MinimumSize.Height - cGrip)
{
m.Result = (IntPtr)17; // HTBOTTOMRIGHT
return;
}
}
base.WndProc(ref m);
}
But its not working to set the minimum size. its resizing to size as 0.please refer the below image. i want to set the minimum size to restrict the resizing. please suggest any solutions?
I haven't completely understood what you are trying to achieve. But, if you are populating your UserControl in a Windows Forms, simply set the MinimumSize property of the control. If that's not right, please explain more and share screenshots & code snippets in order to help us understand.
Related
I have created a custom Control called Ellipse. I'm able to resize, move, and paint this Ellipse. Now I'm trying to add undo/redo functionality for the resizing. The user can resize the control at the bottom right corner. At the moment the control prints hello as long as the cursor is positioned at the bottom right corner of the Control. But what I want is that when the user starts resizing (so leftmouse button is down and cursor is at the bottom right corner) hello is printed (only once). How to do this or is there a another (better) way to do it?
Ellipse.cs
class Ellipse : Control
{
private Point mDown { get; set; }
public Ellipse()
{
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Draw a black ellipse in the rectangle represented by the control.
e.Graphics.FillEllipse(Brushes.Black, 0, 0, Width, Height);
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
mDown = e.Location;
}
protected override void OnMouseMove(MouseEventArgs e)
{
// Call MyBase.OnMouseMove to activate the delegate.
base.OnMouseMove(e);
if (e.Button == MouseButtons.Left)
{
Location = new Point(e.X + Left - mDown.X, e.Y + Top - mDown.Y);
}
}
/* Allow resizing at the bottom right corner */
protected override void WndProc(ref Message m)
{
const int wmNcHitTest = 0x84;
const int htBottomLeft = 16;
const int htBottomRight = 17;
if (m.Msg == wmNcHitTest)
{
Console.WriteLine("Hello");
int x = (int)(m.LParam.ToInt64() & 0xFFFF);
int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16);
Point pt = PointToClient(new Point(x, y));
Size clientSize = ClientSize;
if (pt.X >= clientSize.Width - 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htBottomLeft : htBottomRight);
return;
}
}
base.WndProc(ref m);
}
I would try adding a couple more messages to check for the mouse going down in the non-client area and then another one for when the sizing was finished to complete the transaction:
private bool userResizing = false;
protected override void WndProc(ref Message m) {
const int wmNcHitTest = 0x84;
const int htBottomLeft = 16;
const int htBottomRight = 17;
const int WM_EXITSIZEMOVE = 0x232;
const int WM_NCLBUTTONDWN = 0xA1;
if (m.Msg == WM_NCLBUTTONDWN) {
if (!userResizing) {
userResizing = true;
Console.WriteLine("Start Resizing");
}
} else if (m.Msg == WM_EXITSIZEMOVE) {
if (userResizing) {
userResizing = false;
Console.WriteLine("Finish Resizing");
}
} else if (m.Msg == wmNcHitTest) {
int x = (int)(m.LParam.ToInt64() & 0xFFFF);
int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16);
Point pt = PointToClient(new Point(x, y));
Size clientSize = ClientSize;
if (pt.X >= clientSize.Width - 16 &&
pt.Y >= clientSize.Height - 16 &&
clientSize.Height >= 16) {
m.Result = (IntPtr)(IsMirrored ? htBottomLeft : htBottomRight);
return;
}
}
base.WndProc(ref m);
}
I have created two custom controls. A rectangle and an ellipse. I'm am able to move them by dragging with the mouse, but I also want to resize them. The rectangle resizing works fine, but resizing of the ellipse gives a strange effect. When I click on the ellipse after resizing and drag it again the ellipse looks normal again. Here the link with a gif showing what I mean by a 'strange' effect http://gyazo.com/319adb7347ed20fe28b6b93ced8744eb. How fix this effect? Also the ellipse has some white space in the corners because it's drawn in a rectangle shape, maybe there's a way to fix that too?
Control.cs
class Ellipse : Control
{
Point mDown { get; set; }
public Ellipse()
{
MouseDown += shape_MouseDown;
MouseMove += shape_MouseMove;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Draw a black ellipse in the rectangle represented by the control.
e.Graphics.FillEllipse(Brushes.Black, 0, 0, Width, Height);
//Set transparent background
SetStyle(ControlStyles.SupportsTransparentBackColor | ControlStyles.DoubleBuffer, true);
this.BackColor = Color.Transparent;
}
public void shape_MouseDown(object sender, MouseEventArgs e)
{
mDown = e.Location;
}
public void shape_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Location = new Point(e.X + Left - mDown.X, e.Y + Top - mDown.Y);
}
}
/* Allow resizing at the bottom right corner */
protected override void WndProc(ref Message m)
{
const int wmNcHitTest = 0x84;
const int htBottomLeft = 16;
const int htBottomRight = 17;
if (m.Msg == wmNcHitTest)
{
int x = (int)(m.LParam.ToInt64() & 0xFFFF);
int y = (int)((m.LParam.ToInt64() & 0xFFFF0000) >> 16);
Point pt = PointToClient(new Point(x, y));
Size clientSize = ClientSize;
if (pt.X >= clientSize.Width - 16 && pt.Y >= clientSize.Height - 16 && clientSize.Height >= 16)
{
m.Result = (IntPtr)(IsMirrored ? htBottomLeft : htBottomRight);
return;
}
}
base.WndProc(ref m);
}
}
}
Form1.cs
In this way I create the Ellipse's this code is from the panel_MouseUp (object sender, MouseEventArgs e) method.
case Item.Ellipse:
var el = new Ellipse();
panel.Controls.Add(el);
el.Location = new Point(x, y);
el.Width = (xe - x);
el.Height = (ye - y);
break;
You need to tell the control to repaint itself completely when it is resized. Set its ResizeRedraw property to true. You also need to be careful what you do in the Paint event handler, it should never have global state side-effects. As written, your control promptly crashed the designer when I tried it.
Remove the last 2 lines from OnPaint and make your constructor look like this:
public Ellipse() {
MouseDown += shape_MouseDown;
MouseMove += shape_MouseMove;
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.BackColor = Color.Transparent;
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
Further improve this by overriding OnMouseDown/Move() instead of using the events. And look at ControlPaint.DrawGrabHandle() to make the resizing a bit more intuitive.
In my form, I'm using a tabcontrol. I want to hide the tab headers and border both. I can do either one, if I try to hide the headers then the border becomes visible. Can anyone help me, please? thanks and here's my code:
public Form3()
{
InitializeComponent();
this.NativeTabControl1 = new NativeTabControl();
this.NativeTabControl1.AssignHandle(this.tabControl1.Handle);
}
private NativeTabControl NativeTabControl1;
private class NativeTabControl : NativeWindow
{
protected override void WndProc(ref Message m)
{
if ((m.Msg == TCM_ADJUSTRECT))
{
RECT rc = (RECT)m.GetLParam(typeof(RECT));
//Adjust these values to suit, dependant upon Appearance
rc.Left -= 3;
rc.Right += 3;
rc.Top -= 3;
rc.Bottom += 3;
Marshal.StructureToPtr(rc, m.LParam, true);
}
base.WndProc(ref m);
}
private const Int32 TCM_FIRST = 0x1300;
private const Int32 TCM_ADJUSTRECT = (TCM_FIRST + 40);
private struct RECT
{
public Int32 Left;
public Int32 Top;
public Int32 Right;
public Int32 Bottom;
}
private void Form3_Load(object sender, EventArgs e)
{
//hides tabcontrol headers
tabControl1.Appearance = TabAppearance.Buttons;
tabControl1.ItemSize = new Size(0, 1);
tabControl1.SizeMode = TabSizeMode.Fixed;
}
}
There is another thread on Stackoverflow, which provides some ideas to hide the tab row of the TabControl in Windows Forms.
My favorite one is to override WndProc and set the Multiline property to true.
public partial class TabControlWithoutHeader : TabControl
{
public TabControlWithoutHeader()
{
if (!this.DesignMode) this.Multiline = true;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x1328 && !this.DesignMode)
m.Result = new IntPtr(1);
else
base.WndProc(ref m);
}
}
I tested the code on Windows 8.1 and see neither the tabs nor a border line. So I think you do not need to use code like you posted.
I would usually suggest doing this in the xaml not C# (I'm a WPF developer) I believe you can do it in the C# as well by naming both the TabControl as well as each Tab itself.
tabControlName.BorderBrush = null;
///^Gets rid of the TabControl's border.
tabName1.Height = 0;
tabName2.Height = 0;
tabNameETC.Height = 0;
///^Removes the tabs(headers) if you have the TabControl.TabStripPlacement set to left
/// or right, then use the following instead:
tabName1.Width = 0
tabName2.Width = 0
tabNameETC.Width = 0
I'm creating a custom form (C#/Windows Forms/Vista/Windows7), and overriding WndProc to capture WM_NCPAINT, WM_NCCALCSIZE and WM_NCHITTEST to draw a custom frame. I'm almost done with it but there is a problem that I could not work around myself.
The problem is that NC_CALCSIZE makes my form shrink when I restore it after its maximized. I´ve googled it and found an answer from Bob Powell, and he stated that I neednt to handle NC_CALCSIZE when WPARAM is TRUE. After I've done that, WM_NCPAINT had no effect anymore (it does handle the WM_NCPAINT, but it does not paint the non-client area anymore, only after I invalidate it).
So, resuming, when I handle WM_NCCALCSIZE(WPARAM == TRUE) it shrinks my form, when I dont, it doesnt paint anymore.
Has anyone had this problem before? If more code is needed I can provide it.
Tks.
Here's my WN_CALCSIZE code:
private void WndProcNonClientCalcSize(ref Message m)
{
if (m.WParam == WinAPI.FALSE)
{
this.Log(MethodInfo.GetCurrentMethod(), "FALSE");
WinAPI.NCCALCSIZE_PARAMS csp;
csp = (WinAPI.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(WinAPI.NCCALCSIZE_PARAMS));
csp.rectProposed.Top += this._nonClientHeight;
csp.rectProposed.Bottom -= this._nonClientBorderThickness;
csp.rectProposed.Left += this._nonClientBorderThickness;
csp.rectProposed.Right -= this._nonClientBorderThickness;
Marshal.StructureToPtr(csp, m.LParam, false);
}
else if (m.WParam == WinAPI.TRUE)
{
this.Log(MethodInfo.GetCurrentMethod(), "TRUE");
WinAPI.NCCALCSIZE_PARAMS csp;
csp = (WinAPI.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(WinAPI.NCCALCSIZE_PARAMS));
csp.rectProposed.Top += this._nonClientHeight;
csp.rectProposed.Bottom -= this._nonClientBorderThickness;
csp.rectProposed.Left += this._nonClientBorderThickness;
csp.rectProposed.Right -= this._nonClientBorderThickness;
Marshal.StructureToPtr(csp, m.LParam, false);
}
m.Result = WinAPI.TRUE;
}
Here's my WM_NCPAINT code:
private bool WndProcNonClientPaint(ref Message m)
{
this.Log(MethodInfo.GetCurrentMethod(), string.Empty);
this.PaintNonClient(m.HWnd, (IntPtr)m.WParam);
m.Result = WinAPI.TRUE;
return true;
}
private void PaintNonClient(IntPtr hWnd, IntPtr hRgn)
{
WinAPI.RECT windowRect = new WinAPI.RECT();
WinAPI.GetWindowRect(hWnd, ref windowRect);
Rectangle bounds = new Rectangle(0, 0,
windowRect.Right - windowRect.Left,
windowRect.Bottom - windowRect.Top);
if (bounds.Width == 0 || bounds.Height == 0)
return;
Region clipRegion = new Region(bounds);
if (hRgn != (IntPtr)1)
clipRegion = Region.FromHrgn(hRgn);
WinAPI.DCV dcv =
WinAPI.DCV.WINDOW |
WinAPI.DCV.INTERSECTRGN |
WinAPI.DCV.CACHE |
WinAPI.DCV.CLIPSIBLINGS;
IntPtr hDC =
WinAPI.GetDCEx(
hWnd,
hRgn,
dcv);
if (hDC == IntPtr.Zero)
hDC = WinAPI.GetWindowDC(hWnd);
IntPtr compatiblehDC = WinAPI.CreateCompatibleDC(hDC);
IntPtr compatibleBitmap = WinAPI.CreateCompatibleBitmap(hDC, bounds.Width, bounds.Height);
try
{
WinAPI.SelectObject(compatiblehDC, compatibleBitmap);
WinAPI.BitBlt(compatiblehDC, 0, 0, bounds.Width, bounds.Height, hDC, 0, 0, WinAPI.SRCCOPY);
using (Graphics g = Graphics.FromHdc(compatiblehDC))
{
Rectangle outterEdge = new Rectangle(0, 0, this.Width, this.Height);
int x = this._nonClientBorderThickness;
int y = this._nonClientHeight;
int width = this.Width - (this._nonClientBorderThickness * 2);
int height = this.Height - this._nonClientBorderThickness - this._nonClientHeight;
Rectangle innerEdge = new Rectangle(x, y, width, height);
GraphicsPath path = new GraphicsPath();
path.AddRectangle(outterEdge);
path.AddRectangle(innerEdge);
using (SolidBrush brush = new SolidBrush(Color.FromArgb(45, 45, 48)))
g.FillPath(brush, path);
path.Dispose();
}
WinAPI.BitBlt(hDC, 0, 0, bounds.Width, bounds.Height, compatiblehDC, 0, 0, WinAPI.SRCCOPY);
}
finally
{
WinAPI.DeleteObject(compatibleBitmap);
WinAPI.DeleteDC(compatiblehDC);
}
}
Well I couldnt get it to work, it seems the return WM_NCCALCSIZE is more complex than I expected. I simply couldnt understand it, as it didnt do what i expected it to do. I tried to do what this article says but again it was no use:
http://blogs.msdn.com/b/oldnewthing/archive/2003/09/15/54925.aspx
So i googled again and I found this article on CodeProject, describing the same problem I had:
http://www.codeproject.com/Articles/55180/Extending-the-Non-Client-Area-in-Aero
The work around was to listen to WM_SYSCOMMAND and capture SC_RESTORE, setting my form width and height as I Maximized/Restored.
My WM_NCCALCSIZE became this:
private void WndProcNonClientCalcSize(ref Message m)
{
if (m.WParam == WinAPI.FALSE)
{
this.Log(MethodInfo.GetCurrentMethod(), "FALSE");
WinAPI.RECT rect = (WinAPI.RECT)Marshal.PtrToStructure(m.LParam, typeof(WinAPI.RECT));
rect.Left += this._nonClientBorderThickness;
rect.Top += this._nonClientHeight;
rect.Right -= this._nonClientBorderThickness;
rect.Bottom -= this._nonClientBorderThickness;
Marshal.StructureToPtr(rect, m.LParam, false);
m.Result = WinAPI.FALSE;
}
else if (m.WParam == WinAPI.TRUE)
{
this.Log(MethodInfo.GetCurrentMethod(), "TRUE");
WinAPI.NCCALCSIZE_PARAMS csp;
csp = (WinAPI.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(m.LParam, typeof(WinAPI.NCCALCSIZE_PARAMS));
WinAPI.RECT rectNewClient = csp.rectProposed;
rectNewClient.Left += this._nonClientBorderThickness;
rectNewClient.Top += this._nonClientHeight;
rectNewClient.Right -= this._nonClientBorderThickness;
rectNewClient.Bottom -= this._nonClientBorderThickness;
csp.rectProposed = rectNewClient;
csp.rectBeforeMove = csp.rectProposed;
Marshal.StructureToPtr(csp, m.LParam, false);
m.Result = (IntPtr)(WinAPI.NCCALCSIZE_RESULTS.ValidRects);
}
}
and my WM_SYSCOMMAND:
private void WndProcSysCommand(ref Message m)
{
UInt32 param;
if (IntPtr.Size == 4)
param = (UInt32)(m.WParam.ToInt32());
else
param = (UInt32)(m.WParam.ToInt64());
if ((param & 0xFFF0) == (int)WinAPI.SystemCommands.SC_RESTORE)
{
this.Height = this._storedHeight;
this.Width = this._storedWidth;
}
else if (this.WindowState == FormWindowState.Normal)
{
this._storedHeight = this.Height;
this._storedWidth = this.Width;
}
base.WndProc(ref m);
}
It might not be the best solution, but got the work done. If anyone could provide a better solution, I would really apreciate.
Tks, Hans Passant and Tergiver for your attention.
It's not problem with WM_NCCALCSIZE. When processing WM_NCCALCSISE, .NET call RestoreWindowBoundsIfNecessary() method, so the window's size changed (See Changing a window's restore position using SetWindowPlacement doesn't work on every window).
You can either manually cache form's size when form is minimized and reset when it's restored, or do not call base WndProc() method, which call RestoreWindowBoundsIfNecessary()
I have had different results and I'd actually like to get public input on this solution:
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
width += BORDER_LEFT_WIDTH + BORDER_RIGHT_WIDTH;
height += BORDER_TOP_HEIGHT + TITLEBAR_HEIGHT + BORDER_BOTTOM_HEIGHT;
base.SetBoundsCore(x, y, width, height, specified);
}
This, in effect, counteracts the shrinkage I was running into while dealing with a similar problem.
It looks like the problem has more elegant solution, though still hackish. See Custom window frame with DWM: how to handle WM_NCCALCSIZE correctly.
The difference is that solutions here either change window size causing redraw/flickering or artificially shrink bounds, while solution from the post above just say that your window does not have title/border hence size of the window and client area are equal.
By the way, if you want client area to occupy all window size WndProcNonClientCalcSize can be simplified to
private void WndProcNonClientCalcSize(ref Message m)
{
m.Result = IntPtr.Zero;
}
Is there anyway to control where you can move a form?
So if i move a form, it can only be moved on the vertical axis and when i try to move it horizontally, nothing happens.
I dont want a buggy implementation like locationchanged or move event and poping it back inline. I no there is a way using something like a WndProc override but after searching for a while, i couldnt find anything. Please help
For example:
using System.Runtime.InteropServices;
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x216) // WM_MOVING = 0x216
{
Rectangle rect =
(Rectangle) Marshal.PtrToStructure(m.LParam, typeof (Rectangle));
if (rect.Left < 100)
{
// compensates for right side drift
rect.Width = rect.Width + (100 - rect.Left);
// force left side to 100
rect.X = 100;
Marshal.StructureToPtr(rect, m.LParam, true);
}
}
base.WndProc(ref m);
}
The above code sets a minimum lefthand position of 100.
There is no need to recreate the RECT structure, like driis did, the .NET native Rectangle works fine. However, you have to set the location via the X property, since Left is a Get only property.
You would most likely want to override WndProc and handle the WM_MOVING message. According to MSDN:
The WM_MOVING message is sent to a
window that the user is moving. By
processing this message, an
application can monitor the position
of the drag rectangle and, if needed,
change its position.
This would be a way to do it, however, you would obviously need to tweek it for your needs:
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace VerticalMovingForm
{
public partial class Form1 : Form
{
private const int WM_MOVING = 0x0216;
private readonly int positionX;
private readonly int positionR;
public Form1()
{
Left = 400;
Width = 500;
positionX = Left;
positionR = Left + Width;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOVING)
{
var r = (RECT)Marshal.PtrToStructure(m.LParam, typeof(RECT));
r.Left = positionX;
r.Right = positionR;
Marshal.StructureToPtr(r, m.LParam, false);
}
base.WndProc(ref m);
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
}
}
VB.NET Version:
Protected Overloads Overrides Sub WndProc(ByRef m As Message)
If m.Msg = &H216 Then
' WM_MOVING = 0x216
Dim rect As Rectangle = DirectCast(Marshal.PtrToStructure(m.LParam, GetType(Rectangle)), Rectangle)
If rect.Left < 100 Then
' compensates for right side drift
rect.Width = rect.Width + (100 - rect.Left)
' force left side to 100
rect.X = 100
Marshal.StructureToPtr(rect, m.LParam, True)
End If
End If
MyBase.WndProc(m)
End Sub