I have a form which has a Combo Box Control. I have selected the drop down style property to DropDown. I have also set the DropDown Width to 250. I have set the auto complete mode to suggest and the auto complete source to listitems. it works absolutely fine when i click on the drop down. but when i type in somethin, the auto complete mode activates a drop down which has a small width.
any help appreciate. i wanna know how to increase the width of the auto complete drop down via code so that the list items are viewed properly. I am using C#.
I had asked this a couple of months back but didn't get a proper answer. now the customer wants it bad :(
??
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
/// <summary>
/// Represents an ComboBox with additional properties for setting the
/// size of the AutoComplete Drop-Down window.
/// </summary>
public class ComboBoxEx : ComboBox
{
private int acDropDownHeight = 106;
private int acDropDownWidth = 170;
//<EditorBrowsable(EditorBrowsableState.Always), _
[Browsable(true), Description("The width, in pixels, of the auto complete drop down box"), DefaultValue(170)]
public int AutoCompleteDropDownWidth
{
get { return acDropDownWidth; }
set { acDropDownWidth = value; }
}
//<EditorBrowsable(EditorBrowsableState.Always), _
[Browsable(true), Description("The height, in pixels, of the auto complete drop down box"), DefaultValue(106)]
public int AutoCompleteDropDownHeight
{
get { return acDropDownHeight; }
set { acDropDownHeight = value; }
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
ACWindow.RegisterOwner(this);
}
#region Nested type: ACWindow
/// <summary>
/// Provides an encapsulation of an Auto complete drop down window
/// handle and window proc.
/// </summary>
private class ACWindow : NativeWindow
{
private static readonly Dictionary<IntPtr, ACWindow> ACWindows;
#region "Win API Declarations"
private const UInt32 WM_WINDOWPOSCHANGED = 0x47;
private const UInt32 WM_NCDESTROY = 0x82;
private const UInt32 SWP_NOSIZE = 0x1;
private const UInt32 SWP_NOMOVE = 0x2;
private const UInt32 SWP_NOZORDER = 0x4;
private const UInt32 SWP_NOREDRAW = 0x8;
private const UInt32 SWP_NOACTIVATE = 0x10;
private const UInt32 GA_ROOT = 2;
private static readonly List<ComboBoxEx> owners;
[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr GetAncestor(IntPtr hWnd, UInt32 gaFlags);
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
[DllImport("user32.dll")]
private static extern void GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll")]
private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy,
uint uFlags);
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);
#region Nested type: EnumThreadDelegate
private delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
#endregion
#region Nested type: RECT
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public readonly int Left;
public readonly int Top;
public readonly int Right;
public readonly int Bottom;
public Point Location
{
get { return new Point(Left, Top); }
}
}
#endregion
#endregion
private ComboBoxEx owner;
static ACWindow()
{
ACWindows = new Dictionary<IntPtr, ACWindow>();
owners = new List<ComboBoxEx>();
}
/// <summary>
/// Creates a new ACWindow instance from a specific window handle.
/// </summary>
private ACWindow(IntPtr handle)
{
AssignHandle(handle);
}
/// <summary>
/// Registers a ComboBoxEx for adjusting the Complete Dropdown window size.
/// </summary>
public static void RegisterOwner(ComboBoxEx owner)
{
if ((owners.Contains(owner)))
{
return;
}
owners.Add(owner);
EnumThreadWindows(GetCurrentThreadId(), EnumThreadWindowCallback, IntPtr.Zero);
}
/// <summary>
/// This callback will receive the handle for each window that is
/// associated with the current thread. Here we match the drop down window name
/// to the drop down window name and assign the top window to the collection
/// of auto complete windows.
/// </summary>
private static bool EnumThreadWindowCallback(IntPtr hWnd, IntPtr lParam)
{
if ((GetClassName(hWnd) == "Auto-Suggest Dropdown"))
{
IntPtr handle = GetAncestor(hWnd, GA_ROOT);
if ((!ACWindows.ContainsKey(handle)))
{
ACWindows.Add(handle, new ACWindow(handle));
}
}
return true;
}
/// <summary>
/// Gets the class name for a specific window handle.
/// </summary>
private static string GetClassName(IntPtr hRef)
{
var lpClassName = new StringBuilder(256);
GetClassName(hRef, lpClassName, 256);
return lpClassName.ToString();
}
/// <summary>
/// Overrides the NativeWindow's WndProc to handle when the window
/// attributes changes.
/// </summary>
protected override void WndProc(ref Message m)
{
if ((m.Msg == WM_WINDOWPOSCHANGED))
{
// If the owner has not been set we need to find the ComboBoxEx that
// is associated with this dropdown window. We do it by checking if
// the upper-left location of the drop-down window is within the
// ComboxEx client rectangle.
if ((owner == null))
{
Rectangle ownerRect = default(Rectangle);
var acRect = new RECT();
foreach (ComboBoxEx cbo in owners)
{
GetWindowRect(Handle, ref acRect);
ownerRect = cbo.RectangleToScreen(cbo.ClientRectangle);
if ((ownerRect.Contains(acRect.Location)))
{
owner = cbo;
break; // TODO: might not be correct. Was : Exit For
}
}
owners.Remove(owner);
}
if (((owner != null)))
{
SetWindowPos(Handle, IntPtr.Zero, -5, 0, owner.AutoCompleteDropDownWidth,
owner.AutoCompleteDropDownHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
}
}
if ((m.Msg == WM_NCDESTROY))
{
ACWindows.Remove(Handle);
}
base.WndProc(ref m);
}
}
#endregion
}
This is what I did and it actually works really well. Good to find an answer atlast :)
This answer is an addition to reggie's answer.
If you want the user to be able to resize the auto-complete dropdown, then add the following code inside the WndProc method:
private const int WM_SIZING = 0x214;
if (m.Msg == WM_SIZING) {
if (owner != null) {
RECT rr = (RECT) Marshal.PtrToStructure(m.LParam, typeof(RECT));
owner.acDropDownWidth = (rr.Right - rr.Left);
owner.acDropDownHeight = (rr.Bottom - rr.Top);
}
}
kind of a bad design decision to do that. Why not set it to a static large size to start with? You can always use one of the events to get the text width and then use that to set the combobox width. Possibly the onPaint? easier way might be to create your own combobox class that inherits from combo box and then override the methods to do this yourself.
Related
So I've started fiddling around with VSB to, generally be better. I really want to learn, but I feel either the information I can find is outdated, or the code just doesn't work for me, for whatever reason. But, to the problem:
I want to be able to: Be able to click on a tab inside my VSB project, once that tab is clicked, there's a panel. Inside that panel, I want for example notepad to open, maximized to the panel window, docked and not able to move it (notepad).
I would like to do the same for other programs as well. My current code is the basic that opens notepad in a new window. I've only just begun poking at VSB so my knowledge is very limited.
I was able to do this in VSB (No C#) but not for C3
Current Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Software_Solution_C__Project__v._10._0._0
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
AboutBox1 myForm = new AboutBox1();
myForm.ShowDialog();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
Process.Start("mspaint.exe");
}
}
}
I tried to google, I tried different solutions I found, tried to find my way around, but it either crashed or gave endless error messages rendering me unable to do it.
Edit:
I've also tried the following code:
namespace Software_Solution_C__Project__v._10._0._0
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
AboutBox1 myForm = new AboutBox1();
myForm.ShowDialog();
}
private const int WM_SYSCOMMAND = 274; private const int SC_MAXIMIZE = 61488;
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
private void panel1_Paint(object sender, PaintEventArgs e)
{
Process proc;
proc = Process.Start("Notepad.exe");
proc.WaitForInputIdle();
SetParent(proc.MainWindowHandle, panel1.Handle);
//SendMessage(proc.MainWindowHandle, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
}
}
}
The problem here is, notepad does open in the panel, but not stretched / docked to fit window, and if I move the window, another instance of notepad opens. And if I close notepad, it just reopens again.
After layers of hard work, I've built a "panel1" in "form1" to realize this function.
You can refer to the following code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
/// <summary>
/// Embed external exe
/// </summary>
public class EmbeddedExeTool
{
[DllImport("User32.dll", EntryPoint = "SetParent")]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll", EntryPoint = "ShowWindow")]
private static extern int ShowWindow(IntPtr hwnd, int nCmdShow);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);
[DllImport("user32.dll")]
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
IntPtr WindowHandle = IntPtr.Zero;
private const int WS_THICKFRAME = 262144;
private const int WS_BORDER = 8388608;
private const int GWL_STYLE = -16;
private const int WS_CAPTION = 0xC00000;
private Process proApp = null;
private Control ContainerControl = null;
private const int WS_VISIBLE = 0x10000000;
[DllImport("user32.dll", EntryPoint = "SetWindowLong", CharSet = CharSet.Auto)]
private static extern IntPtr SetWindowLongPtr32(HandleRef hWnd, int nIndex, int dwNewLong);
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", CharSet = CharSet.Auto)]
private static extern IntPtr SetWindowLongPtr64(HandleRef hWnd, int nIndex, int dwNewLong);
private IntPtr SetWindowLong(HandleRef hWnd, int nIndex, int dwNewLong)
{
if (IntPtr.Size == 4)
{
return SetWindowLongPtr32(hWnd, nIndex, dwNewLong);
}
return SetWindowLongPtr64(hWnd, nIndex, dwNewLong);
}
/// <summary>
/// Load an external exe program into the program container
/// </summary>
/// <param name="control">To display the container control of the exe</param>
/// <param name="exepath">The full absolute path of the exe</param>
public void LoadEXE(Control control, string exepath)
{
ContainerControl = control;
control.SizeChanged += Control_SizeChanged;
ProcessStartInfo info = new ProcessStartInfo(exepath);
info.WindowStyle = ProcessWindowStyle.Minimized;
info.UseShellExecute = false;
info.CreateNoWindow = false;
proApp = Process.Start(info);
Application.Idle += Application_Idle;
EmbedProcess(proApp, control);
}
/// <summary>
/// Load an external exe program into the program container
/// </summary>
/// <param name="form">The form to display the exe</param>
/// <param name="exepath">The full absolute path of the exe</param>
public void LoadEXE(Form form, string exepath)
{
ContainerControl = form;
form.SizeChanged += Control_SizeChanged;
proApp = new Process();
proApp.StartInfo.UseShellExecute = false;
proApp.StartInfo.CreateNoWindow = false;
proApp.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
proApp.StartInfo.FileName = exepath;
proApp.StartInfo.Arguments = Process.GetCurrentProcess().Id.ToString();
proApp.Start();
Application.Idle += Application_Idle;
EmbedProcess(proApp, form);
}
/// <summary>
/// Make sure the application embeds this container
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Application_Idle(object sender, EventArgs e)
{
if (this.proApp == null || this.proApp.HasExited)
{
this.proApp = null;
Application.Idle -= Application_Idle;
return;
}
if (proApp.MainWindowHandle == IntPtr.Zero) return;
Application.Idle -= Application_Idle;
EmbedProcess(proApp, ContainerControl);
}
/// <summary>
/// Embed the specified program into the specified control
/// </summary>
private void EmbedProcess(Process app, Control control)
{
// Get the main handle
if (app == null || app.MainWindowHandle == IntPtr.Zero || control == null) return;
try
{
// Put it into this form
SetParent(app.MainWindowHandle, control.Handle);
// Remove border and whatnot
SetWindowLong(new HandleRef(this, app.MainWindowHandle), GWL_STYLE, WS_VISIBLE);
ShowWindow(app.MainWindowHandle, (int)ProcessWindowStyle.Maximized);
MoveWindow(app.MainWindowHandle, 0, 0, control.Width, control.Height, true);
}
catch (Exception ex3)
{
Console.WriteLine(ex3.Message);
}
}
/// <summary>
/// Embed container resize event
/// </summary>
private void Control_SizeChanged(object sender, EventArgs e)
{
if (proApp == null)
{
return;
}
if (proApp.MainWindowHandle != IntPtr.Zero && ContainerControl != null)
{
MoveWindow(proApp.MainWindowHandle, 0, 0, ContainerControl.Width, ContainerControl.Height, true);
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
EmbeddedExeTool exetool = new EmbeddedExeTool();
exetool.LoadEXE(panel1, "notepad.exe"); //The specific path to embed the external exe
}
/// <summary>
/// The dock of the panel needs to be set to full
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void panel1_Paint(object sender, PaintEventArgs e)
{
}
}
}
If you have any questions, we can discuss them together.
I have a small desktop app where I'm using a mousehook to monitor a mouse click event inside and outside a window's form. So I want a Custom Control to raise an event whenever a mouse is click out of that control, but when the control itself clicked, or child controls within this custom control clicked, I want to ignore the event, or act differently.
The problem is all mousehooks I found so far, has no way of knowing the control window that is about to receive a click event.
I need to know which control window is about to receive the click event ahead of time, so that I can differentiate whether it is a child control of the Custom Control or any other control or outside of the form.
Here is the mousehook class I'm using. I found it online.
using System;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Threading;
using Microsoft;
namespace library
{
/// <summary>
/// Abstract base class for Mouse and Keyboard hooks
/// </summary>
public abstract class GlobalHook
{
/// <summary>
///
/// </summary>
#region Windows API Code
[StructLayout(LayoutKind.Sequential)]
protected class POINT
{
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
protected class MouseHookStruct
{
public POINT pt;
public int hwnd;
public int wHitTestCode;
public int dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
protected class MouseLLHookStruct
{
public POINT pt;
public int mouseData;
public int flags;
public int time;
public int dwExtraInfo;
}
[StructLayout(LayoutKind.Sequential)]
protected class KeyboardHookStruct
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
protected static extern int SetWindowsHookEx(
int idHook,
HookProc lpfn,
IntPtr hMod,
int dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
protected static extern int UnhookWindowsHookEx(int idHook);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
protected static extern int CallNextHookEx(
int idHook,
int nCode,
IntPtr wParam,
IntPtr lParam);
[DllImport("user32")]
protected static extern int ToAscii(
int uVirtKey,
int uScanCode,
byte[] lpbKeyState,
byte[] lpwTransKey,
int fuState);
[DllImport("user32")]
protected static extern int GetKeyboardState(byte[] pbKeyState);
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
protected static extern short GetKeyState(int vKey);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetThreadDesktop(uint dwThreadId);
protected delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
protected const int WH_MOUSE_LL = 14;
protected const int WH_KEYBOARD_LL = 13;
protected const int WH_MOUSE = 7;
protected const int WH_KEYBOARD = 2;
protected const int WM_MOUSEMOVE = 0x200;
protected const int WM_LBUTTONDOWN = 0x201;
protected const int WM_RBUTTONDOWN = 0x204;
protected const int WM_MBUTTONDOWN = 0x207;
protected const int WM_LBUTTONUP = 0x202;
protected const int WM_RBUTTONUP = 0x205;
protected const int WM_MBUTTONUP = 0x208;
protected const int WM_LBUTTONDBLCLK = 0x203;
protected const int WM_RBUTTONDBLCLK = 0x206;
protected const int WM_MBUTTONDBLCLK = 0x209;
protected const int WM_MOUSEWHEEL = 0x020A;
protected const int WM_KEYDOWN = 0x100;
protected const int WM_KEYUP = 0x101;
protected const int WM_SYSKEYDOWN = 0x104;
protected const int WM_SYSKEYUP = 0x105;
protected const byte VK_SHIFT = 0x10;
protected const byte VK_CAPITAL = 0x14;
protected const byte VK_NUMLOCK = 0x90;
protected const byte VK_LSHIFT = 0xA0;
protected const byte VK_RSHIFT = 0xA1;
protected const byte VK_LCONTROL = 0xA2;
protected const byte VK_RCONTROL = 0x3;
protected const byte VK_LALT = 0xA4;
protected const byte VK_RALT = 0xA5;
protected const byte LLKHF_ALTDOWN = 0x20;
#endregion
/// <summary>
///
/// </summary>
#region Private Variables
protected int _hookType;
protected int _handleToHook;
protected bool _isStarted;
protected HookProc _hookCallback;
#endregion
/// <summary>
///
/// </summary>
#region Properties
public bool IsStarted
{
///
get
{
return _isStarted;
}
}
#endregion
/// <summary>
///
/// </summary>
#region Constructor
public GlobalHook()
{
///
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
}
#endregion
/// <summary>
///
/// </summary>
#region Methods
public void Start()
{
///
if (!_isStarted && _hookType != 0)
{
// Make sure we keep a reference to this delegate!
// If not, GC randomly collects it, and a NullReference exception is thrown
_hookCallback = new HookProc(HookCallbackProcedure);
//
IntPtr pp = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
///
_handleToHook = SetWindowsHookEx(
_hookType,
_hookCallback,
(IntPtr)0/*Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0])*/,
/*0*/(int)GetThreadDesktop((uint)Thread.CurrentThread.ManagedThreadId));
/// Were we able to sucessfully start hook?
if (_handleToHook != 0)
{
///
_isStarted = true;
}
}
}
public void Stop()
{
///
if (_isStarted)
{
///
UnhookWindowsHookEx(_handleToHook);
///
_isStarted = false;
}
}
protected virtual int HookCallbackProcedure(int nCode, IntPtr wParam, IntPtr lParam)
{
// This method must be overriden by each extending hook
return 0;
}
protected void Application_ApplicationExit(object sender, EventArgs e)
{
///
if (_isStarted)
{
///
Stop();
}
}
#endregion
}
/// <summary>
/// Captures global mouse events
/// </summary>
public class MouseHook : GlobalHook
{
/// <summary>
///
/// </summary>
#region MouseEventType Enum
private enum MouseEventType
{
None,
MouseDown,
MouseUp,
DoubleClick,
MouseWheel,
MouseMove
}
#endregion
/// <summary>
///
/// </summary>
#region Events
public event MouseEventHandler MouseDown;
public event MouseEventHandler MouseUp;
public event MouseEventHandler MouseMove;
public event MouseEventHandler MouseWheel;
public event EventHandler Click;
public event EventHandler DoubleClick;
#endregion
/// <summary>
///
/// </summary>
#region Constructor
public MouseHook()
{
_hookType = WH_MOUSE_LL;
}
#endregion
/// <summary>
///
/// </summary>
#region Methods
protected override int HookCallbackProcedure(int nCode, IntPtr wParam, IntPtr lParam)
{
///
if (nCode > -1 && (MouseDown != null || MouseUp != null || MouseMove != null))
{
///
MouseLLHookStruct mouseHookStruct =
(MouseLLHookStruct)Marshal.PtrToStructure(lParam, typeof(MouseLLHookStruct));
///
MouseButtons button = GetButton((Int32)wParam);
MouseEventType eventType = GetEventType((Int32)wParam);
///
MouseEventArgs e = new MouseEventArgs(
button,
(eventType == MouseEventType.DoubleClick ? 2 : 1),
mouseHookStruct.pt.x,
mouseHookStruct.pt.y,
(eventType == MouseEventType.MouseWheel ?
(short)((mouseHookStruct.mouseData >> 16) & 0xffff) : 0));
// Prevent multiple Right Click events (this probably happens for popup menus)
if (button == MouseButtons.Right && mouseHookStruct.flags != 0)
{
///
eventType = MouseEventType.None;
}
///
switch (eventType)
{
///
//Control cont = Control.FromHandle(wParam);
///
case MouseEventType.MouseDown:
///
if (MouseDown != null)
{
///
MouseDown(this, e);
}
break;
case MouseEventType.MouseUp:
if (Click != null)
{
///
Click(this, new EventArgs());
}
if (MouseUp != null)
{
///
MouseUp(this, e);
}
break;
case MouseEventType.DoubleClick:
if (DoubleClick != null)
{
///
DoubleClick(this, new EventArgs());
}
break;
case MouseEventType.MouseWheel:
if (MouseWheel != null)
{
///
MouseWheel(this, e);
}
break;
case MouseEventType.MouseMove:
if (MouseMove != null)
{
///
MouseMove(this, e);
}
break;
default:
break;
}
}
///
return CallNextHookEx(_handleToHook, nCode, wParam, lParam);
}
private MouseButtons GetButton(Int32 wParam)
{
///
switch (wParam)
{
///
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_LBUTTONDBLCLK:
return MouseButtons.Left;
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDBLCLK:
return MouseButtons.Right;
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDBLCLK:
return MouseButtons.Middle;
default:
return MouseButtons.None;
}
}
private MouseEventType GetEventType(Int32 wParam)
{
///
switch (wParam)
{
///
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
return MouseEventType.MouseDown;
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
return MouseEventType.MouseUp;
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
return MouseEventType.DoubleClick;
case WM_MOUSEWHEEL:
return MouseEventType.MouseWheel;
case WM_MOUSEMOVE:
return MouseEventType.MouseMove;
default:
return MouseEventType.None;
}
}
#endregion
}
}
So I have a DateTimePicker and I want to set it to null when save it into a database, but by default it has the today's value. If I want to remove it and empty the DateTimePicker I am not able too.
I've tried to put it into the load form:
dateTimePicker1.Format = DateTimePickerFormat.Custom;
dateTimePicker1.CustomFormat = " ";
It actually works, but if I want to set a date it will not display into the DateTimePicker. Do you have any idea how can I solve it?
I have once written a date time picker that accepts null values (and allows week numbers too, as a free bonus).
[DefaultEvent("ValueChanged")]
[ComVisible(true)]
[DefaultProperty("Value")]
[DefaultBindingProperty("Value")]
[ClassInterface(ClassInterfaceType.AutoDispatch)]
[Designer("System.Windows.Forms.Design.DateTimePickerDesigner, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
public class NullableDateTimePicker : DateTimePicker
{
#region Externals
private const int GWL_STYLE = (-16);
private const int MCM_FIRST = 0x1000;
private const int MCM_GETMINREQRECT = (MCM_FIRST + 9);
private const int MCS_WEEKNUMBERS = 0x4;
private const int DTM_FIRST = 0x1000;
private const int DTM_GETMONTHCAL = (DTM_FIRST + 8);
[DllImport("User32.dll")]
public static extern int GetWindowLong(IntPtr h, int index);
[DllImport("User32.dll")]
public static extern int SetWindowLong(IntPtr h, int index, int value);
[DllImport("User32.dll")]
private static extern IntPtr SendMessage(IntPtr h, int msg, int param, int data);
[DllImport("User32.dll")]
private static extern int SendMessage(IntPtr h, int msg, int param, ref Rectangle data);
[DllImport("User32.dll")]
private static extern int MoveWindow(IntPtr h, int x, int y, int width, int height, bool repaint);
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
private static extern IntPtr GetParent(IntPtr hWnd);
#endregion
#region General
public NullableDateTimePicker()
{
this.ShowCheckBox = true;
}
#endregion
#region Properties
/// <summary>
/// Gets or sets the date/time value assigned to the control.
/// </summary>
/// <exception cref="System.ArgumentOutOfRangeException">
/// The set value is less than System.Windows.Forms.DateTimePicker.MinDate or more than System.Windows.Forms.DateTimePicker.MaxDate.
/// </exception>
[RefreshProperties(RefreshProperties.All)]
[Bindable(true)]
public new DateTime? Value
{
get
{
if (!base.Checked)
{
return null;
}
return base.Value;
}
set
{
if (value.HasValue)
{
base.Checked = true;
if (this.Format == DateTimePickerFormat.Short)
{
base.Value = value.Value.Date;
}
else if (this.Format == DateTimePickerFormat.Time)
{
base.Value = default(DateTime).Add(value.Value.TimeOfDay);
}
else
{
base.Value = value.Value;
}
}
else
{
base.Checked = false;
}
}
}
/// <summary>
/// Gets or sets whether to show week numbers.
/// </summary>
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[DefaultValue(false)]
public bool ShowWeekNumbers
{
get;
set;
}
#endregion
#region Week numbers
/// <summary>
/// Raises the System.Windows.Forms.DateTimePicker.DropDown event.
/// </summary>
/// <param name="e">An System.EventArgs that contains the event data.</param>
protected override void OnDropDown(EventArgs e)
{
IntPtr monthView = SendMessage(this.Handle, DTM_GETMONTHCAL, 0, 0);
int style = GetWindowLong(monthView, GWL_STYLE);
if (this.ShowWeekNumbers)
{
style = style | MCS_WEEKNUMBERS;
}
else
{
style = style & ~MCS_WEEKNUMBERS;
}
Rectangle rect = new Rectangle();
SetWindowLong(monthView, GWL_STYLE, style);
SendMessage(monthView, MCM_GETMINREQRECT, 0, ref rect);
MoveWindow(monthView, 0, 0, rect.Right + 3, rect.Bottom, true);
//
// Resize the surrounding window to let the new text fit
//
IntPtr parent = GetParent(monthView);
Rectangle mainRect = new Rectangle();
SendMessage(parent, MCM_GETMINREQRECT, 0, ref mainRect);
MoveWindow(parent, 0, 0, mainRect.Right + 6, mainRect.Bottom + 6, true);
base.OnDropDown(e);
}
#endregion
}
It shows a checkbox to allow null values, and the new Value property allows null too, so it works from both the designer and code.
first I would like to stress that I am looking for a Windows DESKTOP WPF solution, so please no android. Thank you.
Second, I am fairly new to WPF and designing software for Windows Tablet, so please bear with me... And please do be explicit in your answers.
Third, the target software will run on a Windows Tablet (Win 8.0/8.1) and is being developed with Visual Studio 2013 Pro (C#, Desktop App, WPF).
OK, here is my problem:
I have been using a Textbox inherited class to show the Soft Keyboard when the user touches the Textbox (class code bellow). This part actually works well.
My problem is that the Soft Keyboard may popup over my actual control. In WinRT this would not happen since the OS seems to scroll the said control up until it becomes visible, but with DESKTOP app no such feature exists.
So my question is: does anyone know how to solve this overlap issue?
How could I (in Win desktop app) replicate the normal WinRT behavior?
Should I be using some other API to call the Soft Keyboard?
Code for the "TouchTextbox" class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace WPF_DT_Soft_Keyboard
{
class TouchTextbox : TextBox
{
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_CLOSE = 0xF060;
protected override void OnGotFocus(System.Windows.RoutedEventArgs e)
{
base.OnGotFocus(e);
ShowSoftKeyboard();
}
protected override void OnLostFocus(System.Windows.RoutedEventArgs e)
{
base.OnLostFocus(e);
HideSoftKeyboard();
}
protected override void OnPreviewKeyUp(System.Windows.Input.KeyEventArgs e)
{
base.OnPreviewKeyUp(e);
if (e.Key == System.Windows.Input.Key.Enter) HideSoftKeyboard();
}
private void ShowSoftKeyboard()
{
System.Diagnostics.Process.Start(#"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe");
}
private void HideSoftKeyboard()
{
int iHandle = FindWindow("IPTIP_Main_Window", "");
if (iHandle > 0)
{
SendMessage(iHandle, WM_SYSCOMMAND, SC_CLOSE, 0);
}
}
}
}
Ok, I think I finally found a decent solution.
I have encapsulated it into the TouchTextbox class bellow.
For it to work properly, the TouchTextbox must be contained withing a [ScrollViewer] control.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Threading;
namespace WPF_DT_Soft_Keyboard
{
class TouchTextbox : TextBox
{
/// <summary>
/// Private Container
/// </summary>
DispatcherTimer mBringIntoViewTimer = new DispatcherTimer();
/// <summary>
/// DLL imports
/// </summary>
[DllImport("user32.dll")]
public static extern int FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
[DllImport("user32.dll")]
public static extern int MoveWindow(int hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll")]
public static extern int SetWindowPos(int hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
[DllImport("Kernel32.dll")]
public static extern uint GetLastError();
/// <summary>
/// Constante
/// </summary>
public const int WM_SYSCOMMAND = 0x0112;
public const int SC_CLOSE = 0xF060;
public const int HWND_TOP = 0;
public const int SWP_NOSIZE = 0x0001;
public const int SWP_NOZORDER = 0x0004;
public const int SWP_SHOWWINDOW = 0x0040;
public const int BRING_INTO_VIEW_DELAY = 500;
/// <summary>
/// TouchTextbox
/// </summary>
public TouchTextbox()
{
mBringIntoViewTimer.IsEnabled = false;
mBringIntoViewTimer.Tick += MyTimer_Tick;
mBringIntoViewTimer.Interval = new TimeSpan(0, 0, 0, 0, BRING_INTO_VIEW_DELAY);
}
/// <summary>
/// MyTimer_Tick
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MyTimer_Tick(object sender, EventArgs e)
{
mBringIntoViewTimer.IsEnabled = false;
this.BringIntoView();
}
/// <summary>
/// OnGotFocus
/// </summary>
/// <param name="e"></param>
protected override void OnGotFocus(System.Windows.RoutedEventArgs e)
{
base.OnGotFocus(e);
ShowSoftKeyboard();
mBringIntoViewTimer.IsEnabled = true;
}
///// <summary>
///// OnLostFocus
///// </summary>
///// <param name="e"></param>
//protected override void OnLostFocus(System.Windows.RoutedEventArgs e)
//{
// base.OnLostFocus(e);
// HideSoftKeyboard();
//}
/// <summary>
/// OnPreviewKeyUp
/// </summary>
/// <param name="e"></param>
protected override void OnPreviewKeyUp(System.Windows.Input.KeyEventArgs e)
{
base.OnPreviewKeyUp(e);
if (e.Key == System.Windows.Input.Key.Enter) HideSoftKeyboard();
}
/// <summary>
/// ShowSoftKeyboard
/// </summary>
private void ShowSoftKeyboard()
{
Microsoft.Win32.Registry.CurrentUser.OpenSubKey(#"Software\Microsoft\TabletTip\1.7", true).SetValue("EdgeTargetDockedState", "1", Microsoft.Win32.RegistryValueKind.DWord);
System.Diagnostics.Process.Start(#"C:\Program Files\Common Files\Microsoft Shared\ink\TabTip.exe");
}
/// <summary>
/// HideSoftKeyboard
/// </summary>
private void HideSoftKeyboard()
{
int iHandle = FindWindow("IPTIP_Main_Window", "");
if (iHandle > 0)
{
SendMessage(iHandle, WM_SYSCOMMAND, SC_CLOSE, 0);
}
}
}
}
I am using Visual Studio 2010 (C#) and a Windows Forms application.
I have two treeviews side by side, and I have figured out how to synchronise the scrolling using the up/down buttons on the scrollbar, but when I use the slider it does not move the other treeview. I have taken a listview example that works, but the same code does not work for treeviews.
So far I have, in the main form:
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);
private void myListBox1_Scroll(ref Message m)
{
SendMessage(myListBox2.Handle, (uint)m.Msg, (uint)m.WParam, (uint)m.LParam);
}
I have created a control:
public partial class MyTreeView : TreeView
{
public MyTreeView()
{
InitializeComponent();
}
public event ScrollEventHandler Scroll;
public delegate void ScrollEventHandler(ref Message m);
private const int WM_VSCROLL = 0x115;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
if (m.Msg == WM_VSCROLL)
if (Scroll != null)
{
Scroll(ref m);
}
base.WndProc(ref m);
}
}
which I add two of to the form.
I can use the same code to have a listivew control the treeview and that will work if you drag the slider, but in reverse it only works with the up down buttons.
Rather than use SendMessage and mark your DLL as unsafe you can use the GetScrollPos and SetScrollPos functions from user32.dll.
I've wrapped the code up into your MyTreeView class so it's nicely encapsulated.
You just need to call the AddLinkedTreeView method like so:
treeView1.AddLinkedTreeView(treeView2);
Here's the source for the MyTreeView class.
public partial class MyTreeView : TreeView
{
public MyTreeView() : base()
{
}
private List<MyTreeView> linkedTreeViews = new List<MyTreeView>();
/// <summary>
/// Links the specified tree view to this tree view. Whenever either treeview
/// scrolls, the other will scroll too.
/// </summary>
/// <param name="treeView">The TreeView to link.</param>
public void AddLinkedTreeView(MyTreeView treeView)
{
if (treeView == this)
throw new ArgumentException("Cannot link a TreeView to itself!", "treeView");
if (!linkedTreeViews.Contains(treeView))
{
//add the treeview to our list of linked treeviews
linkedTreeViews.Add(treeView);
//add this to the treeview's list of linked treeviews
treeView.AddLinkedTreeView(this);
//make sure the TreeView is linked to all of the other TreeViews that this TreeView is linked to
for (int i = 0; i < linkedTreeViews.Count; i++)
{
//get the linked treeview
var linkedTreeView = linkedTreeViews[i];
//link the treeviews together
if (linkedTreeView != treeView)
linkedTreeView.AddLinkedTreeView(treeView);
}
}
}
/// <summary>
/// Sets the destination's scroll positions to that of the source.
/// </summary>
/// <param name="source">The source of the scroll positions.</param>
/// <param name="dest">The destinations to set the scroll positions for.</param>
private void SetScrollPositions(MyTreeView source, MyTreeView dest)
{
//get the scroll positions of the source
int horizontal = User32.GetScrollPos(source.Handle, Orientation.Horizontal);
int vertical = User32.GetScrollPos(source.Handle, Orientation.Vertical);
//set the scroll positions of the destination
User32.SetScrollPos(dest.Handle, Orientation.Horizontal, horizontal, true);
User32.SetScrollPos(dest.Handle, Orientation.Vertical, vertical, true);
}
protected override void WndProc(ref Message m)
{
//process the message
base.WndProc(ref m);
//pass scroll messages onto any linked views
if (m.Msg == User32.WM_VSCROLL || m.Msg == User32.WM_MOUSEWHEEL)
{
foreach (var linkedTreeView in linkedTreeViews)
{
//set the scroll positions of the linked tree view
SetScrollPositions(this, linkedTreeView);
//copy the windows message
Message copy = new Message
{
HWnd = linkedTreeView.Handle,
LParam = m.LParam,
Msg = m.Msg,
Result = m.Result,
WParam = m.WParam
};
//pass the message onto the linked tree view
linkedTreeView.RecieveWndProc(ref copy);
}
}
}
/// <summary>
/// Recieves a WndProc message without passing it onto any linked treeviews. This is useful to avoid infinite loops.
/// </summary>
/// <param name="m">The windows message.</param>
private void RecieveWndProc(ref Message m)
{
base.WndProc(ref m);
}
/// <summary>
/// Imported functions from the User32.dll
/// </summary>
private class User32
{
public const int WM_VSCROLL = 0x115;
public const int WM_MOUSEWHEEL = 0x020A;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetScrollPos(IntPtr hWnd, System.Windows.Forms.Orientation nBar);
[DllImport("user32.dll")]
public static extern int SetScrollPos(IntPtr hWnd, System.Windows.Forms.Orientation nBar, int nPos, bool bRedraw);
}
}
Edit: Added forwarding of the WM_MOUSEWHEEL message as per MinnesotaFat's suggestion.
I have done this with TextBoxes before, but I think the solution should work for you as well:
// Get/Set Scroll positions of a control handle
private unsafe Win32.POINT GetScrollPos(System.IntPtr myHandle)
{
Win32.POINT res = new Win32.POINT();
IntPtr ptr = new IntPtr(&res);
Win32.SendMessage(myHandle, Win32.EM_GETSCROLLPOS, 0, ptr);
return res;
}
private unsafe void SetScrollPos(Win32.POINT point, System.IntPtr myHandle)
{
IntPtr ptr = new IntPtr(&point);
Win32.SendMessage(myHandle, Win32.EM_SETSCROLLPOS, 0, ptr);
}
Win32 details
public const int WM_USER = 0x400;
public const int EM_GETSCROLLPOS = (WM_USER + 221);
public const int EM_SETSCROLLPOS = (WM_USER + 222);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
}
[DllImport("user32")] public static extern int SendMessage(
HWND hwnd, int wMsg, int wParam, IntPtr lParam);
Then just attached to both of the ListView scrolled events and do something like this:
private void ListView1Scrolled(object sender, System.EventArgs e)
{
SetScrollPos(GetScrollPos(ListView1.Handle), ListView2.Handle);
}
private void ListView2Scrolled(object sender, System.EventArgs e)
{
SetScrollPos(GetScrollPos(ListView2.Handle), ListView1.Handle);
}
DoctaJonez' answer works marvellously. For completeness, if you add another condition to the if statement in the WndProc method, you can handle the mouse wheel scrolling events as well:
if (m.Msg == WM_VSCROLL || m.Msg == WM_MOUSEWHEEL)
And declare WM_MOUSEWHEEL:
private cont int WM_MOUSEWHEEL = 0x020A;