How to make a window Draggable(C# Winforms)? - c#

I have form. I have enabled the transparency on the form and I have removed it's Title Bar and Border. Inside that i have created a Custom UI, Which have the same features like a window. Basically, my idea is to create custom window.
Everything is working as expected but only the windows dragging is not working. I am not sure how to enable it. I googled for this. But i didn't find any useful info for me.
Please help me to implement this window dragging.

I've implemented this behavior by capturing mousedown (uncapture on mouseup), and then mousemove.
Just move the form co-ordinates (left, top), equivalent amounts to the mouse movement (those events have the amount the mouse moved).
This worked fine for me.

class YourForm : Form
{
private const int WM_NCHITTEST = 0x84;
private const int HTCLIENT = 0x1;
private const int HTCAPTION = 0x2;
///
/// Handling the window messages
///
protected override void WndProc(ref Message message)
{
base.WndProc(ref message);
if (message.Msg == WM_NCHITTEST && (int)message.Result == HTCLIENT)
message.Result = (IntPtr)HTCAPTION;
}
}

The easiest way is to process WM_NCHITTEST message and return HTCAPTION for the portions of your custom window which work like the title bar does in a normal window. Windows will do the rest.

Related

Disabling maximizing form in C# [duplicate]

I'm annoyed that the I'm promised a fixed window that the user can't resize, but then of course they're allowed to double click the title bar to maximize this 'unresizable' window. How can I turn this off? Can I do it with winforms code, or must I go down to Win32?
Thanks!
You could set the MaximizeBox property of the form to false
You can disable the double-click message on a title bar in general (or change the default behavior which is maximizing the window). it works on any FormBorderStyle:
private const int WM_NCLBUTTONDBLCLK = 0x00A3; //double click on a title bar a.k.a. non-client area of the form
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_NCLBUTTONDBLCLK)
{
m.Result = IntPtr.Zero;
return;
}
base.WndProc(ref m);
}
MSDN Source
Cheers!
///
/// This is we are overriding base WIN32 window procedure to prevent the form from being moved by the mouse as well as resized by the mouse double click.
///
///
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MOVE = 0xF010;
const int WM_NCLBUTTONDBLCLK = 0x00A3; //double click on a title bar a.k.a. non-client area of the form
switch (m.Msg)
{
case WM_SYSCOMMAND: //preventing the form from being moved by the mouse.
int command = m.WParam.ToInt32() & 0xfff0;
if (command == SC_MOVE)
return;
break;
}
if(m.Msg== WM_NCLBUTTONDBLCLK) //preventing the form being resized by the mouse double click on the title bar.
{
m.Result = IntPtr.Zero;
return;
}
base.WndProc(ref m);
}
I know I'm late to the party, May help someone who is searching for the same.
private const int WM_NCLBUTTONDBLCLK = 0x00A3; //double click on a title bar a.k.a. non-client area of the form
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
switch (msg)
{
case WM_NCLBUTTONDBLCLK: //preventing the form being resized by the mouse double click on the title bar.
handled = true;
break;
default:
break;
}
return IntPtr.Zero;
}
I just checked it in VB.Net. Below code worked for me.
Private Const Win_FormTitleDoubleClick As Integer = 163
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = Win_FormTitleDoubleClick Then
m.Result = IntPtr.Zero
Return
End If
MyBase.WndProc(m)
End Sub
Note: 163 is the event code
I approached the problem slightly differently. First I removed the minimize and maximize options from the control box via the MaximizeBox and MinimizeBox properties as you'd expect.
Then I added the following OnResizeEnd() event and attached it to the Form's ResizeEnd event handler:
/// <summary>Locks the form to fill the screen that it's placed on and remain in that state as long as it's open.</summary>
private void GuiMain_ResizeEnd( object sender, EventArgs e )
{
this.Location = Screen.WorkingArea.Location;
this.Size = Screen.WorkingArea.Size;
this.MaximizedBounds = Screen.WorkingArea;
this.MinimumSize = Screen.WorkingArea.Size;
this.WindowState = FormWindowState.Normal;
}
This solution necessarily relies on the existence of the following accessor which you can copy, or you can simply replace each instance of Screen. in the above with Screen.FromHandle( this.Handle ).
protected Screen Screen => Screen.FromHandle( this.Handle );
Obviously, if ironically, this actually keeps the form in the FormWindowState.Normal state, but it mimics the effect of maximization, and resets this fullscreen state after any attempt to change it.
Interestingly, due to the use of the Screen.FromHandle() settings (as opposed to hard-coded ones), you can actually drag the form from one display to another, whereupon it immediately "snaps" to fill THAT screen instead. Something I found quite handy, but which may require additional code to correct if you don't want that functionality in your application.

Make MouseWheel event pass from child to parent

I have a Panel where AutoScroll is true. This panel contains many smaller panels that fill all available space like tiles. When there are too many sub-panels to display I get the vertical scroll bar as expected.
Each of these "tiles" have some event handlers tied to them to handle MouseDown / MouseUp / MouseMove as they can be dragged around.
The problem I'm having is that mouse wheel scroll doesn't work on the parent panel as it won't have focus. I can't give it focus because in all probability I'll be scrolling while moving a sub-panel which will have the focus instead, and even then that would require workarounds since panels don't like focus.
I've been trying (and failing) to find a way to propagate only mousewheel events from the children to the parent.
I've read that in Winforms if a control cannot handle a mouse event it will bubble it up to the parent of that control, and then to the parent of that control and so on until it finds a suitable handler.
With this in mind I figured the best solution would be to use WndProc to override all scroll related events on the sub-panel and pass them to the parent while leaving all other events intact but admittedly this isn't my strong suit and I'm lost.
I've tried a few other solutions such as making the sub-panels invisible to all mouse events but as you might have guessed this was bad. I've read about implementing a message filter but didn't understand it.
Here's the code that will give you a very basic example of the panel and its children:
private void Form1_Load(object sender, EventArgs e)
{
Height = 600;
Width = 300;
Color[] colors = new Color[]{ Color.PowderBlue, Color.PeachPuff };
Panel panel = new Panel()
{
Height = this.ClientSize.Height - 20,
Width = 200,
Top = 10,
Left = 10,
BackColor = Color.White,
BorderStyle = BorderStyle.FixedSingle,
AutoScroll = true
};
for (int i = 0; i < 10; i++)
{
Panel subPanel = new Panel()
{
Name = #"SubPanel " + i.ToString(),
Height = 100,
Width = panel.Width - System.Windows.Forms.SystemInformation.VerticalScrollBarWidth - 2,
BackColor = colors[i % 2],
Top = i * 100
};
subPanel.MouseClick += subPanel_MouseClick;
panel.Controls.Add(subPanel);
}
Controls.Add(panel);
}
void subPanel_MouseClick(object sender, MouseEventArgs e)
{
Panel panel = sender as Panel;
Text = panel.Name;
}
Here's my attempt at overriding WndProc in a custom panel:
class NoScrollPanel : Panel
{
private const int WM_HSCROLL = 0x114;
private const int WM_VSCROLL = 0x115;
private const int MOUSEWHEEL = 0x020A;
private const int KEYDOWN = 0x0100;
protected override void WndProc(ref Message m)
{
if ((m.HWnd == Handle) && (m.Msg == MOUSEWHEEL || m.Msg == WM_VSCROLL || (m.Msg == KEYDOWN && (m.WParam == (IntPtr)40 || m.WParam == (IntPtr)35))))
{
PostMessage(Parent.Handle, m.Msg, m.WParam, m.LParam);
}
else
{
base.WndProc(ref m);
}
}
[DllImport("User32.dll")]
private static extern IntPtr PostMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
}
Any help or alternative approaches are most welcome. Thanks!
At my side #a-clymer's solution does not work, perhaps different environment. Currently there is no direct, clear answer for my problem so I try to combine several thoughts of other professionals and succeed.
In my current project, I have a few input controls contained in a Panel. I managed to make the mouse wheel scroll the panel instead of the ComboBox items by creating a child class of ComboBox and override its WndProc:
public class ComboBoxWithParentMouseWheel : ComboBox
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
const int WM_MOUSEWHEEL = 0x020A;
//thanks to a-clymer's solution
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEWHEEL)
{
//directly send the message to parent without processing it
//according to https://stackoverflow.com/a/19618100
SendMessage(this.Parent.Handle, m.Msg, m.WParam, m.LParam);
m.Result = IntPtr.Zero;
}else base.WndProc(ref m);
}
}
All credit goes to Hans Passant (again), taken from the thread he suggested: https://stackoverflow.com/a/3562449/17034
Allowing the the containing panel to take focus worked fine. For the demo code above the class needs no changes, just use it for only the containing panel. I had to make some tweaks to my project to call focus when necessary, but it was far from rocket science.
Thanks again.
I could not get any of these solutions to work. I am overriding the WndProc fxn just like you. Finally found the solution! I noticed that if the container holding my TextBox was in focus, then the scroll worked, just not when the TextBox was in focus.
I didn't need to change the transparency of the event, I needed to send the event to a different Control Handle! (It seems so simple now, but I've trying to figure this out for days!)
internal class myTextBox : TextBox
{
const int WM_MOUSEWHEEL = 0x020A;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEWHEEL)
m.HWnd = this.Parent.Handle; // Change the Handle of the message
base.WndProc(ref m);
}
}
I haven't seen any mention of this method in all my Google searches, if there is some reason NOT to do this I hope someone will reply.
An improved version of #a-clymer version that works better.
const int WM_MOUSEWHEEL = 0x020A;
if (m.Msg == WM_MOUSEWHEEL)
{
// find the first scrollable parent control
Control p = this;
do
{
p = p.Parent;
} while (p != null && !(p is ScrollableControl));
// rewrite the destination handle of the message
if (p != null)
m.HWnd = p.Handle;
}

How to position a form on top of another form without taking the focus

My application needs to display a popup near the cursor position of any active application when certain conditions are met (known by my app).
So I would like to display the form (without stealing the focus from the active application). I tried using ShowWindow with the SW_SHOWNOACTIVATE param but, this way, the my form is displayed under the currently active form.
How can I force my form to be displayed on top of any form active on the screen without stealing the input focus?
Thanks.
What you want is to use the TopMost property of the form you want to stay on top.
You need to add a bit of plumbing to the pop-up form so you can override the WM_ACTIVATE message:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_ACTIVATE)
{
if (((int)m.WParam & 0xFFFF) != WA_INACTIVE)
{
if (m.LParam != IntPtr.Zero)
{
SetActiveWindow(m.LParam);
}
else
{
// Could not find sender, just in-activate it.
SetActiveWindow(IntPtr.Zero);
}
}
}
base.WndProc(ref m);
}
Make sure you add the following to your pop-up form as well:
[DllImport("user32.dll")]
private extern static IntPtr SetActiveWindow(IntPtr handle);
private const int WM_ACTIVATE = 6;
private const int WA_INACTIVE = 0;
You can use the pop-up form as you would any other, by calling Show() on it. You can make it the top-most window through the TopMost property, as with other forms.

Show a taskbar item with a NativeWindow

My application is intended to work almost entirely through a Windows 7 taskbar item with the use of thumbnails and jump lists. I know I can easily create a Form and simply hide it, but this seems like overkill. Plus, I'd like to toy around with NativeWindow as much as possible, because I've never used it before.
Essentially, I have a class called RootWindow that derives from NativeWindow that will handle hotkeys and hopefully everything else. I don't need a visible window at all, but simply something to process window messages and provide a taskbar item that I can attach thumbnails and jump lists to.
Is there some kind of special CreateParams option I need to pass to CreateHandle? Or am I out of luck?
EDIT: Well, it was easier than I thought it would be, although it's not exactly what I want. Once I passed the NativeWindow's handle to the ShowWindow API, the taskbar item appeared. However, it also shows a window in the upper left corner of the screen. Is there some way to get rid of that window while still showing the taskbar item?
public class RootWindow : NativeWindow {
public const int SW_SHOWNOACTIVATE = 4;
[DllImport("User32.dll")]
private static extern int ShowWindow(IntPtr hWnd, short cmdShow);
public RootWindow() {
CreateHandle(new CreateParams());
ShowWindow(this.Handle, SW_SHOWNOACTIVATE);
}
}
The trick was to set the window's style to WS_POPUP.
const int WS_POPUP = unchecked((int)0x80000000);
const int SW_SHOWNOACTIVATE = 4;
CreateHandle(new CreateParams() {
Style = WS_POPUP
});
ShowWindow(Handle, SW_SHOWNOACTIVATE);
I also disabled Aero Peek for the window, since it's just for background work.
const int DWMNCRP_ENABLED = 2;
const int DWMWA_DISALLOW_PEEK = 11;
int policy = DWMNCRP_ENABLED;
DwmSetWindowAttribute(Handle, DWMWA_DISALLOW_PEEK, ref policy, sizeof(int));

Make a form not focusable in C#

I'm wanting to write a virtual keyboard, like windows onscreen keyboard for touchscreen pcs.
But I'm having problem with my virtual keyboard stealing the focus from the application being used. The windows onscreen keyboard mantains the focus on the current application even when the user clicks on it. Is there a way to do the same with windows forms in C#?
The only thing I can do for now is to send a keyboard event to an especific application, like notepad in the following code. If I could make the form not focusable, I could get the current focused window with GetForegroundWindow.
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName,string lpWindowName);
[DllImport("USER32.DLL")]
public static extern bool SetForegroundWindow(IntPtr hWnd);
private void button1_Click(object sender, EventArgs e)
{
IntPtr calculatorHandle = FindWindow("notepad", null);
SetForegroundWindow(calculatorHandle);
SendKeys.SendWait("111");
}
Is there a way this can be done? Any suggestions of a better way to have the form sending keyboard events to the application being used?
Thanks!!
Its solved!
I've tried the solution from gehho, but I also needed to override the CreateParams method:
private const int WS_EX_NOACTIVATE = 0x08000000;
protected override CreateParams CreateParams
{
get
{
var createParams = base.CreateParams;
createParams.ExStyle |= WS_EX_NOACTIVATE;
return createParams;
}
}
Instead of trying to reset the active window after your one has been clicked, I would rather try to prevent your window from receiving focus/being activated.
Have a look at this article. At the end, the author briefly explains how this can be done:
How can I prevent my window from getting activation and focus when shown?
In Windows Forms 2.0 there is a new property called
ShowWithoutActivation – which you would need to override on the Form.
In native applications you can use SetWindowPos with the
SWP_NOACTIVATE flag or the ShowWindow with the SW_SHOWNA flag.
Furthermore, in this article he provides a code example for Windows Forms:
If you want a full-on form, you can now override a property called
ShowWithoutActivation:
public class NoActivateForm : Form
{
protected override bool ShowWithoutActivation => true;
}
Keep in mind this does not “prevent” activation all the time – you can
still activate by calling the Activate(), Focus()… etc methods. If
you want to prevent clicks on the client area from activating the
window, you can handle the WM_MOUSEACTIVATE message.
private const int WM_MOUSEACTIVATE = 0x0021, MA_NOACTIVATE = 0x0003;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEACTIVATE)
{
m.Result = (IntPtr)MA_NOACTIVATE;
return;
}
base.WndProc(ref m);
}

Categories

Resources