Sync mouseclicks between controls - c#

I’ve got a TableLayoutPanel and a Treeview and I would like to sync the mouseclicks with each other.
The reason for this is that I want to be able to select something in my TableLayoutPanel and then it should also select something in the Treeview.
This is how it looks:
My first attempt works but there is some delay.
I hooked up my Treeview to the NodeMouseClick event and when that event fires I Refresh() the TableLayoutPanel so the CellPaint event gets called and paints the whole row. With this approach I’m seeing some delay because the Treeview gets painted first and then the TableLayoutPanel.
When I’m using the same method but the other way around (click the TableLayoutPanel and select the corresponding node in the Treeview) I don’t get AS MUCH delay. I’m guessing this is because it takes longer to paint my rows than it takes to select a node.
I tried a different solution:
class TableControl : TableLayoutPanel
{
TreeViewWithPaint m_TreeviewChild;
public void AddChildControl(TreeViewWithPaint treeview)
{
m_TreeviewChild = treeview;
}
protected override void WndProc(ref Message message)
{
const int WM_LBUTTONDOWN = 0x201;
switch (message.Msg)
{
case WM_LBUTTONDOWN:
//invalidate our table control so the OnPaint Method gets fired
this.Update();
//now copy the message and send it to the treeview
Message copy = new Message
{
HWnd = m_TreeviewChild.Handle,
LParam = message.LParam,
Msg = message.Msg,
Result = message.Result,
WParam = message.WParam
};
//pass the message onto the linked tree view
m_TreeviewChild.RecieveWndProc(ref copy);
break;
}
base.WndProc(ref message);
}
In my Treeview class I added this:
public void RecieveWndProc(ref Message m)
{
base.WndProc(ref m);
}
I got the idea of an example how to sync Treeview scrollbars
The problem with this is that the CellPaint event in the TableLayoutPanel isn’t getting fired anymore, even with Update()… It does however select the correct node in the Treeview:
I’m also foreseeing some problems with this if I try to implement the same thing in Treeview (overriding the WndProc), will this cause a crazy loop of copied messages?
So is there a(n) (easy) way to do this?
Thanks  

Solved it, instead of trying to send another click message to the TableLayoutPanel I just did all the painting in the Treeview WM_LBUTTONDOWN (I did the same for the TableLayoutPanel WM_LBUTTONDOWN message)
const int WM_LBUTTONDOWN = 0x201;
switch( message.Msg )
{
case WM_LBUTTONDOWN:
Int16 x = (Int16)message.LParam;
Int16 y = (Int16)((int)message.LParam >> 16);
//Getting the control at the correct position
Control control = m_TableControl.GetControlFromPosition(0, (y / 16));
if (control != null)
m_TableControl.Refresh();
TreeNode node = this.GetNodeAt(x, y);
this.SelectedNode = node;
break;
}

Related

C# Disable click/doubleClick or any WindowsMessage

My goal is to disable all DoubleClick() events in my application. Just simply unsubscribing from those events is not possbile, because those are external custom controls. So I am aiming for disabling either the DoubleClick() for those controls or my whole application, it doesn't really matter.
What I am trying to do is to intervene once the window gets a WindowsMessage WM and the ID number Message.Msg is the code of e.g. a Button.Click() event.
private const int WM_COMMAND = // ???
if (m.Msg == WM_COMMAND)
...
But no matter which WindowsMessage notification code I use, I don't get the correct one which gets fired once a control gets clicked. But I was abel to intervene on a DoubeClick() on the Form.
private const int WM_NCLBUTTONDBLCLK = 0x00A3;
private const int WM_LBUTTONDBLCLK = 0x0203;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_NCLBUTTONDBLCLK || m.Msg == WM_LBUTTONDBLCLK)
{
m.Result = IntPtr.Zero;
return;
}
base.WndProc(ref m);
}
This works totally fine and disabels a DoubleClick on the client area and non client area of the Form. But in which area do I locate when I am hovering a control? Because those WindowsMessages referred to either the client area and non client area dont' get fired when I am on a control.
Sent when the user clicks a button.
The parent window of the button receives this notification code through the WM_COMMAND message.
MSDN doc about a button click notifaction message
The WM_COMMAND message has this notfication code:
private const int WM_COMMAND = 0x0111;
So when I try to react to this message being sent I can't, because this message doesn't get fired.
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_COMMAND)
MessageBox.Show("foo"); // nothing happens
base.WndProc(ref m);
}
What do I miss or missunderstand about this WindowsMessage?
Since no one posted an answer yet and I found a well working solution here is my attempt to disable all DoubleClick()s or just for one/a few desired Control/s.
The problem is that WndProc() doesn't filter or respond to all WMs that are being sent. Therefore the WM for a default doubleClick on the non client area 0x0203 can't get detected. So I went deeper to already filter the WMs before they reach WndProc(). One way is to use a MessageFilter that is being assigned to my Application.
private MessageFilter msgFilter = new MessageFilter();
Application.AddMessageFilter(msgFilter);
The method Param is an object of my own class MessageFilter, which needs to implement the IMessageFilter Interface in order to use it's PreFilterMessage(). This method gets invoked for each single WM that gets send to your application.
class MessageFilter : IMessageFilter
{
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == 0x0203)
{
m.Result = IntPtr.Zero;
return true;
}
else
return false;
}
}
So basically once there is a WM with the desired Message.Msg ID, I set the Result to zero so the DoubleClick() is completely disabled for the whole application.
Later on it turned out it would be better to disable the DoubleClick() only from one Control. Since every Control in Windows has a unique Handle HWnd, which is part of the Message, I can use it to scan if the certain Message aims to my specific single Control.
When you create your Control either subscribe in the .Designer.cs to the event:
this.yourControl.HandleCreated += new System.EventHandler(this.yourControl_HandleCreated);
Or if it's a custom Control subscribe to the event after it's creation. In the event you assign the value of the created Handle to any instance your MessageFilter has access to, like a Property in this class.
private void yourControl_HandleCreated(object sender, EventArgs e)
{
msgFilter.OwnHandle = yourControl.Handle;
}
After that simply add a second condition to the if statement:
if (m.Msg == 0x0203 && OwnHandle == m.HWnd)
...

windows 8 tap and hold event c#

I'm trying to write an application that senses when someone taps and holds something. I am using windows forms. I tried using the mouse down even but it doesn't appear to fire all the time. This is also going to be a multi touch application. I'm going to have two buttons , and the user can tap and hold one button, while they press on the other button. Or Just press one button. I'm not even sure how a windows form app can handle that.
All the examples inhave seen for a windows touch app use xaml. Is this really the only way to capture tap and hold ??
I'm essentially making an onscreen keyboard here, and I don't think that isnpossible WITHOUT windows forms. Correct me if I am wrong here.
Any help or guidance in this is greatly appreciated. Thanks.
If your program is running on Windows 8, you can use the WM_POINTER API to get the input you need. Override WndProc to capture the messages. You will have to do some P/Invoke to get it working, but it's not terribly hard. Here's some incomplete code to get you started, you'll need to add cases for up, down, and update events for each type of pointer you want to track. Keep track of the pointer IDs to process multi touch. To handle the press-and-hold you'll need to track the time yourself from WM_POINTERDOWN to WM_POINTERUP and act accordingly. Hope this helps.
public const int WM_POINTERDOWN = 0x0246;
public const int WM_POINTERUP = 0x0247;
public const int WM_POINTERUPDATE = 0x0245;
public enum POINTER_INPUT_TYPE : int
{
PT_POINTER = 0x00000001,
PT_TOUCH = 0x00000002,
PT_PEN = 0x00000003,
PT_MOUSE = 0x00000004
}
public static uint GET_POINTERID_WPARAM(uint wParam) { return wParam & 0xFFFF; }
[DllImport("User32.dll")]
public static extern bool GetPointerType(uint pPointerID, out POINTER_INPUT_TYPE pPointerType);
protected override void WndProc(ref Message m)
{
bool handled = false;
uint pointerID;
POINTER_INPUT_TYPE pointerType;
switch(m.Message)
{
case WM_POINTERDOWN:
pointerID = User32.GET_POINTERID_WPARAM((uint)m.WParam);
if (User32.GetPointerType(pointerID, out pointerType))
{
switch (pointerType)
{
case POINTER_INPUT_TYPE.PT_PEN:
// Stylus Down
handled = true;
break;
case POINTER_INPUT_TYPE.PT_TOUCH:
// Touch down
handled = true;
break;
}
}
break;
}
if (handled)
m.Result = (IntPtr)1;
base.WndProc(ref m);
}
This question has been around for a while and might benefit from a simple approach. You can simulate the "tap and hold" (or click and hold) by measuring the time between the MouseDown event and the Click event (which fires before MouseUp). If the time is greater than some value then you cancel the Click and (perhaps) fire your own TapAndHold event. I have created a test control that anyone can use to try this approach out. Just add a UserControl to your test app (I called mine TestTapAndHold) and then paste in the following:
public partial class TestTapAndHold : UserControl
{
private string showText = "Tap Me";
private DateTime mouseDown;
private const int holdTime = 500;
public TestTapAndHold()
{
InitializeComponent();
this.Paint += drawText;
}
public delegate void OnTapAndHold(EventArgs e);
public event OnTapAndHold TapAndHold;
private void drawText(object sender, PaintEventArgs e)
{
using (var drawBrush = new SolidBrush(Color.Black))
{
e.Graphics.DrawString(showText, Font, drawBrush, new Point(5,3));
}
}
protected override void OnClick(EventArgs e)
{
if (DateTime.Now.Subtract(mouseDown).Milliseconds >= holdTime)
{
showText = "Tap Hold";
TapAndHold?.Invoke(e);
} else
{
base.OnClick(e);
showText = "Tapped";
}
Invalidate();
}
private void TestTapAndHold_MouseDown(object sender, MouseEventArgs e)
{
mouseDown = DateTime.Now;
}
}
Build the app and then pop one of the test controls onto a form. You can then add an event handler to your form like:
private void testTapAndHold1_TapAndHold(EventArgs e)
{
MessageBox.Show("You tapped and Held");
}
This general approach enabled me to add "Tap and Hold" functionality to a Windows Forms app running on a Microsoft Surface 4

C# - Capturing CTRL-Mouse wheel in WebBrowser control

I'm developing a Windows Forms application in C# with an embedded WebBrowser control to "dummy-proof" (i.e. disable context menus, back button, free navigation, etc.) access to a third-party web application.
Right now I'm trying to add the Zoom feature to my custom browser. I have the keyboard combos working for it (CTRL + and CTRL - make the correct OLE calls to the underlying ActiveX WebBrowser object), but among the other frustrating things about WebBrowser I've had to deal with, I can't seem to figure out how to capture CTRL-Mouse wheel to simulate the Zoom function like IE does. I've looked everywhere to find a solution to this but to no avail.
To try to figure it out, I created an empty form with just the WebBrowser control in it, and found the following:
CTRL-MouseWheel always fires the MouseWheel event when the parent form has focus and the mouse cursor is hovering over the top of the window (e.g. over the title of the application), or when the mouse cursor is hovering over the WebBrowser control when it does not appear to have focus even though the parent form has focus.
CTRL-MouseWheel never fires the MouseWheel event when the mouse cursor is hovering over the WebBrowser control and WebBrowser has focus, and there seems to be no effect.
Using the mouse wheel without CTRL scrolls the window contents of WebBrowser but does not fire the MouseWheel event until the vertical scroll bar has fully reached either the top or the bottom.
Intercepting the Message for WM_MOUSEWHEEL by overriding WndProc and DefWndProc both for a sample class inherited from WebBrowser and for the parent form applies only for the above conditions (with wParam properly denoting MK_CONTROL).
The PreviewKeyDown event fires when CTRL is pressed, as expected, but still does nothing in unison with the mouse wheel.
So I guess the Message is being processed below the parent form and the managed control level and does not bubble up to where I can intercept or even handle it. Is there a way to do this, or some other way to simulate zooming in and out using CTRL-MouseWheel?
Thanks for reading!
First cast the WebBrowser.Document.DomDocument to the right interface in the mshtml namespace, like mshtml.HTMLDocumentEvents2_Event, then you can handle (and cancel) mousewheel events. I'm not sure, but I think you need to wire up the event handler anytime the document is changed, so I do it on the WebBrowser.DocumentCompleted event. I'm also not sure if you need to release any COM objects.
This was frustrating enough that I got it to work and stopped caring...
Here is at least one document explaining the basics: How to handle document events in a Visual C# .NET application
For your specific case, just conditionally squash the onmousewheel event, based on whether or not the CTRL key is pressed.
private void webBrowser_DocumentCompleted(object sender,
WebBrowserDocumentCompletedEventArgs e)
{
if (webBrowser.Url.ToString() == "about:blank")
return;
var docEvents = (mshtml.HTMLDocumentEvents2_Event)webBrowser.Document.DomDocument;
docEvents.onmousewheel -= docEvents_onmousewheel; //may not be necessary?
docEvents.onmousewheel += docEvents_onmousewheel;
}
bool docEvents_onmousewheel(mshtml.IHTMLEventObj pEvtObj)
{
if (pEvtObj.ctrlKey)
{
pEvtObj.cancelBubble = true; //not sure what this does really
pEvtObj.returnValue = false; //this cancels the event
return false; //not sure what this does really
}
else
return true; //again not sure what this does
}
Now if you need to know the Wheel Delta (scroll amount), you'll want to cast the events object to yet another interface.
bool docEvents_onmousewheel(mshtml.IHTMLEventObj pEvtObj)
{
var wheelEventObj = (mshtml.IHTMLEventObj4)pEvtObj;
var delta = wheelEventObj.wheelDelta;
[...]
}
Perhaps using SetWindowsHookEx to look for these events may work for you. This is what I've used to get scroll wheel events on top of the ActiveX MapPoint control.
Be aware there are some quirks with this on Windows 7 that may require some tinkering. For more details do a search for SetWindowsHookEx on Windows 7 on the MSDN forums.
To solve this problem you have to listen for and handle these messages:
OLECMDID_GETZOOMRANGE
OLECMDID_OPTICAL_GETZOOMRANGE
OLECMDID_OPTICAL_ZOOM
OLECMDID_ZOOM
They're dispatched by Internet Explorer. See the remarks on MSDN.
This is the code I used to disable ctrl+shift: You need to change the behavior of WndProc in the deepest control "Internet Explorer_Server",
Do this after your web browser is ready:
IntPtr wbHandle = Win32.FindWindowEx(this.wbMain.Handle, IntPtr.Zero, "Shell Embedding", String.Empty);
wbHandle = Win32.FindWindowEx(wbHandle, IntPtr.Zero, "Shell DocObject View", String.Empty);
wbHandle = Win32.FindWindowEx(wbHandle, IntPtr.Zero, "Internet Explorer_Server", String.Empty);
WbInternal wb = new WbInternal(wbHandle);
class WbInternal : NativeWindow
{
public WbInternal(IntPtr handle)
{
this.AssignHandle(handle);
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEWHEEL)
{
if (((int)m.WParam & 0x00FF) == MK_SHIFT)
{
return;
}
}
base.WndProc(ref m);
}
}
You can find more message about WM_MOUSEWHEEL from MSDN.
I find this from MSDN. But I forgot the link, Once find, will append it here.
I couldn't get any of these to work reliably so after some (frustrating) experimentation, I came up with a derivative of the answer posted by TCC. My webbrowser control is hosted in a usercontrol. The main differences are I use a class-level variable for the HTMLDocumentEvents2_Event so I can unsubscribe successfully, and I set the mshtml.IHTMLEventObj pEvtObj.Returnvalue to true.. seems to work well now..
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_wbData = (WebBrowser)FindElement("DataBrowser");
_horzScroll = (ScrollBar)FindElement("HorizontalScroll");
_vertScroll = (ScrollBar)FindElement("VerticalScroll");
_wbData.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(OnLoadCompleted);
_horzScroll.Scroll += new ScrollEventHandler(OnHorizontalScroll);
_vertScroll.Scroll += new ScrollEventHandler(OnVerticalScroll);
LoadDefault();
EnableSoundEffects(SoundEffects);
}
private void OnHorizontalScroll(object sender, ScrollEventArgs e)
{
// _wbData.Handle
mshtml.HTMLDocument htmlDoc = _wbData.Document as mshtml.HTMLDocument;
_horzPosition = (int)e.NewValue;
if (htmlDoc != null && htmlDoc.body != null)
htmlDoc.parentWindow.scroll(_horzPosition, _vertPosition);
}
private void OnVerticalScroll(object sender, ScrollEventArgs e)
{
mshtml.HTMLDocument htmlDoc = _wbData.Document as mshtml.HTMLDocument;
_vertPosition = (int)e.NewValue;
if (htmlDoc != null && htmlDoc.body != null)
htmlDoc.parentWindow.scroll(_horzPosition, _vertPosition);
}
private void OnLoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
mshtml.HTMLDocument htmlDoc = _wbData.Document as mshtml.HTMLDocument;
if (htmlDoc != null && htmlDoc.body != null)
{
mshtml.IHTMLElement2 body = (mshtml.IHTMLElement2)htmlDoc.body;
_horzScroll.Visibility = Visibility.Collapsed;
if (body.scrollHeight > _wbData.ActualHeight)
_vertScroll.Visibility = Visibility.Visible;
else
_vertScroll.Visibility = Visibility.Collapsed;
_vertScroll.ViewportSize = _wbData.ActualHeight;
_vertScroll.Maximum = body.scrollHeight - (_wbData.ActualHeight - 8);
_eventHelper = (HTMLDocumentEvents2_Event)_wbData.Document;
_eventHelper.onmousewheel -= OnMouseWheel;
_eventHelper.onmousewheel += new HTMLDocumentEvents2_onmousewheelEventHandler(OnMouseWheel);
}
}
private bool OnMouseWheel(mshtml.IHTMLEventObj pEvtObj)
{
mshtml.HTMLDocument htmlDoc = _wbData.Document as mshtml.HTMLDocument;
var wheelEventObj = (mshtml.IHTMLEventObj4)pEvtObj;
var delta = wheelEventObj.wheelDelta;
if (htmlDoc != null && htmlDoc.body != null && wheelEventObj != null)
{
_vertPosition += (int)wheelEventObj.wheelDelta;
htmlDoc.parentWindow.scroll(_horzPosition, _vertPosition);
}
pEvtObj.returnValue = true;
return true;
}

How can you make a DataGridView scroll one item at a time using the mouse wheel?

We'd like to override DataGridView's default behavior when using a mouse wheel with this control. By default, the DataGridView scrolls a number of rows equal the SystemInformation.MouseWheelScrollLines setting. What we'd like to do is scroll just one item at a time.
(We display images in the DataGridView, which are somewhat large. Because of this scroll three rows (a typical system setting) is too much, often causing the user to scroll to items they can't even see.)
I've tried a couple things already and haven't had much success so far. Here are some issues I've run into:
You can subscribe to MouseWheel events but there's no way to mark the event as handled and do my own thing.
You can override OnMouseWheel but this never appears to be called.
You might be able to correct this in the base scrolling code but it sounds like a messy job since other types of scrolling (e.g. using the keyboard) come through the same pipeline.
Anyone have a good suggestion?
Here's the final code, using the wonderful answer given:
/// <summary>
/// Handle the mouse wheel manually due to the fact that we display
/// images, which don't work well when you scroll by more than one
/// item at a time.
/// </summary>
///
/// <param name="sender">
/// sender
/// </param>
/// <param name="e">
/// the mouse event
/// </param>
private void mImageDataGrid_MouseWheel(object sender, MouseEventArgs e)
{
// Hack alert! Through reflection, we know that the passed
// in event argument is actually a handled mouse event argument,
// allowing us to handle this event ourselves.
// See http://tinyurl.com/54o7lc for more info.
HandledMouseEventArgs handledE = (HandledMouseEventArgs) e;
handledE.Handled = true;
// Do the scrolling manually. Move just one row at a time.
int rowIndex = mImageDataGrid.FirstDisplayedScrollingRowIndex;
mImageDataGrid.FirstDisplayedScrollingRowIndex =
e.Delta < 0 ?
Math.Min(rowIndex + 1, mImageDataGrid.RowCount - 1):
Math.Max(rowIndex - 1, 0);
}
I just did a little scrounging and testing of my own. I used Reflector to investigate and discovered a couple things. The MouseWheel event provides a MouseEventArgs parameter, but the OnMouseWheel() override in DataGridView casts it to HandledMouseEventArgs. This also works when handling the MouseWheel event. OnMouseWheel() does indeed get called, and it is in DataGridView's override that it uses SystemInformation.MouseWheelScrollLines.
So:
You could indeed handle the MouseWheel event, casting MouseEventArgs to HandledMouseEventArgs and set Handled = true, then do what you want.
Subclass DataGridView, override OnMouseWheel() yourself, and try to recreate all the code I read here in Reflector except for replacing SystemInformation.MouseWheelScrollLines with 1.
The latter would be a huge pain because it uses a number of private variables (including references to the ScrollBars) and you'd have replace some with your own and get/set others using Reflection.
I would subclass the DataGridView into my own custom control (you know, add a new Windows Forms --> Custom Control file and change the base class from Control to DataGridView).
public partial class MyDataGridView : DataGridView
Then override the WndProc method and substitute something like so:
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x20a)
{
int wheelDelta = ((int)m.WParam) >> 16;
// 120 = UP 1 tick
// -120 = DOWN 1 tick
this.FirstDisplayedScrollingRowIndex -= (wheelDelta / 120);
}
else
{
base.WndProc(ref m);
}
}
Of course, you'll have the check that you don't set FirstDisplayedScrollingRowIndex to a number outside of the range of your grid etc. But this works quite well!
Richard
Overriding OnMouseWheel and not calling base.OnMouseWheel should work. Some wheel mice have special settings that you may need to set yourself for it to work properly. See this post http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=126295&SiteID=1
UPDATE: Since I've now learned that the DataGridView has a MouseWheel event, I've added a second, simpler override.
One way to accomplish this is to subclass the DataGridView and override the WndProc to add special handling of the WM_MOUSEWHEEL message.
This example catches the mouse wheel movement and replaces it with a call to SendKeys.Send.
(This is a little different than just scrolling, since it also selects the next/previous row of the DataGridView. But it works.)
public class MyDataGridView : DataGridView
{
private const uint WM_MOUSEWHEEL = 0x20a;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MOUSEWHEEL)
{
var wheelDelta = ((int)m.WParam) >> 16;
if (wheelDelta < 0)
{
SendKeys.Send("{DOWN}");
}
if (wheelDelta > 0)
{
SendKeys.Send("{UP}");
}
return;
}
base.WndProc(ref m);
}
}
2nd take (with the same caveats as mentioned above):
public class MyDataGridView : DataGridView
{
protected override void OnMouseWheel(MouseEventArgs e)
{
if (e.Delta < 0)
SendKeys.Send("{DOWN}");
else
SendKeys.Send("{UP}");
}
}

How do I make a Windows Forms control readonly?

Returning to WinForms in VS2008 after a long time.. Tinkering with a OOD problem in VS2008 Express Edition.
I need some controls to be "display only" widgets. The user should not be able to change the value of these controls... the widgets are updated by a periodic update tick event. I vaguely remember there being a ReadOnly property that you could set to have this behavior... can't find it now.
The Enabled property set to false: grays out the control content. I want the control to look normal.
The Locked property set to false: seems to be protecting the user from accidentally distorting the control in the Visual Form Designer.
What am I missing?
For some typical winforms controls:
http://jquiz.wordpress.com/2007/05/29/c-winforms-readonly-controls/
This is also a good tip to preserve the appearance:
Color clr = textBox1.BackColor;
textBox1.ReadOnly = true;
textBox1.BackColor = clr;
To make the forms control Readonly instantly on one click do use the following peice of Code :
public void LockControlValues(System.Windows.Forms.Control Container)
{
try
{
foreach (Control ctrl in Container.Controls)
{
if (ctrl.GetType() == typeof(TextBox))
((TextBox)ctrl).ReadOnly = true;
if (ctrl.GetType() == typeof(ComboBox))
((ComboBox)ctrl).Enabled= false;
if (ctrl.GetType() == typeof(CheckBox))
((CheckBox)ctrl).Enabled = false;
if (ctrl.GetType() == typeof(DateTimePicker))
((DateTimePicker)ctrl).Enabled = false;
if (ctrl.Controls.Count > 0)
LockControlValues(ctrl);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
Then call it from your Button Click Event like this :
LockControlValues(this)
Hope, this helps to solve your problem :
Happy Programming,
Rajan Arora
www.simplyrajan.co.nr
Textbox
.ReadOnly property to true
Controls without ReadOnly
Other control do not have all the time the ReadOnly property. You will require to play with the Events to take off the editing process and keeping your value not editable.
Two relevant properties ReadOnly and Enabled. ReadOnly = true prevents editing grays out the background, but it still allows focus. Enabled = false grays out the background, text and prevents editing or focus.
Windows UI conventions dicate giving the user a visual cue that a control is readonly (that way they won't attempt to edit it and be subsequently frustrated). The grayed out disabled state is the defined system convention, but it's arguable too much of a cue (and not a legibile enough one).
The simplest route is probababy to set your control to ReadOnly, set the background to System.Drawing.SystemColors.Window and then block focus messages. You could do this by catching OnEnter events and immediately moving Focus to another control that's not readonly (say, a Close or Edit button). Or you could derive your own control and eat any WM_SETFOCUS messages. Example below.
I believe various third-party control sets give you additional options and granularity.
public class ReadOnlyTextBox : TextBox
{
const uint WM_SETFOCUS = 0x0007;
public ReadOnlyTextBox()
{
this.ReadOnly = true;
this.BackColor = System.Drawing.SystemColors.Window;
this.ForeColor = System.Drawing.SystemColors.WindowText;
}
protected override void WndProc(ref Message m)
{
// eat all setfocus messages, pass rest to base
if (m.Msg != WM_SETFOCUS)
base.WndProc(ref m);
}
}
I was given this same requirement at work yesterday. Except instead of a textbox I had to make an entire form disabled without changing it's color.
So I replaced a call to
form->Enabled = false;
with
IntPtr hWnd = form->Handle;
HWND window_handle = (HWND)hWnd.ToPointer();
::EnableWindow(window_handle, aEnable ? TRUE:FALSE);
Which worked well. You can see above that I am using managed C++. The entire form is now disabled, but not greyed out.

Categories

Resources