I'm using the ICommand interface to perform binding on a couple of buttons in my application, the Run and Close button. I'm using a helper method that was mentioned in this question to hook up the ICommand to a delegate as follows:
_someCommand = new DelegateCommand(this.DoSomething, this.CanDoSomething);
private ICommand _someCommand;
public ICommand SomeCommand
{
get { return _someCommand; }
}
private void DoSomething(object state)
{
// do something here
}
private bool CanDoSomething(object state)
{
// return true/false here is enabled/disable button
}
This appears to work just fine as, in my implementation, CanDoSomething returns the value of a property that I have in my application.
If I set the initial value of the property to true, then the button is enabled and false it is disabled.
I have a series of events that are raised from a BackgroundWorker in the application layer back to the ViewModel that change the value of the property to true or false based on the current state of the application.
The current problem I'm having is that the button is not "re-enabling" when I set the value to true after the work has completed. If I click somewhere within the window, it will update. So, therefore, I'm thinking than manually refreshing the window will solve my problem, at least for the interim. This feels a bit gross to do it this way, but I'm kind of at a loss for what else I could try.
If anyone has any suggestions, I'm all ears.
Thanks for the help!
Ian
Edit -
A little bit more information on the application itself. It uses a background worker in the application thread to handle the "work". The application is a simple utility to manage the creating of tables and loading of data into the tables. We use a lot of pre-defined SQL scripts to setup our test environment, so this is a simple utility that allows us to do that sort of thing based on parameters provided by the user in the UI.
Hopefully that helps, because when I re-read my question it read as if I were doing everything in the UI thread, which is not the case. Progress reports are raised back up to the UI thread and everything is updated as expected, except the button..
CommandManager.InvalidateRequerySuggested() may be the answer - it tells all the commands to check whether they are enabled or not.
You have to raise the CanExecuteChanged event:
http://msdn.microsoft.com/en-us/library/system.windows.input.icommand.canexecutechanged.aspx
This may be more useful than the other answer in cases where you know you should re-evaluate a single control, and re-evaluating all the controls would be costly. The other answer is simpler if you don't have a case like that, though.
Related
At the moment i'm working with Caliburn Micro. But i got to a problem which i don't know how to solve.
The problem is i want to disable Buttons, but every website has only a solution with propertys. the functions of my buttons for example just start a thread to establish a connection over tcp with Netmq. So i don't know how i'll be able to disable them. Searched a lot through google but didn't find anything helpful.
Example of a button function
public void startPubButton()
{
Thread entryThread = new Thread(startPublisher);
entryThread.IsBackground = true;
entryThread.Start();
}
is there maybe a possibility to enable the Buttons only when the thread runs ?
That is the one premise behind CM wiring up by convention all you have to do is provide a CanstartPubButton Boolean property run a code check to see if you can enable or disable button according to the logic with that guard property. Call with NotifyOfPropertyChange(() => CanstartPubButton); in some fashion to do what you want. The logic with in the property (get only needed) is up to you. one other thing I will drop on you is a thread presently in the GitHub discussions on the repository itself. Might help and it might not
https://github.com/Caliburn-Micro/Caliburn.Micro/issues/422
I'm currently developping an application in MVVM.
I'm using for all buttons RelayCommand to perform action. The fact is that, with RelayCommand, the button is disabled when clicked on it, the time that the command is executing.
Because our rendering engine is a little bit heavy, when we open a new window, the button stay disabled for a bit, like a second.
My question is : is there a way to disable this behaviour and let the button enabled, because it changes style ?
Changing disabled style is not an option ...
I haven't see that kind of behavior on the net or in the documentation ?
Thank you
EDIT :
Two differents implementations of my RelayCommand
AnswerCommand = new RelayCommand(p => AnswerMail(p), p => IsMailSelected());
DeleteMailCommand = new RelayCommand(p => Task.Run(() => DeleteMail()), p => IsMailSelected());
I think you should synchronize the access for the block existing in the ICommand instance Execute method and every time you get there, start a new thread. If you want to click on that Button many times, having that lock section there, will put your calls in a queue. If you want to reject the requests received while it's still working you can implement the balking pattern.
https://en.wikipedia.org/wiki/Balking_pattern
This way, your button will stop being disabled when the created thread is starting.
I am not sure if this approach helps you but i think it's a feasible scenario.
One more thing, i've just found this:
Patterns for Asynchronous MVVM Applications: Commands
i haven't used it before but looks nice, i will give it a try tonight. Good luck!
I have just updated a project from Catel 3.4 to Catel 4.0 and a custom apply button that had been working now never gets enabled.
AddCustomButton(new DataWindowButton("Apply", ExecuteApply, canExecuteApply));
In Catel 3.4 the canExecuteApply got called when the window got focus or any control was changed. In 4.0 it gets called twice when the window is created and never again.
I suspect this has something to do with the IViewPropertySelector part of the update guide, however registering the default implementation had no effect and I can't figure out what namespace the AutoDetectViewPropertiesToSubscribe extension method is in.
Edit: I have found I am getting the same behavior with some AsynchronousCommand instances elsewhere in the application. The CanExecute delegate fires when the control is created then never again.
Edit 2: These were the same issue with diffrent solutions. For an explanation of the issue see Geert van Horrik's answer.
If the command is registered in a view model you can use
ViewModelCommandManager.InvalidateCommands(true);
to get the can execute state to re-evaluate. For a DataWindowButton as described above I had to manually call RaiseCanExecuteChanged on the button's command since that command does not belong to a vie model as far as i can tell.
var catelCommand = (applyButton.Command as ICatelCommand);
if (catelCommand != null)
{
catelCommand.RaiseCanExecuteChanged();
}
In either case, this is far from the approach with the best performance characteristics, but if the same behavior you had before the upgrade is desired, you can make these calls in the following event subscription:
System.Windows.Input.CommandManager.RequerySuggested += RequerySuggested;
Hope this helps anyone else facing this issue.
The reason is that in the past (pre 4.0), Catel subscribed to the CommandManager of WPF and invalidated all the commands on all view models on nearly everything (mouse move, focus, etc). To improve performance (a lot), we decided to only invalidate commands automatically when a property changes on a specific view model.
For example, if you have a vm where you change a property, it will automatically re-evaluate the commands on that vm. You can still manually re-evaluate commands using this code (inside a vm):
ViewModelCommandManager.InvalidateCommands(true);
How about this? I had a problem where my nested user controls must cause my outer user control's commands to update. It is not elegant but it gets the job done for me, until I can get a better way.
public partial class App : Application
{
private static IViewModelManager _ViewModelManager;
public App()
: base()
{
var dependencyResolver = this.GetDependencyResolver();
_ViewModelManager = dependencyResolver.Resolve<IViewModelManager>();
System.Windows.Input.CommandManager.RequerySuggested += RequerySuggested;
}
private void RequerySuggested(object sender, EventArgs e)
{
foreach (IViewModel viewModel in _ViewModelManager.ActiveViewModels)
{
(viewModel as ViewModelBase).GetViewModelCommandManager().InvalidateCommands(true);
}
}
}
So let me warn you; I am asking for a way to make a total hack work somewhat better. I admit that it is a hack and am certainly open to different takes on the problem as a whole. That said, I need to get this in soon if I want to make code cutoff and we have a somewhat aggressive release date.
As such, I will not be able to make large changes immediately, but I can easily do so for the first patch to this software. So, short and long term solutions are welcome, but a short term solution (if possible) is preferable.
Ok, so here is the issue; I have an application that send commands to a robotic hardware device. After a command is sent that requires a wait (for example, a physical motion that takes an unknown amount of time) the UI goes into a "Busy State". When this occurs all controls that would issue another blocking command are disabled as the machine cannot process them.
When the UI comes out of a busy state all controls are once again enabled, but the tab order is not maintained for obvious reasons. This makes tabbing through input areas impossible and, seeing as I myself use the keyboard almost exclusively, is not acceptable. My current scheme to "solve" this problem is to:
At startup, register to the GotFocus event for each control of interest in the application. This is made difficult due to the fact that this is a WPF MVVM app and also because some controls are created dynamically. Nevertheless, I can search the visual and/or logical trees and get by...
In the GotFocus event, save a reference to the control.
When we exit a busy state, attempt to set focus to the saved control.
This works... kinda sorta. The issue at hand (as I can think of more fail scenarios...) is that this logic will blow away a context menu if it was open and another. Consider:
Focus is in a text area.
User right clicks another control. This control does not get focus (even if I try to set it in a mouse handler).
System goes into a busy state as the right click performed a move.
When the busy state ends, text area is given focus and the context menu closes.
(Now I realize that you may say that performing a move on a right click and also displaying a context menu is a bad idea, but the context menu commands are non-blocking, enabled, and it has a domain specific use that is convenient.)
So there it is. I can't even get focus in a right click, and setting focus to the menu itself doesn't work either. I'm just curious if anyone has a better scheme for something like this. I realize it is awkward and a very narrow circumstance. Thanks for any help you can offer in advance, I will be playing around with this some more...
Interesting question and sadly i cannot think of an immediate solution to the problem, i would try to work around the issue by not blocking the application at all if this is such a frequent thing.
If the roboter can only take one command at a time it might work to just implement a command-queue so that the interface can still be used and while the roboter is busy issued commands get deferred. In terms of usability it might be a good idea to make this queue very visible so it is apparent that the command has been issued and will be taken into account.
How about instead of actually disabling controls you just do a check when they are invoked if they should send the command or not in the beginning of their event handlers. eg:
if(!bControlsEnabled) { return; }
You could also change the controls style when they should be "disabled".
Implement a custom behavior that listens when your control's enabled changes, saves the focused element and refocuses that element when enabled changes back to true:
public class KeyboardFocus
{
public static bool GetReturnFocusAfterEnable(DependencyObject obj)
{
return (bool)obj.GetValue(ReturnFocusAfterEnableProperty);
}
public static void SetReturnFocusAfterEnable(DependencyObject obj, bool value)
{
obj.SetValue(ReturnFocusAfterEnableProperty, value);
}
private static Dictionary<object, IInputElement> values = new Dictionary<object, IInputElement>();
// Using a DependencyProperty as the backing store for ReturnFocusAfterEnable. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ReturnFocusAfterEnableProperty =
DependencyProperty.RegisterAttached("ReturnFocusAfterEnable", typeof(bool), typeof(KeyboardFocus), new UIPropertyMetadata(false, PropertyChangedCallback));
static void PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
UIElement element = d as UIElement;
if (element != null)
{
element.IsEnabledChanged += (element_IsEnabledChanged);
}
}
else
{
UIElement element = d as UIElement;
if (element != null)
{
element.IsEnabledChanged -= (element_IsEnabledChanged);
}
}
}
static void element_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue)
{
if (values.ContainsKey(sender))
{
Keyboard.Focus(values[sender]);
values.Remove(sender);
}
}
else
{
values[sender] = Keyboard.FocusedElement;
}
}
}
This could then be implemented in XAML as such:
<Grid Name="layout" local:KeyboardFocus.ReturnFocusAfterEnable="True">
This works in my test setup, but if you press a button before disabling your stuff, that button would have keyboard focus at the time the thing gets disabled, and this fails.
This solution is independant of your architecture, and does not require code in the form. It's a bit quick and dirty but it does the job.
Workaround for this :-Instead of disabling the control You should use XamBusyIndicator as a parent of that control and set the property for IsBusy. If IsBusy is true it will disable the child control and also TabIndex property will run in a sensible manner.
For example:-
<igComboEditor:XamBusyIndicator IsBusy="{Binding IsBusy}" Height="22">
<igComboEditor:XamBusyIndicator.Animation>
<igComboEditor:CustomBusyAnimation DataTemplate="{StaticResource CustomBusyIndicatorTemplate}" />
</igComboEditor:XamBusyIndicator.Animation>
<igComboEditor:XamComboEditor Width="125" VerticalAlignment="Bottom" Style="{StaticResource XamComboEditorStyle}"
ItemsSource="{Binding DataList}" IsEditable="False"
SelectedItem="{Binding SelectedData}">
</igComboEditor:XamComboEditor>
</igComboEditor:XamBusyIndicator>
I would
OnCommandSentThatRequiresAWait Event Call SaveStateAndDisableUI()
SaveStateAndDisableUI()
{
Foreach control in controlsCollection
{
switch(controlType)
{
for each control extract and save all you need.
if it's a menu check if it is opened up and so on,
does control have focus,
where is the caret located in case of textbox etc
}
}
save position of mouse x,y
save position of form, state like if it is minimized, maximized
}
Corresponding RestoreState() should restore everything back up.
I must be retarded with searching, because here's another seemingly common problem that I haven't been able to solve.
Here's my problem -- I am using WPF and MVVM, and I have a statemachine that executes in the model. If an error occurs, I need to pass information up to the ViewModel to display the error. This part seems to work okay. When the user clicks the desired behavior, the code in the model continues, and looks at the object the user interacts with to determine what to do next.
The problem is that the model needs to reload a file, which updates the GUI with the contents of said file. Because the model is executing in a thread, you can imagine what I'm going to ask next -- how the hell do you synchronize with the GUI properly? In MFC, I would have used either SendMessage or PostMessage to accomplish the GUI update.
I've read articles for WinForms that suggest using InvokeRequired to automatically call BeginInvoke if necessary. I actually didn't know that BeginInvoke would accomplish what I wanted, so I was encouraged to learn this.
How do I actually call BeginInvoke from my model? Does this method even apply to WPF? I went ahead and implemented a delegate and then called Invoke, but I get the same error that tells me the collection can't be modified from this thread. I also tried BeginInvoke for the hell of it, but I assume that also wouldn't work because it would just launch from a different thread anyway.
Confused. If I have missed something really obvious that's been posted about all over the internet, go ahead and give me a verbal lashing, I probably deserve it.
EDIT - I should probably also add that I am looking for something other than a timer or BackgroundWorker-based solution, unless that's the only way to solve this in WPF / MVVM. Also, I wonder if any of the MVVM toolkits would have facilities for this sort of thing already...
If you want to schedule some work from a background thread to the UI thread in WPF, use the DispatcherObject. Here's a nice article on how to Build More Responsive Apps with the Dispatcher.
Update: Note that if you use an event to send notifications from the Model to the ViewModel, you still need to switch to the UI thread somewhere. Whether that switch should be in the Model or the ViewModel is a good design discussion, but it's orthogonal to your question.
The event will be raised on the corresponding Dispatcher thread. Since you need to get to the UI thread, you need to use a Dispatcher that is created on the UI thread. The easiest way to get one is to use the DispatcherObject.Dispatcher property on one of the UI elements. The alternative is to create one in your Model or ViewModel. If you are a design purist, I would suggest you create the Dispatcher in your Model and dispatch the call back to the UI thread before you raise the event to which the ViewModel is listening. This way all the thread switching and management is contained in your Model and the ViewModel works as a single-threaded on the UI thread only.
I think that your ViewModel really shouldn't know anything about the View, including whether or not it's a WPF UI, or whether or not that UI even has the concept of a Dispatcher thread, so the red flag should fly as soon as you start writing code in your ViewModel that attempts to CheckAccess() or InvokeRequired in order to marshal some code to the UI thread. Instead I'd have the model raise an event that the View can listen for and update itself accordingly, or have the ViewModel expose a property (eg. bool FileIsLoading) that the View simply binds to in order to detect and display what the model is doing asynchronously, and it's the ViewModel's responsibility to ensure that the value of that property is accurate.
For example:
public partial class MainWindow : Window {
private ViewModel _model = new ViewModel();
public MainWindow() {
InitializeComponent();
DataContext = _model;
}
private void Button_Click(object sender, RoutedEventArgs e) {
_model.Run();
}
}
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Click="Button_Click"
Content="Run"
IsEnabled="{Binding IsIdle}" />
</Grid>
</Window>
public class ViewModel : INotifyPropertyChanged {
private bool _isIdle = true;
public bool IsIdle {
get { return _isIdle; }
set {
_isIdle = value;
OnPropertyChanged("IsIdle");
}
}
public void Run() {
ThreadPool.QueueUserWorkItem((state) => {
IsIdle = false;
Thread.Sleep(10000);
IsIdle = true;
});
}
#region INotifyPropertyChanged Implementation
protected void OnPropertyChanged(string propertyName) {
PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
if (propertyChanged != null) {
propertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
I've got another approach that seems to work, and I just wanted to throw it out there to get some comments (if anyone is even reading this question anymore!).
I started to use the MVVM Light Toolkit's Messenger class, and it seems to work really well for me. For example, let's take the ProgressBar as an example. I registered two messages with my ViewModel for setting the progress value and progress maximum. Then in my model, as it sets up the tasks and overall process, it sends these messages. When the VM receives the messages, it just updates databound values, and my GUI updates automatically! It's super duper easy, but I was wondering what you all thought about this approach. Is anyone else doing this without incident?