I am trying to catch the MouseDown and MouseUp events in a listview's column header, to make the parent form movable via insider listview's header.
I could actually achieve this overriding the WndPrc but it only works within the listview area, not the columnheader. While the idea of moving a form via it's insider listview's header can be good (since my listview is not sortable by column click), moving the form from anywhere inside the listview is definetely not. It will cause to move on every item click which can be very disturbing.
This page says we can create a custom header control for a list/listview control but I could not find an example for C#/VB.Net despite all my search.
Anyone knows how to implement this or have an example ?
Thanks a lot by now.
Actually, it was very simple.
I share the code that I developed in case anyone will ever need this.
public class lw : ListView
{
const int LVM_FIRST = 0x1000;
const int LVM_GETHEADER = (LVM_FIRST + 31);
ListViewColumnHeaderWindow Header;
public lw()
{
//add 3 items for experiment
ListViewItem lwItem = new ListViewItem("a");
lwItem.SubItems.Add("b");
lwItem.SubItems.Add("c");
Items.Add(lwItem);
lwItem = new ListViewItem("d");
lwItem.SubItems.Add("e");
lwItem.SubItems.Add("f");
Items.Add(lwItem);
lwItem = new ListViewItem("g");
lwItem.SubItems.Add("h");
lwItem.SubItems.Add("i");
Items.Add(lwItem);
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
//here we need to wrap the header window to listen it's WndProc messages
IntPtr hHeader = (IntPtr)SendMessage(Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
Console.WriteLine("Creating ListViewColumnHeaderWindow for LVM_GETHEADER IntPtr {0}", hHeader);
Header = new ListViewColumnHeaderWindow(hHeader, this);
}
public void OnColumnMouseDown(object state)
{
Console.WriteLine("Header.MouseDown #{0}", (Point)state);
//now I can start to move the form from this point...
}
public void OnColumnMouseUp()
{
Console.WriteLine("Header.MouseUp");
}
public void MoveFromHeader(Point p) {
Console.WriteLine("Mouse moved to #{0}", p);
}
}
class ListViewColumnHeaderWindow : NativeWindow
{
lw Parent;
bool MouseIsDown;
System.Threading.Timer MTIMER;
public ListViewColumnHeaderWindow(IntPtr hHeader, lw parent)
{
Parent = parent;
MouseIsDown = false;
AssignHandle(hHeader);
}
protected override void WndProc(ref Message m)
{
if (MouseIsDown && m.Msg == 4608)
{
//this message is being sent when user starts to RESIZE
//the column header by clicking the column resizer.
//We should immediately cancel the mousedown event
//to allow user to resize the column (instead of moving the form)
MouseIsDown = false;
MTIMER?.Dispose();
}
switch (msg)
{
case 513: // WM_LBUTTONDOWN
MouseIsDown = true;
//due to possible column header resizing,
//I don't call OnColumnMouseDown event immediately here,
//but set a delay to detect the message '4608'
//which is being sent right after WM_LBUTTONDOWN
//that indicates the mouse is down to column resizing.
MTIMER?.Dispose();
MTIMER = new System.Threading.Timer(Parent.OnColumnMouseDown, new Point(m.LParam.ToInt32()), 200, System.Threading.Timeout.Infinite);
break;
case 512: // WM_MOUSEMOVE
if (MouseIsDown)
Parent.MoveFromHeader(new Point(m.LParam.ToInt32()));
break;
case 514: // WM_LBUTTONUP
if (MouseIsDown)
{
MouseIsDown = false;
Parent.OnColumnMouseUp();
}
break;
}
base.WndProc(ref m);
}
}
Related
I'm working on a project where I need a popup window. But the thing is I also want to be able to add textboxes etc in this popup window thru the form designer.
So basically I have a button and when you click on it it will open another window that I've designed in the form designer.
I've been doing some googling but I haven't found what I needed yet so I was hoping you guys could help me!
Just create another form (let's call it formPopup) using Visual Studio. In a button handler write the following code:
var formPopup = new Form();
formPopup.Show(this); // if you need non-modal window
If you need a non-modal window use: formPopup.Show();. If you need a dialog (so your code will hang on this invocation until you close the opened form) use: formPopup.ShowDialog()
This is not so easy because basically popups are not supported in windows forms. Although windows forms is based on win32 and in win32 popup are supported.
If you accept a few tricks, following code will set you going with a popup. You decide if you want to put it to good use :
class PopupWindow : Control
{
private const int WM_ACTIVATE = 0x0006;
private const int WM_MOUSEACTIVATE = 0x0021;
private Control ownerControl;
public PopupWindow(Control ownerControl)
:base()
{
this.ownerControl = ownerControl;
base.SetTopLevel(true);
}
public Control OwnerControl
{
get
{
return (this.ownerControl as Control);
}
set
{
this.ownerControl = value;
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.Style = WindowStyles.WS_POPUP |
WindowStyles.WS_VISIBLE |
WindowStyles.WS_CLIPSIBLINGS |
WindowStyles.WS_CLIPCHILDREN |
WindowStyles.WS_MAXIMIZEBOX |
WindowStyles.WS_BORDER;
createParams.ExStyle = WindowsExtendedStyles.WS_EX_LEFT |
WindowsExtendedStyles.WS_EX_LTRREADING |
WindowsExtendedStyles.WS_EX_RIGHTSCROLLBAR |
WindowsExtendedStyles.WS_EX_TOPMOST;
createParams.Parent = (this.ownerControl != null) ? this.ownerControl.Handle : IntPtr.Zero;
return createParams;
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr SetActiveWindow(HandleRef hWnd);
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_ACTIVATE:
{
if ((int)m.WParam == 1)
{
//window is being activated
if (ownerControl != null)
{
SetActiveWindow(new HandleRef(this, ownerControl.FindForm().Handle));
}
}
break;
}
case WM_MOUSEACTIVATE:
{
m.Result = new IntPtr(MouseActivate.MA_NOACTIVATE);
return;
//break;
}
}
base.WndProc(ref m);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(SystemBrushes.Info, 0, 0, Width, Height);
e.Graphics.DrawString((ownerControl as VerticalDateScrollBar).FirstVisibleDate.ToLongDateString(), this.Font, SystemBrushes.InfoText, 2, 2);
}
}
Experiment with it a bit, you have to play around with its position and its size. Use it wrong and nothing shows.
Forms in C# are classes that inherit the Form base class.
You can show a popup by creating an instance of the class and calling ShowDialog().
If you mean to create a new form when a button is clicked, the below code may be of some use to you:
private void settingsButton_Click(Object sender, EventArgs e)
{
// Create a new instance of the Form2 class
Form2 settingsForm = new Form2();
// Show the settings form
settingsForm.Show();
}
From here, you could also use the 'Show Dialog' method
"But the thing is I also want to be able to add textboxes etc in this popup window thru the form designer."
It's unclear from your description at what stage in the development process you're in. If you haven't already figured it out, to create a new Form you click on Project --> Add Windows Form, then type in a name for the form and hit the "Add" button. Now you can add controls to your form as you'd expect.
When it comes time to display it, follow the advice of the other posts to create an instance and call Show() or ShowDialog() as appropriate.
i am using this method.
add a from that you want to pop up, add all controls you need.
in the code you can handle the user input and return result to the caller.
for pop up the form just create a new instance of the form and show method.
/* create new form instance. i am overriding constructor to allow the caller form to set the form header */
var t = new TextPrompt("Insert your message and click Send button");
// pop up the form
t.Show();
if (t.DialogResult == System.Windows.Forms.DialogResult.OK)
{
MessageBox.Show("RTP", "Message sent to user");
}
I am using a win form that is inserted into another application using some Win32 magic via a .dll call .
I have to use custom control on the minimize and maximize events on the winform, because the default min/max operations break the panel's housing inside the parent window.
To get around this problem, I use opacity 0 just to make the panel invisible/visible on min/max operations. I use a custom button inside the host program for the maximize panel operation.
I am capturing the windows msgs to hook into this functionality.
protected override void WndProc(ref Message m)
{
const int WM_SYSCOMMAND = 0x0112;
const int SC_MAXIMIZE = 0xF030;
const int SC_MINIMIZE = 0xF020;
const int WM_CLOSE = 0x0010;
//user clicked close
if (m.Msg == WM_CLOSE)
{
//custom code for close event
}
if (m.Msg == WM_SYSCOMMAND)
{
switch ((int)m.WParam)
{
case SC_MAXIMIZE:
//disable maximize button
return;
case SC_MINIMIZE:
//hide the form by making it invisible, then disabling it
//this prevents form being detached from parent chart
HidePanel();
return;
}
}
//return control back to windows
base.WndProc(ref m);
}
So when HidePanel is called...
//panel delegate
private delegate void TradePanelDelegate();
private void _HidePanel()
{
Opacity = 0;
Enabled = false;
_formMinimzed = true;
}
private void HidePanel()
{
try
{
TradePanelForm thisForm = this;
thisForm.Invoke(new TradePanelDelegate(_HidePanel));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + "\r\n" + ex.StackTrace);
}
}
This works perfectly fine on Windows 10. But tests on windows 7 cause an exception, which seems to be centered around the opacity trick.
Here is the exception thrown on windows 7 when HidePanel() is called...
I am also using a delegate to call the 'UI Thread' to make the changes, as an attempt to fix this issue. It didn't make a difference.
Any suggestions? I am only an intermediate level C# programmer at this stage.
I have a TextBox component in C# WinForms.
When I Right-click on it, I would like to disable the paste option, whilst still allowing users to Cut and Copy.
I have investigated a few options, neither of which fully meet my requirements -
If I use this option, it will prevent Cut and Copy along with Paste which I do not want.
txt.ShortcutsEnabled = false;
If I override the ContextMenu of TextBox, I will have to write the Cut and Copy Features myself in the new context menu.
txt.ContextMenu = new ContextMenu(); // or some other
Are there any options I can use to disable only the Paste option of the default context menu, retaining Cut and Copy?
Assuming the Paste menu item is always the fifth element in the textbox context menu (zero-based and a separator counts as item too), you could subclass the TextBox class (here: CustomMenuTextBox) and override the WndProc method to disable that specific menu item:
public static class User32
{
[DllImport("user32.dll")]
public static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
}
public class CustomMenuTextBox : TextBox
{
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0093 /*WM_UAHINITMENU*/ || m.Msg == 0x0117 /*WM_INITMENUPOPUP*/ || m.Msg == 0x0116 /*WM_INITMENU*/)
{
IntPtr menuHandle = m.Msg == 0x0093 ? Marshal.ReadIntPtr(m.LParam) : m.WParam;
// MF_BYPOSITION and MF_GRAYED
User32.EnableMenuItem(menuHandle, 4, 0x00000400 | 0x00000001);
}
base.WndProc(ref m);
}
}
Based on Add item to the default TextBox context menu.
The following two steps disable the Copy/Paste feature in a textbox:
Disable the default menu and associate the textbox with an empty
context menu that has no menu items (mouse actions).
The user can still use the shortcut keys on the keyboard and perform
these operations.
So, override the ProcessCmdKey method as shown below:
// Constants
private const Keys CopyKeys = Keys.Control | Keys.C;
private const Keys PasteKeys = Keys.Control | Keys.V;
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if((keyData == CopyKeys) || (keyData == PasteKeys))
{
return true;
}
else
{
return base.ProcessCmdKey(ref msg, keyData);
}
}
Note: Return true, which supresses the base class functionality.
OR You can use :
ContextMenuStrip mnu = new ContextMenuStrip();
ToolStripMenuItem mnuCopy = new ToolStripMenuItem("Copy");
ToolStripMenuItem mnuCut = new ToolStripMenuItem("Cut");
mnuCopy.Click += new EventHandler(mnuCopy_Click);
mnuCut.Click += new EventHandler(mnuCut_Click);
mnu.MenuItems.AddRange(new MenuItem[] { mnuCopy, mnuCut});
txt.ContextMenu = mnu;
Note : You can't disable Past option in default context menu for that you have to add contextMenuStrip in your form.
this.contextMenuStrip1.Items.AddRange(new
System.Windows.Forms.ToolStripItem[] {
this.Undo,
this.Cut,
this.Copy,
this.Paste,
this.Delete,
this.SelectAll});
this.contextMenuStrip1.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip1_Opening);
this.txt.ContextMenuStrip = this.contextMenuStrip1;
Hope Useful for you link
If you only need Cut Copy and Paste options, Maybe it is better create your custom contentMenuStrip, you can do that visually in Visual Studio and it is very easy to implement the copy cut and paste options. Then you can control in your program when you want to have any option enable or not.
For example this code in the Opening event of your custom ContentMenuStrip, disable copy and cut when you haven't selected text in a text-box, and only enable Paste if your clipboard contain text or images.
private void contextSuperEditor_Opening(object sender, CancelEventArgs e)
{
if (tbText.SelectionLength > 0)
{
MenuCopy.Enabled = true;
MenuCut.Enabled = true;
MenuPaste.Enabled = false;
}
else
{
MenuCopy.Enabled = false;
MenuCut.Enabled = false;
if (Clipboard.ContainsText() | Clipboard.ContainsImage())
{
MenuPaste.Enabled = true;
}
}
}
I'm working on a project where I need a popup window. But the thing is I also want to be able to add textboxes etc in this popup window thru the form designer.
So basically I have a button and when you click on it it will open another window that I've designed in the form designer.
I've been doing some googling but I haven't found what I needed yet so I was hoping you guys could help me!
Just create another form (let's call it formPopup) using Visual Studio. In a button handler write the following code:
var formPopup = new Form();
formPopup.Show(this); // if you need non-modal window
If you need a non-modal window use: formPopup.Show();. If you need a dialog (so your code will hang on this invocation until you close the opened form) use: formPopup.ShowDialog()
This is not so easy because basically popups are not supported in windows forms. Although windows forms is based on win32 and in win32 popup are supported.
If you accept a few tricks, following code will set you going with a popup. You decide if you want to put it to good use :
class PopupWindow : Control
{
private const int WM_ACTIVATE = 0x0006;
private const int WM_MOUSEACTIVATE = 0x0021;
private Control ownerControl;
public PopupWindow(Control ownerControl)
:base()
{
this.ownerControl = ownerControl;
base.SetTopLevel(true);
}
public Control OwnerControl
{
get
{
return (this.ownerControl as Control);
}
set
{
this.ownerControl = value;
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.Style = WindowStyles.WS_POPUP |
WindowStyles.WS_VISIBLE |
WindowStyles.WS_CLIPSIBLINGS |
WindowStyles.WS_CLIPCHILDREN |
WindowStyles.WS_MAXIMIZEBOX |
WindowStyles.WS_BORDER;
createParams.ExStyle = WindowsExtendedStyles.WS_EX_LEFT |
WindowsExtendedStyles.WS_EX_LTRREADING |
WindowsExtendedStyles.WS_EX_RIGHTSCROLLBAR |
WindowsExtendedStyles.WS_EX_TOPMOST;
createParams.Parent = (this.ownerControl != null) ? this.ownerControl.Handle : IntPtr.Zero;
return createParams;
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr SetActiveWindow(HandleRef hWnd);
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_ACTIVATE:
{
if ((int)m.WParam == 1)
{
//window is being activated
if (ownerControl != null)
{
SetActiveWindow(new HandleRef(this, ownerControl.FindForm().Handle));
}
}
break;
}
case WM_MOUSEACTIVATE:
{
m.Result = new IntPtr(MouseActivate.MA_NOACTIVATE);
return;
//break;
}
}
base.WndProc(ref m);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(SystemBrushes.Info, 0, 0, Width, Height);
e.Graphics.DrawString((ownerControl as VerticalDateScrollBar).FirstVisibleDate.ToLongDateString(), this.Font, SystemBrushes.InfoText, 2, 2);
}
}
Experiment with it a bit, you have to play around with its position and its size. Use it wrong and nothing shows.
Forms in C# are classes that inherit the Form base class.
You can show a popup by creating an instance of the class and calling ShowDialog().
If you mean to create a new form when a button is clicked, the below code may be of some use to you:
private void settingsButton_Click(Object sender, EventArgs e)
{
// Create a new instance of the Form2 class
Form2 settingsForm = new Form2();
// Show the settings form
settingsForm.Show();
}
From here, you could also use the 'Show Dialog' method
"But the thing is I also want to be able to add textboxes etc in this popup window thru the form designer."
It's unclear from your description at what stage in the development process you're in. If you haven't already figured it out, to create a new Form you click on Project --> Add Windows Form, then type in a name for the form and hit the "Add" button. Now you can add controls to your form as you'd expect.
When it comes time to display it, follow the advice of the other posts to create an instance and call Show() or ShowDialog() as appropriate.
i am using this method.
add a from that you want to pop up, add all controls you need.
in the code you can handle the user input and return result to the caller.
for pop up the form just create a new instance of the form and show method.
/* create new form instance. i am overriding constructor to allow the caller form to set the form header */
var t = new TextPrompt("Insert your message and click Send button");
// pop up the form
t.Show();
if (t.DialogResult == System.Windows.Forms.DialogResult.OK)
{
MessageBox.Show("RTP", "Message sent to user");
}
in my win forms app, I am using two List views to compare two files.
when user selects two files using folder browser, files are loaded in List views.
I compare the files and lines that are not matching are shown with different color.
this works absolutely fine.
now when I scroll one List view, I want the other List view should also be scrolled with same amount.
I tried but you cannot set Horizontal scroll or vertical scroll property of a Listview.
how can I do this ?
thanks in advance.
you need to create a custom List view so that you can detect it scrolling and pass the scroll message to the other text box so it will scroll in sync.
class SyncListView: ListView
{
public SyncListView()
{
}
public Control Buddy { get; set; }
private static bool scrolling; // In case buddy tries to scroll us
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
// Trap WM_VSCROLL message and pass to buddy
if ((m.Msg == 0x115 || m.Msg == 0x20a) && !scrolling && Buddy != null && Buddy.IsHandleCreated)
{
scrolling = true;
SendMessage(Buddy.Handle, m.Msg, m.WParam, m.LParam);
scrolling = false;
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}
I had similar task. Two lists that must show theirs items next to each-other.
I found this thread with the Iorn Man's answer, that looked too difficult for me, because I did not have enough experience in C#.
I've found an easier solution for that.
I added a timer to a form. For its tick event, I added this:
Form active_form = Form.ActiveForm;
if (active_form == null) return;
Control control = Form.ActiveForm.ActiveControl;
if (control == newFilesList)
{
Sync_lists(newFilesList);
}
else
{
Sync_lists(oldFilesList);
}
It checks which list is active and calls Sync_list routine with this list as argument.
private void Sync_lists(ListView sender)
{
if ((newFilesList.Items.Count > 0) && (oldFilesList.Items.Count > 0))
{
int cur_top_index = sender.TopItem.Index;
ListViewItem future_top_item;
if (sender == oldFilesList)
{
future_top_item = newFilesList.Items[cur_top_index];
newFilesList.TopItem = future_top_item;
}
else
{
future_top_item = oldFilesList.Items[cur_top_index];
oldFilesList.TopItem = future_top_item;
}
}
}
It just get TopItem property of the base list and sets item with the same index as a top for another list.
It is not so right as a custom ListView. But a little bit simpler. Hope it'll help.
You can also do this in Better ListView or Better ListView Express using only managed code:
public class CustomListView : BetterListView
{
public void SynchronizeScroll(BetterListView listView)
{
VScrollBar.Value = listView.VScrollProperties.Value;
}
}
then handling its VScrollPropertiesChanged event with something like this:
private void ListViewVScrollPropertiesChanged(object sender, BetterListViewScrollPropertiesChangedEventArgs eventArgs)
{
CustomListView listViewThis = (sender as CustomListView);
listViewThis.SynchronizeScroll(this.listViewAnother);
}