First, sorry for my bad english :)
Second, I can know when the form is being moved/resized, using this code:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_WINDOWPOSCHANGING)
{
WINDOWPOS winPos = new WINDOWPOS();
winPos = (WINDOWPOS)Marshal.PtrToStructure(m.LParam, typeof(WINDOWPOS));
//Here I just need to change the values of the WINDOWPOS structure
Marshal.StructureToPtr(winPos, m.LParam, true);
}
}
The WM_WINDOWPOSCHANGING message is sent also when the user is minimizing or maximizing the window. But how I can know when the user is maximizing/minimizing, not moving/resizing? I tried get the WindowState property, but it didn't work :(
The code of the WINDOWPOS structure is:
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPOS
{
public IntPtr hwnd;
public IntPtr hwndInsertAfter;
public int x;
public int y;
public int cx;
public int cy;
public int flags;
}
Any help?
You get WM_SYSCOMMAND when the user clicks one of the buttons in the title bar: http://msdn.microsoft.com/en-us/library/ms646360(VS.85).aspx
You can trap the WM_SYSCOMMAND by overriding WndProc(). But it can easily be done as well with an event handler for the Resize event:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
mPrevState = this.WindowState;
}
FormWindowState mPrevState;
protected override void OnResize(EventArgs e) {
base.OnResize(e);
if (mPrevState != this.WindowState) {
mPrevState = this.WindowState;
// Do something
//..
}
}
}
Related
I'm not able to find an event to fire when I hover my ComboBox Items.
I'm using windows form to build an application.
I found a something similar for WPF:
how to change label text when I hover mouse over a combobox item?.
How can I do it the similar way in Windows Forms, or is there an alternate way?
Class ComboBoxListEx:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
[DesignerCategory("Code")]
public class ComboBoxListEx : ComboBox
{
private int listItem = -1;
private const int CB_GETCURSEL = 0x0147;
public event EventHandler<ListItemSelectionChangedEventArgs> ListItemSelectionChanged;
protected virtual void OnListItemSelectionChanged(ListItemSelectionChangedEventArgs e)
=> this.ListItemSelectionChanged?.Invoke(this, e);
public ComboBoxListEx() { }
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case CB_GETCURSEL:
int selItem = m.Result.ToInt32();
if (listItem != selItem)
{
listItem = selItem;
OnListItemSelectionChanged(new ListItemSelectionChangedEventArgs(
listItem, listItem < 0 ? string.Empty : this.GetItemText(this.Items[listItem]))
);
}
break;
default:
// Add Case switches to handle other events
break;
}
}
public class ListItemSelectionChangedEventArgs : EventArgs
{
public ListItemSelectionChangedEventArgs(int idx, string text)
{
this.ItemIndex = idx;
this.ItemText = text;
}
public int ItemIndex { get; private set; }
public string ItemText { get; private set; }
}
}
private void comboBoxListEx1_ListItemSelectionChanged(object sender, ComboBoxListEx.ListItemSelectionChangedEventArgs e)
{
label15.Text = e.ItemText;
}
You can create a Custom Control, derived from ComboBox, override its WndProc method to intercept the CB_GETCURSEL message.
Call base.WndProc(ref m) first. When the message is processed, the Message object's m.Result property is set to a value (as IntPtr) that represents the Item currently tracked in the ListBox (the Item highlighted when the Mouse Pointer hovers it).
► Note: prior to .Net Framework 4.8, the CB_GETCURSEL message result is not bubbled up automatically: we must send LB_GETCURSEL to the child ListBox to get the index of the Item currently highlighted.
The ListBox handle is retrieved using GetComboBoxInfo: it could be also accessed using reflection (the private ChildListAutomationObject property returns the ListBox AutomationElement, which provides the handle), or sending a CB_GETCOMBOBOXINFO message (but it's the same as calling GetComboBoxInfo()).
This custom ComboBox raises an Event, ListItemSelectionChanged, with a custom EventArgs object, ListItemSelectionChangedEventArgs, which exposes two public properties: ItemIndex and ItemText, set to the Index and Text of the hovered item.
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
[DesignerCategory("Code")]
public class ComboBoxListEx : ComboBox
{
private const int CB_GETCURSEL = 0x0147;
private int listItem = -1;
IntPtr listBoxHandle = IntPtr.Zero;
public event EventHandler<ListItemSelectionChangedEventArgs> ListItemSelectionChanged;
protected virtual void OnListItemSelectionChanged(ListItemSelectionChangedEventArgs e)
=> this.ListItemSelectionChanged?.Invoke(this, e);
public ComboBoxListEx() { }
// .Net Framework prior to 4.8 - get the handle of the ListBox
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
listBoxHandle = GetComboBoxListInternal(this.Handle);
}
protected override void WndProc(ref Message m)
{
int selItem = -1;
base.WndProc(ref m);
switch (m.Msg) {
case CB_GETCURSEL:
selItem = m.Result.ToInt32();
break;
// .Net Framework prior to 4.8
// case CB_GETCURSEL can be left there or removed: it's always -1
case 0x0134:
selItem = SendMessage(listBoxHandle, LB_GETCURSEL, 0, 0);
break;
default:
// Add Case switches to handle other events
break;
}
if (listItem != selItem) {
listItem = selItem;
OnListItemSelectionChanged(new ListItemSelectionChangedEventArgs(
listItem, listItem < 0 ? string.Empty : GetItemText(Items[listItem]))
);
}
}
public class ListItemSelectionChangedEventArgs : EventArgs
{
public ListItemSelectionChangedEventArgs(int idx, string text) {
ItemIndex = idx;
ItemText = text;
}
public int ItemIndex { get; private set; }
public string ItemText { get; private set; }
}
// -------------------------------------------------------------
// .Net Framework prior to 4.8
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int SendMessage(IntPtr hWnd, uint uMsg, int wParam, int lParam);
private const int LB_GETCURSEL = 0x0188;
[StructLayout(LayoutKind.Sequential)]
internal struct COMBOBOXINFO
{
public int cbSize;
public Rectangle rcItem;
public Rectangle rcButton;
public int buttonState;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
public void Init() => this.cbSize = Marshal.SizeOf<COMBOBOXINFO>();
}
internal static IntPtr GetComboBoxListInternal(IntPtr cboHandle)
{
var cbInfo = new COMBOBOXINFO();
cbInfo.Init();
GetComboBoxInfo(cboHandle, ref cbInfo);
return cbInfo.hwndList;
}
}
Works like this:
I’m trying to catch TVN_SELCHANGING message from a TreeView. I know there is also the BeforeSelect event but I’d like to understand why I’m not able to catch the message…
I’ve read on msdn the TVN_SELCHANG(ED)(ING) LParam is a pointer to a NMTREEVIEW structure. Also that the code is sent in the form of a WM_NOTIFY message.
So I’ve tried to implement it:
(this helped me)
public partial class TreeviewEx : TreeView
{
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
}
[StructLayout(LayoutKind.Sequential)]
private struct TVITEM
{
public uint mask;
public IntPtr hItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public int iSelectedImage;
public int cChildren;
public IntPtr lParam;
}
[StructLayout(LayoutKind.Sequential)]
private struct NMHDR
{
public IntPtr hwndFrom;
public IntPtr idFrom;
public int code;
}
[StructLayout(LayoutKind.Sequential)]
private struct NMTREEVIEW
{
public NMHDR hdr;
public int action;
public TVITEM itemOld;
public TVITEM itemNew;
public POINT ptDrag;
}
private const int TVN_FIRST = -400;
private const int TVN_SELCHANGINGA = (TVN_FIRST - 1);
private const int TVN_SELCHANGINGW = (TVN_FIRST - 50);
private const int TVN_SELCHANGEDA = (TVN_FIRST - 2);
private const int TVN_SELCHANGEDW = (TVN_FIRST - 51);
private const int WM_NOTIFY = 0x004e;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_NOTIFY)
{
var notify = (NMTREEVIEW)Marshal.PtrToStructure(m.LParam, typeof(NMTREEVIEW));
if (notify.action == TVN_SELCHANGINGA)
{
MessageBox.Show("changing");
}
}
base.WndProc(ref m);
}
I've tried all actions, but none of them seem to work. What am I doing wrong?
Right, this doesn't work. Lots of history behind it, the native Windows controls were designed to be used in C programs. Using Petzold's "Programming Windows" style coding where you put the custom logic for a window in the window procedure. And just used a control like TreeView as-is. Accordingly, these controls send their notification messages to the parent window. Because that's where you put your code.
That's not very compatible with the way modern GUI code is written. Particularly the notion of inheriting a control to give it new behavior. Like you did with your TreeViewEx class. You really want to get these notifications in your own class first. So you can do interesting things with OnBeforeSelect() to customize the behavior of the control. Now having this message sent to the parent is rather a big problem, a control should never be aware of its parent's implementation.
Winforms fixes this problem, it reflects the message from the parent window back to the original window. Altering the message, necessary so it is completely clear that it is a reflected message. Which it does by adding a constant to the message number, WM_REFLECT, a value that you can hardcode to 0x2000. So fix it like this:
private const int WM_REFLECT = 0x2000;
protected override void WndProc(ref Message m) {
if (m.Msg == WM_REFLECT + WM_NOTIFY) {
var nmhdr = (NMHDR)Marshal.PtrToStructure(m.LParam, typeof(NMHDR));
if (nmhdr.code == TVN_SELCHANGINGW) {
var notify = (NMTREEVIEW)Marshal.PtrToStructure(m.LParam, typeof(NMTREEVIEW));
// etc..
}
}
base.WndProc(ref m);
}
How do I achieve this in a WinForms container control when the scroll bars are visible?
Highlighted here (Google Chrome browser):
EDIT: This cursor is the only one that is visible on a screenshot. I hope it's clear what i mean.
EDIT:
Tried this on my control. Does not work.
const int WM_MBUTTONDOWN = 0x207;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MBUTTONDOWN)
DefWndProc(ref m);
else
base.WndProc(ref m);
}
Here's what I have so far. It exits "reader mode" if I release the middle button, and I haven't implemented scrolling within the control (I used a textbox), but it may give you something to start with.
[DllImport("comctl32.dll", SetLastError=true, EntryPoint="#383")]
static extern void DoReaderMode(ref READERMODEINFO prmi);
public delegate bool TranslateDispatchCallbackDelegate(ref MSG lpmsg);
public delegate bool ReaderScrollCallbackDelegate(ref READERMODEINFO prmi, int dx, int dy);
[StructLayout(LayoutKind.Sequential)]
public struct READERMODEINFO
{
public int cbSize;
public IntPtr hwnd;
public int fFlags;
public IntPtr prc;
public ReaderScrollCallbackDelegate pfnScroll;
public TranslateDispatchCallbackDelegate fFlags2;
public IntPtr lParam;
}
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
public IntPtr hwnd;
public UInt32 message;
public IntPtr wParam;
public IntPtr lParam;
public UInt32 time;
public POINT pt;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
struct RECT
{
public int left, top, right, bottom;
}
private bool TranslateDispatchCallback(ref MSG lpMsg)
{
return false;
}
private bool ReaderScrollCallback(ref READERMODEINFO prmi, int dx, int dy)
{
// TODO: Scroll around within your control here
return false;
}
private void EnterReaderMode()
{
READERMODEINFO readerInfo = new READERMODEINFO
{
hwnd = this.textBox1.Handle,
fFlags = 0x00,
prc = IntPtr.Zero,
lParam = IntPtr.Zero,
fFlags2 = new TranslateDispatchCallbackDelegate(this.TranslateDispatchCallback),
pfnScroll = new ReaderScrollCallbackDelegate(this.ReaderScrollCallback)
};
readerInfo.cbSize = Marshal.SizeOf(readerInfo);
DoReaderMode(ref readerInfo);
}
private void textBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Middle)
{
EnterReaderMode();
}
}
The RichTextBox control does it by default when you press the mouse wheel button.
Edit: Sorry I misunderstood and thought you were asking about doing it within a textbox not a container control
In C# Windows.Forms I want to intercept the paste-windowmessage for a combobox. As this doesn't work by overriding the WndProc-method of the combobox, because I would need to override the WndProc of the textbox inside the combobox, I decided to create a custom class of type NativeWindow which overrides the WndProc. I assign the handle and release it, when the combobox-handle gets destroyed. But when Dispose for the combobox is called the problem is that I get an InvalidOperationException saying that an invalid cross-thread operation occured and that the combobox was accessed from a thread other than the thread it was created on. Any ideas what is going wrong here?
In the following you'll see, how my classes look like:
public class MyCustomComboBox : ComboBox
{
private WinHook hook = null;
public MyCustomComboBox()
: base()
{
this.hook = new WinHook(this);
}
private class WinHook : NativeWindow
{
public WinHook(MyCustomComboBox parent)
{
parent.HandleCreated += new EventHandler(this.Parent_HandleCreated);
parent.HandleDestroyed += new EventHandler(this.Parent_HandleDestroyed);
}
protected override void WndProc(ref Message m)
{
// do something
base.WndProc(ref m);
}
private void Parent_HandleCreated(object sender, EventArgs e)
{
MyCustomComboBox cbx = (MyCustomComboBox)sender;
this.AssignHandle(cbx.Handle);
}
private void Parent_HandleDestroyed(object sender, EventArgs e)
{
this.ReleaseHandle();
}
}
}
Per Hans' suggestion, I modified the code to use CB_GETCOMBOBOXINFO from one of his own examples.
public class PastelessComboBox : ComboBox {
private class TextWindow : NativeWindow {
[StructLayout(LayoutKind.Sequential)]
private struct RECT {
public int Left;
public int Top;
public int Right;
public int Bottom;
}
private struct COMBOBOXINFO {
public Int32 cbSize;
public RECT rcItem;
public RECT rcButton;
public int buttonState;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
[DllImport("user32.dll", EntryPoint = "SendMessageW", CharSet = CharSet.Unicode)]
private static extern IntPtr SendMessageCb(IntPtr hWnd, int msg, IntPtr wp, out COMBOBOXINFO lp);
public TextWindow(ComboBox cb) {
COMBOBOXINFO info = new COMBOBOXINFO();
info.cbSize = Marshal.SizeOf(info);
SendMessageCb(cb.Handle, 0x164, IntPtr.Zero, out info);
this.AssignHandle(info.hwndEdit);
}
protected override void WndProc(ref Message m) {
if (m.Msg == (0x0302)) {
MessageBox.Show("No pasting allowed!");
return;
}
base.WndProc(ref m);
}
}
private TextWindow textWindow;
protected override void OnHandleCreated(EventArgs e) {
textWindow = new TextWindow(this);
base.OnHandleCreated(e);
}
protected override void OnHandleDestroyed(EventArgs e) {
textWindow.ReleaseHandle();
base.OnHandleDestroyed(e);
}
}
In C# winforms, is there a way to not show the dashed focus outline border that shows around a trackbar control when it is being used?
Details: This outline looks kinda tacky to me, so I'm just shooting for aesthetics to not show it.
Thanks,
Adam
ShowFocusCues didn't work for me, but this did:
internal class NoFocusTrackBar : System.Windows.Forms.TrackBar
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
public extern static int SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
private static int MakeParam(int loWord, int hiWord)
{
return (hiWord << 16) | (loWord & 0xffff);
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
SendMessage(this.Handle, 0x0128, MakeParam(1, 0x1), 0);
}
}
See documentation on WM_UPDATEUISTATE for how this works (basically sending a message to turn the dumb thing off the trackbar gets the focus).
I know it's an old question but this is simpler if anyone interested:
public class TrackBarWithoutFocus : TrackBar
{
private const int WM_SETFOCUS = 0x0007;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SETFOCUS)
{
return;
}
base.WndProc(ref m);
}
}
private void trackBar1_MouseLeave(object sender, EventArgs e)
{
button2.Select();
}
really old but i made a button with visible set to false and selected that one
when leaving trackbar area