I made a custom button with some panels and pictureboxes. With the MouseEnter and MouseLeave I set the appropriate hover images like normal buttons.
The problem is that if I move the mouse too fast over the control it sometimes doesn't trigger the MouseLeave event. This way the button is "locked" in hover state.
screenshot problem:
http://www.jesconsultancy.nl/images/screens/screen_prblm.png
the button at the right is locked in "hover" state.
How can i solve this?
Thanks.
Holy... That's a mess!
Firstly, UserControls are very buggy. I suggest you make your control inherit from Control instead and draw the image and text yourself.
Secondly, why are you using reflection?
Thirdly, why are there so many controls?
This misses the event because it takes too much to update!
Here is some code for the simples control possible, that will NEVER miss an event:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace lol
{
public class BlackWhiteControl : Control
{
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
this.BackColor = Color.Black;
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
this.BackColor = Color.White;
}
}
}
You should receive the WM_MOUSELEAVE windows message automatically when the mouse leaves the client area and the base class processing of this message will then call the OnMouseLeave method. If this is really not happening you can certainly get around it. Just intercept the WM_MOUSEMOVE directly and then make a Win32 API call that requests you be notified when the mouse leaves your control.
Use the following simple WndProc override...
private bool _mouseOver = false;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case PI.WM_MOUSEMOVE:
if (!_mouseOver)
{
PI.TRACKMOUSEEVENTS tme = new PI.TRACKMOUSEEVENTS();
tme.cbSize = (uint)Marshal.SizeOf(typeof(PI.TRACKMOUSEEVENTS));
tme.dwHoverTime = 100;
tme.dwFlags = (int)(PI.TME_LEAVE);
tme.hWnd = Handle;
PI.TrackMouseEvent(ref tme);
_mouseOver = true;
}
base.WndProc(ref m);
break;
case PI.WM_MOUSELEAVE:
_mouseOver = false;
base.WndProc(ref m);
break;
}
}
And the platform invoke information you need is...
internal const int WM_MOUSEMOVE = 0x0200;
internal const int WM_MOUSELEAVE = 0x02A3;
internal const int TME_LEAVE = 0x0002;
[StructLayout(LayoutKind.Sequential)]
internal struct TRACKMOUSEEVENTS
{
public uint cbSize;
public uint dwFlags;
public IntPtr hWnd;
public uint dwHoverTime;
}
Hope that helps.
Related
I have an application written in C#, It is a directory system that will display information in a slideshow fashion.
In my Form I have a Panel docked to fill the form's content. Inside that panel there are 9 panels where each one displays the information of a particular object.
Now what I want is that whenever I move the mouse I want to trigger the MouseMoveEvent of the form hosting the panel, instead of those of the big panel or the panels inside it.
Here is my code handling the form's MouseMoveEvent:
protected override void OnMouseMove(MouseEventArgs e)
{
MessageBox.Show("Moved!");
}
I know that this will not fire because the mouse cursor is inside the panel but how to trigger the event on the form anyway?
The purpose of this is to hide the current form and show another form when mouse cursor inside the form moved. It is possible?
This example works correct for me, program call TheMouseMoved() method only if I move mouse.
public partial class Form1 : Form
{
int counter = 0;
public Form1()
{
GlobalMouseHandler gmh = new GlobalMouseHandler();
gmh.TheMouseMoved += new MouseMovedEvent(gmh_TheMouseMoved);
Application.AddMessageFilter(gmh);
InitializeComponent();
}
void gmh_TheMouseMoved()
{
Point cur_pos = System.Windows.Forms.Cursor.Position;
//System.Console.WriteLine(cur_pos);
System.Console.WriteLine("{0}. [ {1},{2} ]", counter++, (cur_pos.X - this.Location.X), (cur_pos.Y - this.Location.Y));
}
}
public delegate void MouseMovedEvent();
public class GlobalMouseHandler : IMessageFilter
{
private const int WM_MOUSEMOVE = 0x0200;
public event MouseMovedEvent TheMouseMoved;
#region IMessageFilter Members
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == WM_MOUSEMOVE)
{
if (TheMouseMoved != null)
{
TheMouseMoved();
}
}
// Always allow message to continue to the next filter control
return false;
}
#endregion
}
Try the "MouseMove" Event on the panel. If you disable the docked panel, the "MouseMove" Event of the Forms will be triggered.
I solve the problem by modifying the answer from How do I capture the mouse move event because the accepted answer is continuously firing even though the mouse is not moving according to #Randy Gamage comment.
I solved it using this code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace GlobalMouseEvents
{
public partial class Form1 : Form
{
public Form1()
{
GlobalMouseHandler gmh = new GlobalMouseHandler();
gmh.TheMouseMoved += new MouseMovedEvent(gmh_TheMouseMoved);
Application.AddMessageFilter(gmh);
InitializeComponent();
}
void gmh_TheMouseMoved()
{
Point cur_pos = System.Windows.Forms.Cursor.Position;
System.Console.WriteLine(cur_pos);
}
}
public delegate void MouseMovedEvent();
public class GlobalMouseHandler : IMessageFilter
{
private const int WM_MOUSEMOVE = 0x0200;
private System.Drawing.Point previousMousePosition = new System.Drawing.Point();
public static event EventHandler<MouseEventArgs> MouseMovedEvent = delegate { };
#region IMessageFilter Members
public bool PreFilterMessage(ref System.Windows.Forms.Message m)
{
if (m.Msg == WM_MOUSEMOVE)
{
System.Drawing.Point currentMousePoint = Control.MousePosition;
// Prevent event from firing twice.
if (previousMousePosition == new System.Drawing.Point(0, 0))
{ return; }
if (previousMousePosition != currentMousePoint)
{
previousMousePosition = currentMousePoint;
MouseMovedEvent(this, new MouseEventArgs(MouseButtons.None, 0, currentMousePoint.X, currentMousePoint.Y, 0));
}
}
// Always allow message to continue to the next filter control
return false;
}
#endregion
}
}
I'm working on a large C# winforms project. After explaining thousands of times to my end users that they have to press tab instead of enter in textboxes, datagrids, and wherever, I decided to add a checkbox somewhere, so users can optionally set if they want to replace enter with tab. I don't like it myself, because I think weird stuff will happen, but I'd like to try it.
The thing is that I have lots of forms, and lots of places where I would have to set a keydown event or similar. I would like to put all of this in one place, on application level. Is there a way for this?
I guess this is not possible, since some controls will expose the keydown event diverently (for example in cells of the gridview). You could iterate through all controls in a form recursively and assign the event for the basic controls though.
The event itself then could be handled in a central place
I'll advice you to create a separate class that the constructor accept the parameters you need (like the textbox), You create global variables and assign the parameters to the variables in the constructor.
Then Create the event handler in the class and then you can put your code in the event handler using the variables.
You can then call the class wherever you need the keydown event
Form level (you can implement the behaviour in the base form and inherit from it):
this.KeyPreview = true;
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
PressedEnter();
base.OnKeyDown(e);
}
private bool PressedEnter()
{
bool res = false; // true if handled
Control ctr = GetFocusedControl();
if (ctr != null && ctr is TextBox)
{
res = this.SelectNextControl(ctr, true, true, true, true);
}
return res;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)]
internal static extern IntPtr GetFocus();
private Control GetFocusedControl()
{
Control focusedControl = null;
IntPtr focusedHandle = GetFocus();
if (focusedHandle != IntPtr.Zero)
// if control is not a .Net control will return null
focusedControl = Control.FromHandle(focusedHandle);
return focusedControl;
}
It probably can be done on application level too: In your main form you'll have to prefilter messages from the message loop (using message filter: Application.AddMessageFilter(your filter)), check for message WM_KEYDOWN = 0x100, check if the pressed key was ENTER, then handle it same as above.You do it only once, in your main form, it'll work on all your child forms.
In your main form class:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.mouseMessageFilter = new MouseMoveMessageFilter();
this.mouseMessageFilter.TargetForm = this;
Application.AddMessageFilter(this.mouseMessageFilter);
}
protected override void OnClosed(EventArgs e)
{
Application.RemoveMessageFilter(this.mouseMessageFilter);
base.OnClosed(e);
}
private class MouseMoveMessageFilter : IMessageFilter
{
public FormMain TargetForm { get; set; }
public bool PreFilterMessage(ref Message m)
{
if (TargetForm.IsDisposed) return false;
int numMsg = m.Msg;
int VK_RETURN=0x0D;
if (m.Msg == 0x100 &&(int)m.WParam == VK_RETURN) // WM_KEYDOWN and enter pressed
{
if (TargetForm.PressedEnter()) return true;
}
return false;
}
}
sources:
https://stackoverflow.com/a/435510/891715
http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
http://www.autohotkey.com/docs/misc/SendMessageList.htm
It's much simpler to use a MessageFilter in combination with SendKeys:
public partial class Form1 : Form, IMessageFilter
{
public Form1()
{
InitializeComponent();
Application.AddMessageFilter(this);
}
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == 0x100)//WM_KEYDOWN
{
if (m.WParam.ToInt32() == 0xd)//VK_RETURN = 0xd
{
SendKeys.Send("{TAB}");
return true; //Discard the Enter key
}
}
return false;
}
}
I'm attempting to create a custom AutoCompleteTextBox control in WinForms. The AutoComplete provided by the basic TextBox provides results only with (string).StartsWith as opposed to (string).Contains.
I am displaying the Auto-Complete search results in a ListBox docked in a separate Form. I can prevent the Form from stealing focus initially using via:
protected override bool ShowWithoutActivation
{
get { return true; }
}
Then, I can prevent the Form from gaining focus entirely by overriding the WndProc method via:
protected override void WndProc( ref Message m )
{
base.WndProc( ref m );
switch ( m.Msg )
{
case WM_MOUSEACTIVATE:
m.Result = ( IntPtr ) MA_NOACTIVATEANDEAT;
break;
default:
break;
}
When I do this, the ListBox contained in the Form will receive MouseMoved events, but not MouseClicked events.
Chaning MA_NOACTIVATEANDEAT to just MA_NOACTIVATE will pass the mouse events to the ListBox, but then will cause the clicks on the ListBox to steal focus from the Form the ListBox resides in - passing it to the 'floating' Form the ListBox is in.
Is there any way I can prevent the 'floating' Form from steeling focus from the 'main' Form while still getting MouseClick events within the ListBox?
Convert your floating form into a Usercontrol as below :-
Pull a ListBox on it to & hook onto Click event to simulate your scenario of Focus.
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication3
{
public partial class InactiveForm : UserControl
{
private const int WS_EX_TOOLWINDOW = 0x00000080;
private const int WS_EX_NOACTIVATE = 0x08000000;
private const int WS_EX_TOPMOST = 0x00000008;
[DllImport("user32")]
public static extern int SetParent
(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32")]
public static extern int ShowWindow
(IntPtr hWnd, int nCmdShow);
protected override CreateParams CreateParams
{
get
{
CreateParams p = base.CreateParams;
p.ExStyle |= (WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
p.Parent = IntPtr.Zero;
return p;
}
}
public InactiveForm()
{
InitializeComponent();
}
public new void Show()
{
if (this.Handle == IntPtr.Zero) base.CreateControl();
SetParent(base.Handle, IntPtr.Zero);
ShowWindow(base.Handle, 1);
}
private void OnListBoxClicked(object sender, EventArgs e)
{
MessageBox.Show("Clicked List Box on floating control");
}
}
}
Code in the MainForm (with a button and its click handler attached):-
This invokes the floating control with ListBox.
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
InactiveForm f = new InactiveForm();
f.Show();
}
}
}
When you show the UserControl (borderless form) as your floating form, you will notice that it wont receive focus even if you clicked (selected) any of its children. In this case, the child is a ListBox on the usercontrol.
Refer here and here.
In addtion to previous answer by Angshuman Agarwal:
If you want the main form does not deactive when the UserControl show as floating form, modify some code to your UserControl:
private Control _mControl;
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
public new void Show(Control c)
{
if (c == null) throw new ArgumentNullException();
_mControl = c;
if (this.Handle == IntPtr.Zero) base.CreateControl();
SetParent(base.Handle, IntPtr.Zero);
ShowWindow(base.Handle, 1);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x86) //WM_NCACTIVATE
{
if (m.WParam != IntPtr.Zero) //activate
{
SendMessage(_mControl.Handle, 0x86, (IntPtr)1, IntPtr.Zero);
}
this.DefWndProc(ref m);
return;
}
base.WndProc(ref m);
}
I've created something similiar and my solution is similar to what lAbstract suggests. The floatin form has an event DoReturnFocus wich the custom TextBox (the AutoCompleteTextBox control in this case) subscribes to, and simply sets focus back to itself. To prevent the main form to appear in front of the floating form I set the main form as the Owner of the floating form, as owned forms are never displayed behind their owner form..
More on Form.Owner Property
I'm having some issues getting a notification box to behave correctly in c#. Basically I'm showing a boarderless form in the lower right hand side of the screen, which displays a message for a few seconds and then disappears. The problem is that I need it to appear on top of other windows without it ever being able to steal focus. Ideally, I want it to be purely managed code, although looking through similar examples I doubt this will be possible.
At the moment I'm preventing it from stealing focus when calling Form.Show() with an override:
protected override bool ShowWithoutActivation // stops the window from stealing focus
{
get { return true; }
}
and then ignoring mouse clicks with:
private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATEANDEAT = 0x0004;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEACTIVATE)
{
m.Result = (IntPtr)MA_NOACTIVATEANDEAT;
return;
}
base.WndProc(ref m);
}
However I find that if I use these in conjunction with TopMost = true (which I need), it gains focus anyway, and if all other windows are minimised, it also gains focus.
So, is there any way to flat out prevent a form from ever gaining focus (whether via mouse click, alt-tab, etc), while still being the top most/second top most form? Even just giving focus immediately back to the window it stole it from would work (although introduce flickering).
Any suggestions would be greatly appreciated, I'm really stuck with this.
EDIT:
Ok, so I finally managed to get this working using:
protected override bool ShowWithoutActivation // stops the window from stealing focus
{
get { return true; }
}
// and
const int WS_EX_NOACTIVATE = 0x08000000;
const int WS_EX_TOPMOST = 0x00000008;
protected override CreateParams CreateParams
{
get
{
CreateParams param = base.CreateParams;
param.ExStyle |= WS_EX_TOPMOST; // make the form topmost
param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated
return param;
}
}
// and
[DllImport("user32.dll")]
private extern static IntPtr SetActiveWindow(IntPtr handle);
private const int WM_ACTIVATE = 6;
private const int WA_INACTIVE = 0;
private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATEANDEAT = 0x0004;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEACTIVATE)
{
m.Result = (IntPtr)MA_NOACTIVATEANDEAT; // prevent the form from being clicked and gaining focus
return;
}
if (m.Msg == WM_ACTIVATE) // if a message gets through to activate the form somehow
{
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);
}
}
}
I also added Form.Hide() to the GotFocus event so that even if it does somehow get focus, it simply closes and gets out of the users way asap.
Also, if anyone is wondering, the constants for all the window styles etc. can be found in WINUSER.H, its online at http://www.woodmann.com/fravia/sources/WINUSER.H if you can't find it.
However, if anyone can see a more elegant way of doing this, it would be appreciated.
Possibly WS_EX_NOACTIVATE extended window style is what you are looking for. Window with this style is not activated when clicked. For example, Virtual Keyboard window has this style.
To apply this style to window, override CreateParams function and change baseParams.ExStyle.
In WPF try this:
ShowActivated="False"
I'm not looking for points here as the original poster already posted a solution that worked for them but I wanted to share my experience with this problem. Using the solution above (which is at the bottom of the question instead of in answer form) gives me a Win32Exception: Error creating window handle error. when using the WndProc code as it is posted there. The ShowWithoutActivation and CreateParams part works great to prevent activation of a form and still keep it topmost.
I came up with an alternate solution to preventing a form from being clicked using SetWindowLong which makes the form transparent and therefore it can be clicked through and SetLayeredWindowAttributes which sets the transparency back to normal so you can see the form again but still retain the ability to click through the form.
NOTE: You -CANNOT- interact with the form at all in this state and even trying to click the 'X' button will just click whatever is behind the form at that position so you will need to use code to close the form:
public partial class Form1 : Form
{
private enum GWL : int
{
ExStyle = -20
}
private enum WS_EX : int
{
Transparent = 0x20,
Layered = 0x80000
}
public enum LWA : int
{
ColorKey = 0x1,
Alpha = 0x2
}
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll")]
static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags);
protected override bool ShowWithoutActivation
{
get { return true; }
}
const int WS_EX_NOACTIVATE = 0x08000000;
const int WS_EX_TOPMOST = 0x00000008;
protected override CreateParams CreateParams
{
get
{
CreateParams param = base.CreateParams;
param.ExStyle |= WS_EX_TOPMOST; // make the form topmost
param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated
return param;
}
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Prevent form from showing up in taskbar which also prevents activation by Alt+Tab
this.ShowInTaskbar = false;
// Allow the form to be clicked through so that the message never physically interferes with work being done on the computer
SetWindowLong(this.Handle, (int)GWL.ExStyle, (int)WS_EX.Layered | (int)WS_EX.Transparent);
// Set the opacity of the form
byte nOpacity = 255; // 0 = invisible, 255 = solid, anything inbetween will show the form with transparency
SetLayeredWindowAttributes(this.Handle, 0, nOpacity, (uint)LWA.Alpha);
}
}
I was also able to get the original approach working by making a small change to the WndProc code above.
NOTE: This also makes the form unclickable but the behavior is different in that you can actually click the min, max and 'X' buttons but nothing happens when you do. The mouse cursor also changes when you are at the edge of the form as if to resize but it doesn't allow resizing:
public partial class Form1 : Form
{
protected override bool ShowWithoutActivation
{
get { return true; }
}
const int WS_EX_NOACTIVATE = 0x08000000;
const int WS_EX_TOPMOST = 0x00000008;
protected override CreateParams CreateParams
{
get
{
CreateParams param = base.CreateParams;
param.ExStyle |= WS_EX_TOPMOST; // make the form topmost
param.ExStyle |= WS_EX_NOACTIVATE; // prevent the form from being activated
return param;
}
}
[DllImport("user32.dll")]
private extern static IntPtr SetActiveWindow(IntPtr handle);
private const int WM_ACTIVATE = 6;
private const int WA_INACTIVE = 0;
private const int WM_MOUSEACTIVATE = 0x0021;
private const int MA_NOACTIVATEANDEAT = 0x0004;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEACTIVATE)
{
m.Result = (IntPtr)MA_NOACTIVATEANDEAT; // prevent the form from being clicked and gaining focus
return;
}
if (m.Msg == WM_ACTIVATE) // if a message gets through to activate the form somehow
{
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);
}
}
}
else
{
base.WndProc(ref m);
}
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// Prevent form from showing up in taskbar which also prevents activation by Alt+Tab
this.ShowInTaskbar = false;
}
}
I'm using SetWindowTheme and SendMessage to make a .net listview look like a vista style listview, but the .net control still has a dotted selection border around the selected item:
Selected items in the explorer listview don't have that border around them. How can I remove it?
Windows Explorer:
Edit: Solution:
public static int MAKELONG(int wLow, int wHigh)
{
int low = (int)LOWORD(wLow);
short high = LOWORD(wHigh);
int product = 0x00010000 * (int)high;
int makeLong = (int)(low | product);
return makeLong;
}
SendMessage(olv.Handle, WM_CHANGEUISTATE, Program.MAKELONG(UIS_SET, UISF_HIDEFOCUS), 0);
Telanors solution worked for me. Here's a slightly more self-contained version.
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class MyListView : ListView
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
private const int WM_CHANGEUISTATE = 0x127;
private const int UIS_SET = 1;
private const int UISF_HIDEFOCUS = 0x1;
public MyListView()
{
this.View = View.Details;
this.FullRowSelect = true;
// removes the ugly dotted line around focused item
SendMessage(this.Handle, WM_CHANGEUISTATE, MakeLong(UIS_SET, UISF_HIDEFOCUS), 0);
}
private int MakeLong(int wLow, int wHigh)
{
int low = (int)IntLoWord(wLow);
short high = IntLoWord(wHigh);
int product = 0x10000 * (int)high;
int mkLong = (int)(low | product);
return mkLong;
}
private short IntLoWord(int word)
{
return (short)(word & short.MaxValue);
}
}
Doing this the NON P/Invoke way...
Override your ListView control and add the following:
protected override void OnSelectedIndexChanged(EventArgs e)
{
base.OnSelectedIndexChanged(e);
Message m = Message.Create(this.Handle, 0x127, new IntPtr(0x10001), new IntPtr(0));
this.WndProc(ref m);
}
protected override void OnEnter(EventArgs e)
{
base.OnEnter(e);
Message m = Message.Create(this.Handle, 0x127, new IntPtr(0x10001), new IntPtr(0));
this.WndProc(ref m);
}
Setting the HotTracking property to true hides the focus rectangle. This repro-ed the Explorer style on my Win7 machine:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class MyListView : ListView {
public MyListView() {
this.HotTracking = true;
}
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
SetWindowTheme(this.Handle, "explorer", null);
}
[DllImport("uxtheme.dll", CharSet = CharSet.Auto)]
public extern static int SetWindowTheme(IntPtr hWnd, string appname, string subidlist);
}
Beware that getting the items underlined is a side-effect.
Does setting the ListView.ShowFocusCues property to false help?
I know this is rather old, and Windows Forms is antiquated now, but it's still in use and it's still an issue. Worse, none of these solution are elegant, and some don't even work at all.
Here's a very simple solution, when you create your own control that inherits the ListView, then just override the WndProc to never allow focus. It gets rid of all focus-related dotted selection boxes, item selection, subitem selection, etc...
using System.Windows.Forms;
public partial class NoSelectionListView : ListView
{
public NoSelectionListView()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x0007) //WM_SETFOCUS
{
return;
}
base.WndProc(ref m);
}
}
It does not seem that there is a particular way to change ListViewItem styles using Windows Forms.
Sometimes there is no way to change some Win32 control behaviors using managed code. The only way is to do some P/Invoke to modify specific behaviors. I find this really tricky but you have no other choice. I often faced this situation when developing Windows Mobile UIs (justly with ListView).
So I have no direct answer to your question but I am pretty sure that if it is not possible using Windows Forms, you can surely do with P/Invoke. The only clues I can give you:
Platform Invoke Tutorial
List View documentation
For me turning focus off didn't work until the control was shown.
I did this:
bool HideFocus { get; set; }
bool _hasEnter;
void OnEnter(object sender, EventArgs e)
{
if (!_hasEnter)
{
_hasEnter = true;
// Selection at startup wont change the actual focus
if (this.SelectedIndices.Count > 0)
this.Items[this.SelectedIndices[0]].Focused = true;
// Hide focus rectangle if requested
if (this.ShowFocusCues && this.HideFocus)
{
var lParam1 = MakeLong(UIS_SET, UISF_HIDEFOCUS);
SendMessage(this.Handle, WM_CHANGEUISTATE, lParam1, 0);
}
}
}
See https://stackoverflow.com/a/15768802/714557 above for the MakeLong call.
Also, any selected items before the control is shown, won't set the selection focus.
I basically used the "Set Focus" event to know that the control was shown and that it was actually getting focus, to make the corretions.