I am writing a .net program using C#. I would like to ask how could I let the user resize
the Textbox, so that he could enlarge or diminish the textbox
This is very easy to do in Winforms, it takes but a few lines of code. Every window in Windows has the innate ability to be sizable by the user. This is normally only done for a top-level window (a form) but it works just as well for any client window.
The key is to respond to the WM_NCHITTEST message. Which is a message that Windows sends to a window when you click on it. It essentially asks "what was hit?" You can simply say "the corner was hit" and then Windows takes it from there. It changes the cursor to indicate that the corner can be dragged. And automatically resizes the window when the user moves the mouse.
Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox onto a form and set its Multiline property to true. Press F5 and drag the lower-right corner of the textbox to see it work.
using System;
using System.Drawing;
using System.Windows.Forms;
class SizeableTextBox : TextBox {
protected override void WndProc(ref Message m) {
base.WndProc(ref m);
// Intercept WM_NCHITTEST
if (m.Msg == 0x84 && this.Multiline) {
// Find out where the cursor is located
var pos = PointToClient(new Point(m.LParam.ToInt32()));
// Return HTBOTTOMRIGHT if in the lower-right corner
if (pos.X >= this.Width - 12 && pos.Y >= this.Height - 12) m.Result = (IntPtr)17;
}
}
}
There isn't a native property for textboxes that allow you to resize them manually. What you can do is set the dock or anchor property so that when the user resizes the form it will resize the textbox with it.
Related
I am trying to save the position of a custom dialog to the users registry, so that when they reload the same dialog, it appears in the same place they moved or resized it to previously.
I am saving the windows X position Y position Width and Height. It all works fine except for when the dialog is minimized. If the dialog is minimized and the user right clicks the dialogs representation on the taskbar (windows 7) they can click "close this window". Strangely, the number -32030 gets saved in the registry as the X and Y positions but the width and height get saved correctly. Any idea where this number comes from and what to do in this situation thx
You want something like this when you save the window position:
if (this.WindowState == WindowState.Normal)
{
Properties.Settings.Default.Top = Top;
Properties.Settings.Default.Left = Left;
Properties.Settings.Default.Height = Height;
Properties.Settings.Default.Width = Width;
}
else
{
Properties.Settings.Default.Top = RestoreBounds.Top;
Properties.Settings.Default.Left = RestoreBounds.Left;
Properties.Settings.Default.Height = RestoreBounds.Height;
Properties.Settings.Default.Width = RestoreBounds.Width;
// Check for WindowState.Maximized or WindowState.Minimized if you
// need to do something different for each case (e.g. store if application
// was Maximized
}
The important bit is the RestoreBounds which you need when the window is maximised or minimised. The code can probably be refactored to make it more efficient, but you get the idea.
I guess you are updating the window position when the window is closed? There are a couple of solutions if that is the case.
1) Save the window position on a different event, like when the window is resized or moved.
2) Check to see if the window is minimized before saving the X and Y positions.
Example:
switch (this.WindowState)
{
case WindowState.Maximized:
// don't update the X,Y
break;
case WindowState.Minimized:
// don't update the X,Y
break;
case WindowState.Normal:
// DO update the X,Y
break;
}
I would like to create a winform like this:
I already accomplished the visual effect (like as seen in the picture), by following other question. But I can't disallow resizing the form, since to have the border, it must be "Sizeable". Someone suggested putting Minimum Size and Maximum Size values equal to the current Form Size. This solves part of the issue, but when the mouse hovers the border, it still shows the double-ended arrow, suggesting the form is resizeable. Is there any way of disable this cursor change? My goal is to mimic the original systray popups in Windows 7, like the network, sound, etc.
Thank you!
Example code:
private const int WM_NCHITTEST = 0x84;
private const int HTCLIENT = 0x1;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_NCHITTEST:
m.Result = (IntPtr)HTCLIENT;
return;
}
base.WndProc(ref m);
}
This way, when the cursor hovers the borders, the pointer doesn't change, because it's treated as if it was inside the form, achieving the desired effect.
Add a message handler to your form and handle WM_NCHITTEST. When the original returns HTSIZE (etc.), return HTNONE or HTCAPTION.
Something like this question should get you started.
To explain:
When Windows wants to know which cursor to use for your window, it first sends you a WM_NCHITTEST message (non-client hit test). This message is handled by the WndProc method. Your window is supposed to return one of the HT* codes to tell Windows which part of the window the mouse is over. For example, return HTCAPTION for the caption area, HTCLIENT for the client area, or HTSIZENESW for the bottom left sizing corner. The default message handler (calling base.WndProc) deals with this for standard windows.
We don't have a standard window.
What we're trying to do here is ask the original window what the mouse is over. If it returns any of the HTSIZE* values, we want to replace that return value with HTNONE (for no action) or HTCLIENT (if you want the cursor to be treated as inside the window -- probably not this one) or HTCAPTION (if you want to be able to drag the window by the edges -- might be useful).
I have a form with a minimum height set, because I don't want it resized beyond a certain point when it is in a "minimalistic display" mode.
When a user attempts to maximize the window by Aero Snapping it to the top of the screen, the window gets maximized, but the height of the window is only 240 pixels (the set maximum size). If I attempt to handle the WM_SIZE message when the wParam is SIZE_MAXIMIZED, any attempt to set the height of the form is bypassed.
Currently I am handling SC_MAXIMIZE to detect when the maximize button is pushed, and WM_NCLBUTTONDBLCLK to maximize the window if the user double clicks the title bar. In both of these situations, I can toggle the extended window mode and set the minimum size such that it will be able to go full screen.
Of course neither of these messages are posted if the window is maximized via ShowWindow(SW_MAXIMIZE) or when aero-snapped to the top of the screen.
Is there another message I can handle that might occur just before the system actually does the maximization so I can adjust the window size and display mode before hand?
Current Code:
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0112) { // WM_SYSCOMMAND
if (m.WParam == new IntPtr(0xF030)) { // Maximize event - SC_MAXIMIZE from Winuser.h
// The window is being maximized
this.MaximumSize = new Size(9999, 9999);
ToggleDeviceDisplay(true);
linkToggleDeviceList.Visible = false;
}
} else if (m.Msg == 0x00A3) { // WM_NCLBUTTONDBLCLK - Double clicking on window title bar, min or max
if (this.WindowState == FormWindowState.Normal) {
if (grpDeviceList.Visible == false) {
this.MaximumSize = new Size(9999, 9999);
ToggleDeviceDisplay(true);
}
this.WindowState = FormWindowState.Maximized;
linkToggleDeviceList.Visible = false;
} else {
this.WindowState = FormWindowState.Normal;
linkToggleDeviceList.Visible = true;
}
return;
} else if (m.Msg == 0x0005) { // WM_SIZE
if (m.WParam == new IntPtr(0x02)) { // SIZE_MAXIMIZED
// CANT GET WINDOW TO GO TO FULL-SCREEN FROM HERE
this.MaximumSize = new Size(9999, 9999);
// THE LINE BELOW DOESN'T WORK, probably because it is already being sized
this.Height = Screen.FromHandle(this.Handle).WorkingArea.Size.Height;
} else if (m.WParam == new IntPtr(0x00)) { // SIZE_RESTORED
linkToggleDeviceList.Visible = true;
}
}
base.WndProc(ref m);
}
If the window is already in extended display mode when WM_SIZE maximize is sent, there is no problem because the max window size is set to allow full screen, however, if they attempt to maximize from the minimal mode, I can't get the app to switch to take up the full screen during the message.
I know I could trigger a timer or something to run from the message so it would resize so quickly the user wouldn't notice it wasn't full screen right away, but that is just a terrible hack.
EDIT:
To illustrate the two window states, I have uploaded two screenshots here. The top image shows the extended display, which has no limits on window size, the bottom image shows the minimal display, which has a height restriction set so they can't increase the height of the window as all it would do is show more empty space.
Thanks.
It seems to me like you're trying to do a simple thing in an overly complicated way. I would process the WM_GETMINMAXINFO message, which is sent to your window whenever its size or position is about to change. Handling this message gives you the opportunity to specify maximal or minimal values for each of these attributes, effectively preventing it from being made smaller or larger than you desire.
I won't post a lot of sample code because your question indicates you already know how to override WndProc and handle window messages. The only thing you'll need to do is define the MINMAXINFO structure in managed code. Something like this:
[StructLayout(LayoutKind.Sequential)]
struct POINT
{
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential)]
struct MINMAXINFO
{
public POINT ptReserved;
public POINT ptMaxSize;
public POINT ptMaxPosition;
public POINT ptMinTrackSize;
public POINT ptMaxTrackSize;
}
Use Marshal.PtrToStructure to convert the pointer contained in the Message.LParam property to an instance of the MINMAXINFO structure defined above. So, inside of your WndProc method, you'd do something like:
MINMAXINFO mmi = (MINMAXINFO)Marshal.PtrToStructure(msg.LParam, typeof(MINMAXINFO));
Update:
From the screenshots you've posted, it looks like the two different displays are identical, the only difference is whether the DataGridView at the bottom is shown. The GroupBox at the top is shown, regardless of the form's size.
Thus, it seems to me that the solution is simply to handle the Form.Resize event (which should be raised regardless of how your form is resized, whether via manually dragging its borders, clicking the caption bar buttons, or with Aero Snap).
Inside of that event handler method, check the current dimensions of your form. If it is sufficiently large, set the DataGridView control's Visible property to true. If it is not of sufficient size, switch to "minimal mode" by setting DataGridView.Visible = false.
It's not a very technically complicated solution, but it seems like it should accomplish all of your desired goals. The motivation as I understand it is just to provide a simpler interface when the form is too small to be able to see everything, and expand that interface when the form is larger. If you handle the Resize event and check the actual size of the form after that event has fired, you can't be wrong.
An alternative solution is just to enable the AutoScroll property and always show both controls. All the user will have to do is scroll up or down to see whatever they want. WinForms takes care of the rest.
Do I understand correctly: you want your window to have minimum allowed size and be able to be maximized? If that is so your code is far too complex and actually not necessary. Just use window's property MinimumSize.
So you want your form to have minimum size, maximum size and still be able to maximize it using title bar double click, maximize button and Aero snap? Tricky :-) Here is the solution.
Set you MinimumSize in properties and then write 2 events:
private void Form1_Resize(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Normal)
{
MaximumSize = new Size(maxWidth, maxHeight);
}
}
and:
private void Form1_ResizeEnd(object sender, EventArgs e)
{
MaximumSize = new Size(0, 0);
}
That will do the trick. At least works on my machine :-)
For one reason or another, I have a need to detect when the user actually clicked on the X button. What I have so far is this:
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)0xa1) //WM_NCLBUTTONDOWN
{
Point p = new Point((int)m.LParam);
p = this.PointToClient(p);
if (p.X > 680)
{
//do what I need to do...
}
}
base.WndProc(ref m);
}
Basically, I look out for the "WM_NCLBUTTONDOWN" message which is the Mouse Click on the Non Client Area of a window. Then, I get the X and Y coordinates from the LParam and finally I convert it to Screen Coordinates. So at this point, I know that the user clicked on the non client area and I know where on the Form.
My question is, how can I tell if these coordinates are on the X button. For now, I'm hardcoding 680 because that's what works in the current size of the window (it's not sizable) but the problem is I'm using Windows 7 which has bigger X buttons than XP, so obviously hardocding isn't a viable option. Furthermore, I haven't even coded for the Y coordinates, so if someone clicks on the right edge of the window, that triggers that code as well. So... anyone have any ideas?
Let's say you have an OK and a Cancel button, why don't you just set a value when one of these buttons is clicked. Then on the form's Closing event, if this value is not set, you know the X button has been clicked. Unless there are other ways of closing the form I'm not aware of...
Edit:
Instead of using a global boolean, you could change the DialogResult property of the form on your button clicks. I'm not sure what is the DialogResult value when you click the X button though, you'll have to try it.
If you test for the WM_NCHITTEST message that should tell you when the mouse is hovering over the close button.
I have to fix some problems and enchance form designer written long ago for a database project.
In Design Panel class code I encountered these lines
private void DesignPanel_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
(sender as Control).Capture = false;
switch (FMousePosition)
{
case MousePosition.mpNone:
SendMessage((sender as Control).Handle, WM_SYSCOMMAND, 0xF009, 0);
break;// Move
case MousePosition.mpRightBottom:
SendMessage((sender as Control).Handle, WM_SYSCOMMAND, 0xF008, 0);
break;//RB
case MousePosition.mpLeftBottom:
SendMessage((sender as Control).Handle, WM_SYSCOMMAND, 0xF007, 0);
// ... here are similar cases ...
case MousePosition.mpLeft:
SendMessage((sender as Control).Handle, WM_SYSCOMMAND, 0xF001, 0);
break;//L
}
}
}
FMousePosition indicates whether mouse was over any edge of selected control.
What confusing me is these windows messages: it seems there is no documentation on WM_SYSCOMMAND with parameters 0xF001-0xF009 (maybe it starts some kind of 'drag/resize sequence'). Any ideas?
If my suggestion is right, then how can I cancel these sequences?
They are undocumented parameters. After searching I managed to find this list.
0xF000 (SC_SIZE, Center cursor on the form)
0xF001 (SC_SZLEFT, Resize from left)
0xF002 (SC_SZRIGHT, Resize from right)
0xF003 (SC_SZTOP, Resize from top)
0xF004 (SC_SZTOPLEFT, Lock the bottom right corner of the form, the top left corner move for resize)
0xF005 (SC_SZTOPRIGHT, Same from bottom left corner)
0xF006 (SC_SZBOTTOM, Lock top right and left border, resize bottom)
0xF007 (SC_SZBOTTOMLEFT, Lock top and right border, resize other border)
0xF008 (SC_SZBOTTOMRIGHT, Lock left and top border and resize other)
0xF009 (SC_SIZE|0x9, Drag from anywhere)
0xF00F (SC_SEPARATOR)
0xF010 (SC_MOVE, Put cursor centered at the upper order)
0xF012 (SC_DRAGMOVE, move by dragging)
0xF020 (SC_MINIMIZE, Auto-Minimize Form)
0xF030 (SC_MAXIMIZE, Auto-Maximize Form)
0xF040 (SC_NEXTWINDOW, Stop! You don't want that, it will lock all mouse click and make you reboot)
0xF148 (SC_SCREENSAVE|0x8, Activate ScreenSaver)
0xF13E (SC_TASKLIST|0xE, Activate StartButton)
Reference: http://www.delphi3000.com/articles/article_1054.asp#Comments
Based on my Win32 Programming (Rector and Newcomer) p902-903 explains WM_SYSCOMMAND is sent when the user selects an item from the system menu (rather than sending the normal WM_COMMAND).
The MSDN help says SC_SIZE = 0xF000 and it and Win32 Programming also say Windows uses the four low-order bits of the predefined system menu IDs internally but doesn't go on to clarify their use. Thanks stukelly for clarifying them.