Capturing WndProc message of a certain button click - winforms

I have a cancel button on my form. I want to determine inside the WndProc method that this Cancel button is clicked and write some code for it. This is absolutely necessary because otherwise I'm not able to cancel all other control validation events that are yet to be performed.
Please help.
.NET - 2.0, WinForms

This is how you could parse the WndProc message for a left-click on a child control:
protected override void WndProc(ref Message m)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/hh454920(v=vs.85).aspx
// 0x210 is WM_PARENTNOTIFY
// 513 is WM_LBUTTONCLICK
if (m.Msg == 0x210 && m.WParam.ToInt32() == 513)
{
var x = (int)(m.LParam.ToInt32() & 0xFFFF);
var y = (int)(m.LParam.ToInt32() >> 16);
var childControl = this.GetChildAtPoint(new Point(x, y));
if (childControl == cancelButton)
{
// ...
}
}
base.WndProc(ref m);
}
BTW: this is 32-bit code.

And if there are controls which failed validation then CauseValidation does not help
Well, sure it does, that's what the property was designed to do. Here's an example form to show this at work. Drop a textbox and a button on the form. Note how you can click the button to clear the textbox, even though the box always fails its validation. And how you can close the form.
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
textBox1.Validating += new CancelEventHandler(textBox1_Validating);
button1.Click += new EventHandler(button1_Click);
button1.CausesValidation = false;
this.FormClosing += new FormClosingEventHandler(Form1_FormClosing);
}
private void textBox1_Validating(object sender, CancelEventArgs e) {
// Always fail validation
e.Cancel = true;
}
void button1_Click(object sender, EventArgs e) {
// Your Cancel button
textBox1.Text = string.Empty;
}
void Form1_FormClosing(object sender, FormClosingEventArgs e) {
// Allow the form to close even though validation failed
e.Cancel = false;
}
}

Related

Prevent RepositoryItemSearchLookUpEdit when Popup is Open When CloseUpKey.Key is pressed

I use a RepositoryItemSearchLookUpEdit. its CloseUpKey property is set to space
result.CloseUpKey = new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.Space);
I want to use this shortcut only for open popup and not for closing popup.
How can I achieve this?
UPDATE------------------------
First I create an RepositoryItemSearchLookUpEdit object
var result = new RepositoryItemSearchLookUpEdit();
result.CloseUpKey = new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.Space);
result.KeyDown += repositoryItemLookUpEdit_KeyDown;
result.CloseUp += repositoryItemLookUpEdit_CloseUp;
result.QueryCloseUp += repositoryItemLookUpEdit_QueryCloseUp;
private void repositoryItemLookUpEdit_QueryCloseUp(object sender, System.ComponentModel.CancelEventArgs e)
{
var edit = sender as SearchLookUpEdit;
KeyEventArgs k = new KeyEventArgs(edit.Properties.CloseUpKey.Key);
AttachKeyPressEvent(k);
if (k.KeyCode == edit.Properties.CloseUpKey.Key)
e.Cancel = true;
}
And pass it to a grid column:
grdListView.Columns["yyy"].ColumnEdit = result
How can I achieve that with these events, without creating a descendant SearchLookUpEdit
UPDATED:
The problem is that CloseUp event (where you could get the necessary info about the closeup key) occurs after the QueryCloseUp event (where you could precent the closing up event). Also the KeyPress, KeyDown and KeyUp events seem also NOT to occur when the QueryCloseUp occurs, as a result they couldn't be overridden. So I tried this, I created a custom KeyEventHandler and triggered him during QueryCloseUp event in order to get the necessary info of what key was pressed and cancel the event if the close key event was the one. Here is my codeTry it to see if it suits you
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//Here you can add your grid control as you have created
DataTable dt = new DataTable();
dt.Columns.Add("ID"); //use your own names and types
gridControl1.DataSource = dt;
var result = new RepositoryItemSearchLookUpEdit();
result.CloseUpKey = new DevExpress.Utils.KeyShortcut(System.Windows.Forms.Keys.Space);
result.QueryCloseUp += new CancelEventHandler(repositoryItemLookUpEdit_QueryCloseUp);
((gridControl1.MainView as GridView).Columns["ID"] as GridColumn).ColumnEdit = result;
}
private static readonly object myQueryCloseUp = new object();
public event KeyEventHandler MyQueryCloseUp
{
add { Events.AddHandler(myQueryCloseUp, value); }
remove { Events.RemoveHandler(myQueryCloseUp, value); }
}
protected virtual void AttachKeyPressEvent(KeyEventArgs e)
{
KeyEventHandler handler = (KeyEventHandler)Events[myQueryCloseUp];
if (handler != null)
handler(this, e);
}
//Here you add your own Handler implementation
public void repositoryItemLookUpEdit_QueryCloseUp(object sender, CancelEventArgs e)
{
KeyEventArgs k = new KeyEventArgs((sender as SearchLookUpEdit).Properties.CloseUpKey.Key);
AttachKeyPressEvent(k);
if (k.KeyCode == (sender as SearchLookUpEdit).Properties.CloseUpKey.Key)
e.Cancel = true;
}
}

How to ignore user clicks in WinForms?

When a user clicks a button, it starts some task. I don't want to block the main application thread, so I run it in a separate thread. Now I need to forbid a user to click the button until my task finishes.
I could set
button.Enabled = false;
, but I'm looking for some way to ignore clicks on it.
I could add some check in click event handler:
if (executingThread != null) return;
, but I will have to do it for each handler which is bad idea.
I know that there is some way to filter user's messages. Could you point me how to do this? And I don't want to filter out all messages, because some other buttons must stay clickable, I need to filter out messages that come to particular controls (buttons,grids and etc).
SOLUTION
internal class MessagesFilter: IMessageFilter
{
private readonly IntPtr ControlHandler;
private const int WM_KEYUP = 0x0101;
public MessagesFilter(IntPtr ControlHandler)
{
this.ControlHandler = ControlHandler;
}
#region IMessageFilter Members
public bool PreFilterMessage(ref Message m)
{
// TODO: Add MessagesFilter.PreFilterMessage implementation
if (m.Msg == WM_KEYUP)
{
if (m.HWnd == ControlHandler)
{
Keys k = ((Keys) ((int) m.WParam));
if (k == Keys.Enter)
return true;
}
}
return false;
}
#endregion
}
As always, the UI should be presented in such a way that user understands what the application is doing and should talk to the user with UI elements.
As Adam Houldsworth suggested I would also prefer keeping the button either disabled or enabled but I would also suggest that the caption of the button should convey the message to the user that the long processing is in progress when the new thread starts..and so the caption of the button should be immediately changed to something like "Processing..Please wait..." (in addition to being disabled or even if you want to keep it enabled), and then if you have kept the button enabled just check the caption of the button (or a isProcessing bool flag) on its click event to return if it says "Processing..Please wait..." or (isProcessing == true).
Lots of the Websites which help users to upload files/images change the Upload button's caption to "Uploading..Please wait..." to inform the user to wait until the upload finishes and additionally some sites also disable the upload button so that the user is not able to click again on Upload button.
You would need to also revert back the caption to normal when the thread finishes long processing.
There may be other advanced ways but the idea is to keep it as simple and basic as possible.
Look at this example on Threading in Windows Forms which shows to disable the button while multi-threading.
+1 for all the suggestions so far. As CSharpVJ suggests - My idea was to additionally inform the user by changing the button's caption making the UI design more intuitive
This can be achieved elegantly with Backgroundworker component in Winforms [No hassles code]. Just copy-paste and HIT F5 (After creating a New Winforms Project with a Button and a Label on it)!
You do not have to check anything related to button here. Everything will be taken care by the appropriate event handlers. its just that you have to do correct stuffs int he resepctive event handlers. Try it !
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form3 : Form
{
private BackgroundWorker _worker;
public Form3()
{
InitializeComponent();
InitWorker();
}
private void InitWorker()
{
if (_worker != null)
{
_worker.Dispose();
}
_worker = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_worker.DoWork += DoWork;
_worker.RunWorkerCompleted += RunWorkerCompleted;
_worker.ProgressChanged += ProgressChanged;
}
/// do time consuming work here...
void DoWork(object sender, DoWorkEventArgs e)
{
int highestPercentageReached = 0;
if (_worker.CancellationPending)
{
e.Cancel = true;
}
else
{
double i = 0.0d;
for (i = 0; i <= 199990000; i++)
{
// Report progress as a percentage of the total task.
var percentComplete = (int)(i / 199990000 * 100);
if (percentComplete > highestPercentageReached)
{
highestPercentageReached = percentComplete;
// Report UI abt the progress
_worker.ReportProgress(percentComplete);
_worker.CancelAsync();
}
}
}
}
void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Enabled = true;
if (e.Cancelled)
{
// Display some message to the user that task has been
// cancelled
label1.Text = "Cancelled the operation";
}
else if (e.Error != null)
{
// Do something with the error
}
button1.Text = "Start again";
}
void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = string.Format("Result {0}: Percent {1}",e.UserState, e.ProgressPercentage);
}
private void OnStartClick(object sender, System.EventArgs e)
{
_worker.RunWorkerAsync();
button1.Text = "Processing started...";
button1.Enabled = false;
}
}
}
As mentioned in other answers, there is probably a better solution than what you are asking for.
To directly answer your question, check out the IMessageFilter interface
Create your filter to have it suppress the mouse messages you don't desire, apply it when necessary using Application.AddMessageFilter().
Something along these lines (this should probably compile...):
public class MouseButtonFilter : IMessageFilter
{
private const int WM_LBUTTONDOWN = 0x0201;
private const int WM_LBUTTONUP = 0x0202;
private const int WM_LBUTTONDBLCLK = 0x0203;
private const int WM_RBUTTONDOWN = 0x0204;
private const int WM_RBUTTONUP = 0x0205;
private const int WM_RBUTTONDBLCLK = 0x0206;
private const int WM_MBUTTONDOWN = 0x0207;
private const int WM_MBUTTONUP = 0x0208;
bool IMessageFilter.PreFilterMessage(ref Message m)
{
switch (m.Msg)
{
case WM_LBUTTONDOWN:
/* case ... (list them all here; i'm being lazy) */
case WM_MBUTTONUP:
return true;
}
return false;
}
}

How to stop ResizeEnd event when form is moved?

I write certain code in my form ResizeEnd event. Now problem is when form is moved by clicking and dragging on the caption bar, ResizeEnd event is fired and code is executed even though form size is NOT changed.
I gone through MSDN documentation for Resizeend event and it says that event will fire when form is moved (don't understand why this happens when the size is NOT changed).
For resolution I put the if condition to check if size is changed like below to stop execution of code on form move:
int Prv_Height; int Prv_Width;
private void TemplateGrid_ResizeEnd(object sender, EventArgs e)
{
if (this.Size.Width != Prv_Width || this.Size.Height != Prv_Height)
{
Prv_Width = this.Size.Width;
Prv_Height = this.Size.Height;
//Other code here when form resize ends...
}
}
So is there any way to stop ResizeEnd event to fire when form is moved? or any other better approach to solve the problem?
You could move your check for sizechange to a new baseform. On derived forms the resizeEnd event will then only fire if the size is actually changed.
public partial class CustomForm : Form
{
private Size _prvSize;
public CustomForm()
{
InitializeComponent();
}
protected override void OnShown(EventArgs e)
{
_prvSize = this.Size;
base.OnShown(e);
}
protected override void OnResizeEnd(EventArgs e)
{
if (this.Size == _prvSize)
return;
_prvSize = this.Size;
base.OnResizeEnd(e);
}
}
private void Form1_ResizeBegin(object sender, EventArgs e)
{
oldSize = ClientSize;
}
private Size oldSize = new Size();
private void Form1_ResizeEnd(object sender, EventArgs e)
{
if (oldSize == ClientSize)
return;
//Add Something
}

Silverlight 4:How to delay Mouseenter event

i have a situation where : User moves mouse over the image .
If user keeps mouse on that image for specific time ex. 2 seconds then only i have to proceed
further in mouseenter event otherwise don't.
I have already refred to http://forums.silverlight.net/t/86671.aspx/1 but looks like mine is different case.
One option is to use a DispatchTimer to determine the length of the mouse over.
bool isMouseOverImage = false;
public void Image_MouseEnter(object sender, MouseEventArgs e)
{
this.isMouseOverImage = true;
var timer = new System.Windows.Threading.DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(2);
timer.Tick += (object timerSender, EventArgs timerArgs) =>
{
if(this.isMouseOverImage)
{
// write your code
}
// stop the timer
timer.Stop();
};
timer.Start();
}
public void Image_MouseLeave(object sender, MouseEventArgs e)
{
this.isMouseOverImage = false;
}
If you have multiple images, you should create a re-usable Behavior and attach it to each image. I can define code for that if that would help.

WPF: Button single click + double click issue

I have to handle both the single click and the double click of a button in a WPF application with different reaction.
Unfortunately, on a doubleclick, WPF fires two click event and a double click event, so it's hard to handle this situation.
It tried to solve it using a timer but without success...I hope you can help me.
Lets see the code:
private void delayedBtnClick(object statInfo)
{
if (doubleClickTimer != null)
doubleClickTimer.Dispose();
doubleClickTimer = null;
this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new VoidDelegate(delegate()
{
// ... DO THE SINGLE CLICK ACTION
}));
}
private void btn_Click(object sender, RoutedEventArgs e)
{
if (doubleClickTimer == null)
doubleClickTimer = new Timer(delayedBtnClick, null, System.Windows.Forms.SystemInformation.DoubleClickTime, Timeout.Infinite);
}
}
}
private void btnNext_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (doubleClickTimer != null)
doubleClickTimer.Change(Timeout.Infinite, Timeout.Infinite); // disable it - I've tried it with and without this line
doubleClickTimer.Dispose();
doubleClickTimer = null;
//.... DO THE DOUBLE CLICK ACTION
}
The problem is that the 'SINGLE CLICK ACTION' called after the 'DOUBLE CLICK ACTION' on doubleclick. It's strange that I set thedoubleClickTimer to null on double click but in the delayedBtnClick it's true :O
I've already tried to use longer time, a bool flag and lock...
Do you have any ideas?
Best!
If you set the RoutedEvent's e.Handled to true after handling the MouseDoubleClick event then it will not call the Click Event the second time after the MouseDoubleClick.
There's a recent post which touches on having different behaviors for SingleClick and DoubleClick which may be useful.
However, if you are sure you want separate behaviors and want/need to block the first Click as well as the second Click, you can use the DispatcherTimer like you were.
private static DispatcherTimer myClickWaitTimer =
new DispatcherTimer(
new TimeSpan(0, 0, 0, 1),
DispatcherPriority.Background,
mouseWaitTimer_Tick,
Dispatcher.CurrentDispatcher);
private void Button_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// Stop the timer from ticking.
myClickWaitTimer.Stop();
Trace.WriteLine("Double Click");
e.Handled = true;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
myClickWaitTimer.Start();
}
private static void mouseWaitTimer_Tick(object sender, EventArgs e)
{
myClickWaitTimer.Stop();
// Handle Single Click Actions
Trace.WriteLine("Single Click");
}
You could try this:
Button.MouseLeftButtonDown += Button_MouseLeftButtonDown;
private void Button_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
if (e.ClickCount > 1)
{
// Do double-click code
}
else
{
// Do single-click code
}
}
If neccessary, you could require mouse click and wait until mouse up to perform the action.

Resources