Basically I've set up a class to handle sending WM_SETREDRAW messages like so:
public static class DrawingLocker
{
[DllImport("user32", CharSet = CharSet.Auto)]
private extern static IntPtr SendMessage(IntPtr hWnd,
int msg, int wParam, IntPtr lParam);
private const int WM_SETREDRAW = 11; //0xB
public static void LockDrawing(IntPtr Handle)
{
SendMessage(Handle, WM_SETREDRAW, 0, IntPtr.Zero);
}
public static void UnlockDrawing(IntPtr Handle)
{
SendMessage(Handle, WM_SETREDRAW, 1, IntPtr.Zero);
}
}
I then have a Redraw method in my custom user control:
public void Redraw()
{
try
{
DrawingLocker.LockDrawing(Handle);
using (Graphics graphics = Graphics.FromHwnd(Handle))
{
//Draw Stuff
}
}
finally { DrawingLocker.UnlockDrawing(Handle); }
}
My problem is that nothing I draw where the "Draw Stuff" comment is gets drawn. What am I doing wrong?
(Redraw gets called when values that effect drawing change, including resize)
I'm not really in Windows and stuff, but judging by what MSDN says about that flag, it doesn't do what you think it does. It's used to disable redrawing controls (think a list view) while you change their contents. Disabling it inside the redraw function is probably not going to do anything.
See if you can find something related to "double buffering", because that's one technique used to avoid flicker.
Related
I have a Form with the FormBorderStyle set to None, and I have also made my own little GUI replacing the Title Bar, I'm trying to find a way to show the Menu that shows up whenever you right click the title bar or click the icon on the title bar
I have tried using this post but after calling the function nothing happened, I'm at a loss now and I cannot find more resources about this.
When the border style of Form is set to FormBorderStyle.None, GetSystemMenu() always returns a NULL handle.
Because you remove some important Window Styles from HWND when you set the border style to None, this function doesn't return a HMENU.
Probably, it does some checks if the window has a title bar or not. This is why it returns NULL.
The workaround for the issue is to get the menu handle before setting the FoormBorderStyle to None.
using System.Runtime.InteropServices;
public partial class MainForm : Form
{
public MainForm() => InitializeComponent();
[DllImport("user32.dll")]
private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
private static extern int TrackPopupMenu(IntPtr hMenu, uint uFlags, int x, int y,
int nReserved, IntPtr hWnd, IntPtr prcRect);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
private IntPtr hMenu;
private const int WM_SYSCOMMAND = 0x112;
protected override void OnHandleCreated(EventArgs e)
{
// Get the system menu and set the border style after that.
hMenu = GetSystemMenu(Handle, false);
FormBorderStyle = FormBorderStyle.None;
}
protected override void OnMouseUp(MouseEventArgs e)
{
int menuIdentifier = TrackPopupMenu(hMenu, 0x102, Control.MousePosition.X, Control.MousePosition.Y, 0, Handle, IntPtr.Zero);
if(menuIdentifier != 0)
{
PostMessage(Handle, WM_SYSCOMMAND, (IntPtr)menuIdentifier, IntPtr.Zero);
}
}
}
I am interested in removing the text selection of ComboBoxes with DropDownStyle = DropDown.
When I add/remove or close the DropPown, then the Item is selected.
I am not able to clear the selected text. Do you have some idea how to do this?
This code does not work:
comboBox.SelectionLenght = 0;
comboBox.SelectionStart = comboBox.Text.Legnth;
comboBox.Select(0,0);
I can see that the text is highlighted after this line:
selectedComboBox.Items.Add(redCompetitorName);
You can defer the execution of the Select() method, calling BeginInvoke() in the SelectedIndexChanged event handler (or SelectionChangedCommitted, if you want this to happen only when a User selects an Item manually).
By deferring the execution (this action is enqueued in the message loop), the Select() action is performed only after the ComboBox.Text has been set and highlighted. So your command is not overridden by the default behavior.
private void comboBox_SelectedIndexChanged(object sender, EventArgs e)
{
// Set the caret to the start of the ComboBox text
BeginInvoke(new Action(()=> comboBox.Select(0, 0)));
// OR - set the caret to the end of the text instead
BeginInvoke(new Action(()=> comboBox.Select(int.MaxValue, 0)));
}
This concept applies in other contexts of course.
You can use this method in other situations, when you need to perform an action, in the UI, that is executed only after the current (or the current sequence of the already scheduled actions) has completed.
If you want a more involved solution that prevents all kind of selection highlights in a ComboBox, you can use a Custom Control derived from ComboBox, get the Handle of its Edit Control, use a NativeWindow to intercept its messages. Override WndProc to handle EM_SETSEL and call PostMessage to remove the selection (only when the starting position is > 0, otherwise you risk to get stuck in a weird auto-loop which has an effect that's usually referred to as StackOverflow :).
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
[DesignerCategory("code")]
public class ComboBoxNoFocus : ComboBox
{
IntPtr editHandle = IntPtr.Zero;
private EditNativeWindow editControl = null;
public ComboBoxNoFocus() { }
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
editHandle = GetComboBoxEditInternal(this.Handle);
editControl = new EditNativeWindow(editHandle);
}
public class EditNativeWindow : NativeWindow
{
private const int EM_SETSEL = 0x0B1;
public EditNativeWindow() : this(IntPtr.Zero) { }
public EditNativeWindow(IntPtr handle)
{
if (handle != IntPtr.Zero) {
this.AssignHandle(handle);
}
}
protected override void WndProc(ref Message m)
{
switch (m.Msg) {
case EM_SETSEL:
int pos = m.LParam.ToInt32();
if (pos > 0) {
PostMessage(this.Handle, EM_SETSEL, 0, 0);
}
return;
default:
// Other operations
break;
}
base.WndProc(ref m);
}
}
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
internal static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);
[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 GetComboBoxEditInternal(IntPtr cboHandle)
{
var cbInfo = new COMBOBOXINFO();
cbInfo.Init();
GetComboBoxInfo(cboHandle, ref cbInfo);
return cbInfo.hwndEdit;
}
}
Set the focus to another control in your form after a new item is selected, using the SelectedIndexChanged event of the Combobox.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
button1.Focus();
}
I'm making a simple WinForm car race game. I've got two objects - cars, and they move on the form when key is pressed (Form1KeyDown_Event).
The only thing is, that when one player press a key, the other player cannot press his key (nothing happens). But when the first player releases the key, second player can press one his keys and normally control his car.
How can I listen for two player keys simultaneously? Should I use threads and have each car on its own thread?
Here's a simple example of what you can do in order to listen to several keys at the same time, using the keyup and keydown events instead.
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace WinFormTest {
public partial class Form1 : Form {
private readonly IDictionary<Keys, bool> downState;
public Form1() {
InitializeComponent();
downState = new Dictionary<Keys, bool>();
downState.Add(Keys.W, false);
downState.Add(Keys.D, false);
KeyDown += remember;
KeyUp += forget;
}
protected override void OnLoad(EventArgs e) {
base.OnLoad(e);
Timer timer = new Timer() { Interval = 100 };
timer.Tick += updateGUI;
timer.Start();
}
private void remember(object sender, KeyEventArgs e) {
downState[e.KeyCode] = true;
}
private void forget(object sender, KeyEventArgs e) {
downState[e.KeyCode] = false;
}
private void updateGUI(object sender, EventArgs e) {
label1.Text = downState[Keys.W] ? "Forward" : "-";
label2.Text = downState[Keys.D] ? "Right" : "-";
}
}
}
You might want to investigate going lower-level and using windows hooks to detect keyboard events. This requires P/Invoking into native methods, but is pretty straight-forward. The hook you'd want is WH_LL_KEYBOARD. Details can be found at pinvoke.net.
You'd need a bit of boilerplate, but it's as close to the keyboard events as you can reasonably expect to get:
[StructLayout(LayoutKind.Sequential)]
public struct KBDLLHOOKSTRUCT
{
public uint vkCode;
public uint scanCode;
public uint flags;
public uint time;
public IntPtr dwExtraInfo;
}
public delegate IntPtr LowLevelKeyboardProc(int, IntPtr, KBDLLHOOKSTRUCT);
[DllImport("kernel32.dll")]
public static extern uint GetCurrentThreadId();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(int idhook, LowLevelKeyboardProc proc, IntPtr hMod, uint threadId);
[DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
public static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (var curProc = Process.GetCurrentProcess())
using (var curMod = curProc.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curMod.ModuleName), 0u);
}
}
public IntPtr MyKeyboardHook(int code, IntPtr wParam, ref KBDLLHOOKSTRUCT keyboardInfo)
{
if (code < 0)
{
return CallNextHookEx(IntPtr.Zero, wParam, ref keyboardInfo);
}
// Do your thing with the keyboard info.
return CallNextHookEx(IntPtr.Zero, code, wParam, ref keyboardInfo);
}
Make sure to unhook your handler when your app stops needing it. The KBDLLHOOKSTRUCT encapsulates all the info Windows will give you about a keyboard event; details of its members can be found at MSDN.
One detail of this kind of hook is that it gets executed on the thread that registered it, so make sure you take note of that, and don't set it on the UI thread if it's going to do anything long-running.
What I am trying to do is to cause a control (in the same process, but which I have no control of) to redraw itself, and for my code to block until it finished redrawing.
I tried using UpdateWindow but that doesn't seem to wait for the redraw to finish.
The reason I need to wait for it to finish redrawing is that I would like to grab the screen afterwards.
The control is not a dotNet control, it's a regular windows control.
I've confirmed that:
The handle is correct.
UpdateWindow returns true.
Tried sending InvalidateRect(hWnd, IntPtr.Zero, true) just before the call to UpdateWindow to make sure the window needs invalidating.
Tried doing the same thing on the parent window of the control.
Code used:
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool InvalidateRect(IntPtr hWnd, IntPtr rect, bool bErase);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UpdateWindow(IntPtr hWnd);
public bool PaintWindow(IntPtr hWnd)
{
InvalidateRect(hWnd, IntPtr.Zero, true);
return UpdateWindow(hWnd);
}
//returns true
You can force application to process all the enqueued messages (including the WM_PAINT!) using Application.DoEvents. Something like this:
public bool PaintWindow(IntPtr hWnd)
{
InvalidateRect(hWnd, IntPtr.Zero, true);
if (UpdateWindow(hWnd))
{
Application.DoEvents();
return true;
}
return false;
}
But if you're going to grab screen anyway, wouldn't it better to kill two birds with one stone by sending WM_PRINT message?
You can do it by the following code:
internal static class NativeWinAPI
{
[Flags]
internal enum DrawingOptions
{
PRF_CHECKVISIBLE = 0x01,
PRF_NONCLIENT = 0x02,
PRF_CLIENT = 0x04,
PRF_ERASEBKGND = 0x08,
PRF_CHILDREN = 0x10,
PRF_OWNED = 0x20
}
internal const int WM_PRINT = 0x0317;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg,
IntPtr wParam, IntPtr lParam);
}
public static void TakeScreenshot(IntPtr hwnd, Graphics g)
{
IntPtr hdc = IntPtr.Zero;
try
{
hdc = g.GetHdc();
NativeWinAPI.SendMessage(hwnd, NativeWinAPI.WM_PRINT, hdc,
new IntPtr((int)(
NativeWinAPI.DrawingOptions.PRF_CHILDREN |
NativeWinAPI.DrawingOptions.PRF_CLIENT |
NativeWinAPI.DrawingOptions.PRF_NONCLIENT |
NativeWinAPI.DrawingOptions.PRF_OWNED
))
);
}
finally
{
if (hdc != IntPtr.Zero)
g.ReleaseHdc(hdc);
}
}
I am implementing a small application (observer) that needs to "attach" itself to the bottom of another window (observed). The latter is not a window inside the application.
At this moment I solved by getting the hWnd of the window and querying periodically in a thread the location of the observed window, moving the observer window accordingly.
However this is a very inelegant solution. What I would like to do is to listen to the resize event of the observed window so that the observer will react only when necessary.
I assume I should use a hook, and I found plenty of ways of doing it, but my lack of knowledge of the C WinAPI is blocking me in understanding which hook I need to create and how (pinvoke/parameters/etc).
I'm pretty sure this is quite trivial, and some of you familiar with C/C++ and WinAPI will have the answer ready at hand ;)
Thanks
Expanding on Chris Taylor's answer: Instead of doing the native interop yourself, you can use ManagedWinApi, which contains a Hook class.
EDIT: To use ManagedWinApi. Somewhere in your code:
Hook MyHook = new Hook(HookType.WH_CALLWNDPROC, false, false);
MyHook.Callback += MyHookCallback;
MyHook StartHook();
For the callback, reference CallWndProc and CWPSTRUCT:
private static int MyHookCallback(int code, IntPtr wParam, IntPtr lParam, ref bool callNext)
{
if (code >= 0)
{
// You will need to define the struct
var message = (CWPSTRUCT)Marshal.PtrToStructure(lParam, typeof(CWPSTRUCT));
// Do something with the data
}
return 0; // Return value is ignored unless you set callNext to false
}
A WH_CALLWNDPROC hook would probably suffice, this will allow you to monitor all messages destined for the window of interest.
Are you asking how to create a global hook using C# or are you happy to create the hook in C++ and then interop with that from .NET? The second option is the route I would go.
Basically off the top of my head, what I would do is the following
1- Create global hook in C, and export functions to InstallHook and UninstallHook, which can be called from your C# app using Interop. InstallHook take an hwnd of the window in your C# application.
2- Have the installed hook function post a custom message to the C# window provided in the call to InstallHook when ever there is a message you are interested in like WM_SIZE in your case.
3- In the C# application your window that receives the posted messages from the hook will override WndProc to handle the custom message.
That is an outline of one approach.
I suggest you use WinEvents:
public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
See also:event hooks
I ran into the same thing in some code I was working with and discovered I couldn't inject a managed .DLL into the process.
Not wanting to write a C++ unmanaged DLL that used SetWindowsHook, I went with a combination of SetWinEventHook, which signals when a window starts and ends a move event and a Timer to poll the window while it's moving to give it the appearance of following the window while it moves.
Here's a (very simplified) version of a class that can do that (just replace the TODO's with code to move the window or raise an event).
public class DockingHelper
{
private readonly uint m_processId, m_threadId;
private readonly IntPtr m_target;
// Needed to prevent the GC from sweeping up our callback
private readonly WinEventDelegate m_winEventDelegate;
private IntPtr m_hook;
private Timer m_timer;
public DockingHelper(string windowName, string className)
{
if (windowName == null && className == null) throw new ArgumentException("Either windowName or className must have a value");
m_target = FindWindow(className, windowName);
ThrowOnWin32Error("Failed to get target window");
m_threadId = GetWindowThreadProcessId(m_target, out m_processId);
ThrowOnWin32Error("Failed to get process id");
m_winEventDelegate = WhenWindowMoveStartsOrEnds;
}
[DllImport("user32.dll", SetLastError = true)]
private static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll")]
private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
private void ThrowOnWin32Error(string message)
{
int err = Marshal.GetLastWin32Error();
if (err != 0)
throw new Win32Exception(err, message);
}
private RECT GetWindowLocation()
{
RECT loc;
GetWindowRect(m_target, out loc);
if (Marshal.GetLastWin32Error() != 0)
{
// Do something useful with this to handle if the target window closes, etc.
}
return loc;
}
public void Subscribe()
{
// 10 = window move start, 11 = window move end, 0 = fire out of context
m_hook = SetWinEventHook(10, 11, m_target, m_winEventDelegate, m_processId, m_threadId, 0);
}
private void PollWindowLocation(object state)
{
var location = GetWindowLocation();
// TODO: Reposition your window with the values from location (or fire an event with it attached)
}
public void Unsubscribe()
{
UnhookWinEvent(m_hook);
}
private void WhenWindowMoveStartsOrEnds(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (hwnd != m_target) // We only want events from our target window, not other windows owned by the thread.
return;
if (eventType == 10) // Starts
{
m_timer = new Timer(PollWindowLocation, null, 10, Timeout.Infinite);
// This is always the original position of the window, so we don't need to do anything, yet.
}
else if (eventType == 11)
{
m_timer.Dispose();
m_timer = null;
var location = GetWindowLocation();
// TODO: Reposition your window with the values from location (or fire an event with it attached)
}
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left, Top, Right, Bottom;
}
private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
}