Moving 2 forms at the same time - c#

I'm stuck a little bit here. I'm trying to move 2 forms at the same time without using OnMove, LocationChanged, Docking etc.
The only way to interact with their locations is to override WndProc. Something which might be helpful is that form A is owner of form B. So whenever A is moved I want to move B as well. Not to the same location but the same distance.
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0084)
{
Form[] temp = this.OwnedForms;
if(temp.Length > 0)
{
/* moving temp[0] to the same ratio as this form */
}
m.Result = (IntPtr)2;
return;
}
base.WndProc(ref m);
}
Both A and B have the same WndProc since they are 2 objects from the same class.

It doesn't make any sense to avoid using the LocationChanged event:
private Point lastPos;
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
lastPos = this.Location;
}
protected override void OnLocationChanged(EventArgs e) {
base.OnLocationChanged(e);
foreach (var frm in this.OwnedForms) {
frm.Location = new Point(frm.Location.X + this.Left - lastPos.X,
frm.Location.Y + this.Top - lastPos.Y);
}
lastPos = this.Location;
}
protected override void WndProc(ref Message m) {
// Move borderless window with click-and-drag on client window
if (m.Msg == 0x84) m.Result = (IntPtr)2;
else base.WndProc(ref m);
}

I managed to solve the problem:
protected override void WndProc(ref Message m)
{
Form temp = this.Owner;
if (m.Msg == 0x0084)
{
m.Result = (IntPtr)2;
return;
}
if (m.Msg == 0x0216 && temp != null)
{
if (!movedonce)
{
oldlocationx = this.Location.X;
oldlocationy = this.Location.Y;
movedonce = true;
}
temp.Location = new Point(temp.Location.X + this.Location.X - oldlocationx, temp.Location.Y + this.Location.Y - oldlocationy);
oldlocationx = this.Location.X;
oldlocationy = this.Location.Y;
}
base.WndProc(ref m);
}

Related

Set SizeAll cursor while moving control by handling NC_HITTEST

I wrote the WndProc method for a moveable control such this:
protected override void WndProc(ref Message m)
{
const int WM_NCHITTEST = 0x0084;
if (m.Msg == WM_NCHITTEST)
{
base.WndProc(ref m);
if ((int)m.Result == 0x1)
m.Result = (IntPtr)0x2;
return;
}
base.WndProc(ref m);
}
and setted SizeAll cursor for the cursor property. but when we set m.Result as i did, the cursor will be Default in any case. How can i do?
You should handle WM_SETCURSOR too.
Also you may want to hanlde WM_NCLBUTTONDBLCLK to prevent your control from being maximized when you double click on it:
protected override void WndProc(ref Message m)
{
const int WM_NCHITTEST = 0x84;
const int WM_SETCURSOR = 0x20;
const int WM_NCLBUTTONDBLCLK = 0xA3;
const int HTCAPTION = 0x2;
if (m.Msg == WM_NCHITTEST)
{
base.WndProc(ref m);
m.Result = (IntPtr)HTCAPTION;
return;
}
if (m.Msg == WM_SETCURSOR)
{
if ((m.LParam.ToInt32() & 0xffff) == HTCAPTION)
{
Cursor.Current = Cursors.SizeAll;
m.Result = (IntPtr)1;
return;
}
}
if ((m.Msg == WM_NCLBUTTONDBLCLK))
{
m.Result = (IntPtr)1;
return;
}
base.WndProc(ref m);
}

ListView ContextMenuStrip for column headers

I display a different ContextMenuStrip when I right click the ListView column header, and another inside ListView.
class ListViewExx : ListView
{
public ContextMenuStrip HeaderContextMenu { get; set; }
int contextMenuSet = 0;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
base.WndProc(ref m);
switch(m.Msg)
{
case 0x210: //WM_PARENTNOTIFY
contextMenuSet = 1;
break;
case 0x21: //WM_MOUSEACTIVATE
contextMenuSet++;
break;
case 0x7b: //WM_CONTEXTMENU
if(contextMenuSet == 2 && HeaderContextMenu != null)
HeaderContextMenu.Show(Control.MousePosition);
break;
}
}
}
This works very well. The problem is the FIRST TIME I right click inside the ListView - the headers contextMenuStrip is shown.
Relying on the activation state is too hacky. It is far simpler, the WM_CONTEXTMENU message passes the handle of the window that generated the message. So you can simply compare it to the handle of the listview. If it doesn't match then you know it was the header control:
protected override void WndProc(ref System.Windows.Forms.Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x7b) { //WM_CONTEXTMENU
if (m.WParam != this.Handle) HeaderContextMenu.Show(Control.MousePosition);
}
}
Technically you should use LVM_GETHEADER but this should work just fine.
I've tried finding a clean way to get Column Header Rectangle of a ListView to check if the Point at which user right-clicks is in a Column Header or not. However, I've just found that the Column Header Rectangle of a ListView seems to be revealed only in a DrawColumnHeader event handler. This solution is all what I can think of to help you out:
public class CustomListView : ListView
{
//This contains the Column Index and its corresponding Rectangle in screen coordinates.
Dictionary<int, Rectangle> columns = new Dictionary<int, Rectangle>();
public CustomListView()
{
OwnerDraw = true;//This will help the OnDrawColumnHeader be called.
}
protected override void OnDrawItem(DrawListViewItemEventArgs e)
{
e.DrawDefault = true;
base.OnDrawItem(e);
}
protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
{
e.DrawDefault = true;
base.OnDrawSubItem(e);
}
protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
{
columns[e.ColumnIndex] = RectangleToScreen(e.Bounds);
e.DrawDefault = true;
base.OnDrawColumnHeader(e);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x7b)//WM_CONTEXTMENU
{
int lp = m.LParam.ToInt32();
int x = ((lp << 16) >> 16);
int y = lp >> 16;
foreach (KeyValuePair<int, Rectangle> p in columns)
{
if (p.Value.Contains(new Point(x, y)))
{
//MessageBox.Show(Columns[p.Key].Text); <-- Try this to test if you want.
//Show your HeaderContextMenu corresponding to a Column here.
break;
}
}
}
base.WndProc(ref m);
}
}

How Can I Move The Window Like I Want With WinApi

When I try to drag my window with this the window jumps and flickers around:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOVE)
{
int x = (m.LParam.ToInt32() & 0xffff);
int y = ((m.LParam.ToInt32() >> 16) & 0xffff);
if (x < 500)
Location = new Point(0, y);
else
base.WndProc(ref m);
}
else
base.WndProc(ref m);
}
must stop jumping
WM_MOVE, WM_MOVING, WM_WINDOWPOSCHANGING or other move event must continue firing while dragging the window because I want every new position to be checked.
another problem is Location = new Point(0, y); fires another move event (this one should be ignored)
Please help!
Here's an example of using WM_WINDOWPOSCHANGING and modifying the WINDOWPOS structure:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public uint flags;
}
public const int WM_WINDOWPOSCHANGING = 0x46;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_WINDOWPOSCHANGING:
WINDOWPOS wp = (WINDOWPOS)System.Runtime.InteropServices.Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));
if (true) // ... if somecondition ...
{
// modify the location with x,y:
wp.x = 0;
wp.y = 0;
}
System.Runtime.InteropServices.Marshal.StructureToPtr(wp, m.LParam, true);
break;
}
base.WndProc(ref m);
}
}
This should do what you want:
protected override void WndProc(ref Message message)
{
const Int32 WM_SYSCOMMAND = 0x0112;
const Int32 SC_MOVE = 0xF010;
switch (message.Msg)
{
case WM_SYSCOMMAND:
Int32 command = message.WParam.ToInt32() & 0xfff0;
if (command == SC_MOVE)
{
Left = 0;
Top = 0;
return;
}
break;
}
base.WndProc(ref message);
}
Hans Passant suggested the LocationChanged event, and it can work quite nicely. In this example, the window is frozen at 0, 0 until the mouse pointer gets outside of a 500, 500 box:
private void Form1_LocationChanged(Object sender, EventArgs e)
{
if (MousePosition.X > 500 || MousePosition.Y > 500)
Location = MousePosition;
else
Location = new Point(0, 0);
}

Setting and using MousePosition

Hey, i have kinda silly problem, i need to set Point Pos as mouse current position in one if statement and in another statement move mouse cursor to setted position. First i need assign global Point variable but then Cursor moves to assigned variable and i don't want that happens.
Part of source:
protected override void WndProc(ref Message m)
{
Point Pos = new Point(0, 0);
if (m.Msg == 0x0312)
{
int id = m.WParam.ToInt32();
if (id == 0)
{
Pos.X = MousePosition.X;
Pos.Y = MousePosition.Y;
}
if (id == 1)
{
Cursor.Position = (Pos);
}
}
base.WndProc(ref m);
}
If you intend to capture the mouse position and restore it later then you have to make the Pos variable a field of your class instead of a local variable of the method. Like this:
private Point Pos;
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0312) // Trap WM_HOTKEY
{
switch (m.WParam.ToInt32()) {
case 0: Pos = Cursor.Position; break;
case 1: Cursor.Position = Pos; break;
}
}
base.WndProc(ref m);
}
Point pos = this.PointToClient(Cursor.Position);
Will return your actuall mouse position.

How to override the minimize control?

I want to override the minimize control to instead of sending the window to the taskbar it would do what ever I write it to do.
Basicly this is what I wanted my new minimized and restored effects to be:
private void ChangeForm(object sender, EventArgs e)
{
if (this.WindowState == FormWindowState.Minimized)
{
this.Height = 80;
iDebug.Visible = false;
mainMenu.Visible = false;
}
else
{
this.Height = 359;
iDebug.Visible = true;
mainMenu.Visible = true;
}
}
I have tried to fire an Event on the Resize to do this but without success
this.Resize += new EventHandler(ChangeForm);
Cancel A WinForm Minimize?
Just tested this and it will make the form 100 pixels shorter when minimize is clicked without flicker.
private const int WM_SYSCOMMAND = 0x0112;
private const int SC_MINIMIZE = 0xf020;
protected override void WndProc(ref Message m) {
if (m.Msg == WM_SYSCOMMAND) {
if (m.WParam.ToInt32() == SC_MINIMIZE) {
m.Result = IntPtr.Zero;
Height -= 100;
return;
}
}
base.WndProc(ref m);
}
The Minimize command has a very well defined meaning to a user, it shouldn't be messed with. Winforms accordingly doesn't have an event for it. But not a real problem, you can detect any Windows message by overriding WndProc(). Like tihs:
private void OnMinimize() {
this.Close(); // Do your stuff
}
protected override void WndProc(ref Message m) {
// Trap WM_SYSCOMMAND, SC_MINIMIZE
if (m.Msg == 0x112 && m.WParam.ToInt32() == 0xf020) {
OnMinimize();
return; // NOTE: delete if you still want the default behavior
}
base.WndProc(ref m);
}

Categories

Resources