WPF toplevel MenuItem enable/disable based on task - wpf

I have a top-level menu item which is responsible for refreshing a datagrid in the same window. My current control flow is:
User clicks on refresh
In the click event handler, I:
Disable the menuitem, by setting oMenuItem.IsEnabled = false.
Dispatch an action to refresh the grid and in that action, I re-enable the menuitem, by setting IsEnabled = true
The problem is that the user can click refresh even when it's disabled and it's as if the clicks get queued up. When the action returns it goes on to process the remaining, "queued-up" clicks. What I expect is: all clicks while the menuitem is disabled are ignored and only when it's enabled, the clicks are acknowledged.
The weird thing is that if I just disable it and never enable it it stays that way, i.e., it is disabled.wpf,

"Dispatch an action" you mean by calling Dispatcher.BeginInvoke() or some other kind of async opetation?
Anyway, in both cases you can get a "handle" to the operation (DispatcherOperation or IAsyncResult) and store it as a field when you dispatch your operation. When it completes - set this field to null.
In the click event handler of the menu-item check this field. If it's null it means it is safe to start the operation. If it is not null - return immediately and do nothing.
And something not related to your question but important - why not use Commands? That way you don't need to play with event handling and enabling/disabling. And of course commands can be invoked by multiple means (for example - the user selected the command from the menu using the keyboard and pressed Enter. No mouse clicks involved, but should do the same as clicking the menu item).
Alex.

Related

Handling/capturing a button click event as a priority in WinForms

This is the mock-up of a child window that I have in a Windows Forms application.
This will be shown when a button is clicked on the parent window.
When the child form opens up, the focus is in Text box1.
The Leave event on the Text box1 fires when the user tabs out of it or clicks on any button.
This triggers a validation:
if(String.IsNullOrEmpty(textBox1.Text)
{
MessageBox.Show("Please enter a value of Id.");
}
else {
... other logic omitted for brevity
}
Now the user can also click on the Cancel button to exit the form without doing any action.
Here, on the first try, the Leave event fires because the focus is in Text box1 when the Form is loaded so when the user clicks on Cancel for the first time the form does not close, instead the message box with validation message is shown.
On the second click, since the focus is no longer on Text box1, the Leave event does not fire and the form closes.
I can put the focus on any other control to handle this but for the sake of user experience I had to put the focus on Textbox1 since it's value decides the further logic of what is shown in Textbox2(Textbox1 can be left empty in which case nothing is shown in Textbox2 but that is not related to this question).
Tried this to see if I can get the Cancel button inside the Leave event of Textbox1:
Button b = sender as Button
b is NULL when Cancel is clicked.
What can I do to make the Cancel click work, without triggering the Leave event initially?
Thanks in advance,
Regards.
If you only want to supress the error message when the cancel button recieves the focus, simply check for the cancelButton.Focused flag in the leave event, then block the message if the cancel button got the focus.
Nevertheless, it is usually considered good practice to avoid modal dialog boxes if they are not required. So depending on your particular situation, you may decide to replace the MessageBox with a ToolTip instead, which will not entirely block the UI thread.

Drag finger from one button to another - want to un-trigger old button and trigger new button - Windows Store

I am working on a windows store application and I want to be able to drag between buttons so that the originally pressed button becomes deactivated and the newly "dragged onto" button becomes activated but I can't seem to get this to work.
I have 2 Buttons inside a StackPanel and the events I have on them are:
PointerPressed
PointerEntered
PointerReleased
PointerExited
PointerCanceled
PointerCaptureLost
PointerPressed and PointerEntered share the same event handler and the rest (the "deactivation" events) share the same event handler.
If I press one button my "activated" event handler is triggered and if I drag off it my "deactivated" event handler is triggered but if I then drag onto the second button the "activated" event handler isn't triggered again.
Strangely, if I start by dragging from off the StackPanel onto one of the buttons the "activated" event handler is triggered. I assume that it is something to do with the internal pointer management stuff but can't seem to find a workaround.
Does anyone know why this is happening and how I can get it to work how I want?
Thanks for your time.
Edit
Okay I've been researching some stuff and I've come across CapturePointer() and ReleasePointerCapture() but this seems to be broken - If I capture the pointer, when I take my finger off the screen, PointerReleased doesn't even get hit.
I've also realized why the "dragging from off the SP onto one of the buttons causes it to 'activate'" - this is because when a button is pressed it doesn't route its event but fires a Click event - meaning the same pointer cannot fire a PointerEntered event of another button, but if it starts outside a Button it will trigger PointerEntered.
This doesn't get me much further but it is a little extra info :)
The concept of Button is a bit unique in regard to mouse capture and how dragging away from it happens. In your scenario I'm not sure if the event model around Button will work correctly for you. On Button, when a pointer is depressed (mouse) it has capture until it is released. This is not the same for touch where a press and drag away is different because in touch there isn't any explicit capture unless you create it.
So what you are hitting is going to be a slight conflict between mouse/touch interactions anyway using Button -- using some other UI element (not sure if you have a styled button) should get you what you want.

Autosaving on blur + "Undo" button

I am writing directive, that will act like this:
allow editing of some text (using content editable)
on loosing focus it should save its value to model (lately watched and saved to DB)
there should be button "Undo" which revert changes.
My implementation is: http://plnkr.co/edit/DsWEYQV4j51i4GO6KjSe?p=preview
The only problem I have is when I press "undo" button, DIV lose focus (so 'focusout' event is fired) and value is saved in model, so "undo" button can't revert its value.
( I click "undo" -> focusout event (autosave) -> click event (??? can't revert) )
Possible workarounds I see:
set timeout on blur and cancel it if 'undo' button was pressed. But it is ugly as user can enter value and navigate to other part of app, so timeouted saving won't run any $watch listeners.
save value on focusin and restore it when 'undo' button is saved. This cause another problem: $watch listeners would run with changed value and then run again with previous value (so there would be 2 writes to DB instead of one)
Do anybody have solution for such behaviour (autosave on blur + undobutton)?
How about using underscore.js debounce function or similar to cause a delay on autosave, where it will check for a undo flag and cancel? Not sure what the $watch listeners are doing. Of course it will still not work if the user completely goes out of the app or refreshes the page etc.

Displaying a WPF context menu after a D&D operation

Here is my problem; I want to display a context menu, with items created on the fly in the code behind, when a D&D operation has finished.
What I can't do is
Insert an item that will cancel cancel the drop operation, if selected
I can't find a way to keep the menu open when I click anywhere outside of the menu
How can I do these two things?
Displaying the context menu will not block the D&D operation from completing, so it won't wait until the user addresses the context menu. You would have to somehow save the D&D action (capture what is being dropped and hold on to it) and wait to complete the action until after the context menu has been addressed.
A context menu will automatically close when it loses focus. However there is a StaysOpen property that overrides this behavior. If you set StaysOpen to true, it will remain open until you explicitly close it (by settings IsOpen to false).

Silverlight click event registered a second time before first event completed

I have a button which launches a "modal dialog" - it just creates a transparent grid covering everything, with the "dialog" created on top of that.
However I have a strange issue - if I double/triple click the button really fast (or add some delay in the event code), the button click event is executed multiple times, creating multiple overlapping modal dialogs. If the first action in my event is to disable the button (IsEnabled=false) it seems to prevent this.
My guess is that Silverlight is being multithreaded with input - it is not only recording the second click in another thread (while the button's click event is running), but it is jumping the gun by evaluating which control should be the target before the previous event has finished executing. Even though that event alters what control is at those mouse coordinates, it doesn't matter.
Does anyone know anything about this behavoir, or a way around it? If I have something like a save window, where the user clicks a save button, a blocking grid ("Saving...") is placed up while it saves, and then the whole "window" is closed, I'd like to avoid the user being able to queue up multiple save event clicks (this could lead to unpredictable program behavoir).
If you've ever worked with WinForms or WPF, this is expected behavior. Your button is broadcasting its Click event until your modal dialog covers it up. Unfortunately, there is some amount of time between your first click and when the modal dialog covers the button which allows multiple clicks to the original button.
You have two solution choices:
Disable the button after the first click and then re-enable after the modal dialog returns. You've already mentioned that this works.
Write code in the Event Handler of the button to determine if a modal dialog is already being displayed. This way, you're putting the responsibility in one location rather than splitting it up (disabling and re-enabling the button). This would be my preferred solution.
I think what you're seeing is the behaviour of Silverlight's routed events.
You can set the Handled property of the event arguments to true to prevent the event from bubbling.

Resources