UWP MVVM on click bind to function - c#

This is probably the easiest thing in the world to do but I'm struggling a bit.
I have this in XAML:
<Button Name="browseButton" Content="Browse" Grid.Column="1" />
I've got everything binding correctly from the view to the viewmodel, like radio buttons and input text boxes etc... but what I want is to bind this button to a function, such that when the user clicks it some operation occurs.
But I'm really having a hard time figuring out how to bind clicking this button to a function in the viewmodel. I've played a bit with ICommand and didn't get very far, and I don't want to do the hack-ish thing of just sticking it in the code behind.
I'm using MVVMLight (Galasoft) if that helps.
Any guidance appreciated.
Edit
Following the example from https://msdn.microsoft.com/en-us/magazine/dn237302.aspx I have, but where does canExecuteMyCommand come into it? And how do I bind it in the XAML?
public RelayCommand BrowseCommand
{
get;
private set;
}
public LoadFilesViewModel()
{
BrowseCommand = new RelayCommand(executeBrowse, () => _canExecuteMyCommand);
}
private void executeBrowse()
{
// Do something
}
Solution
<Button Name="browseButton" Content="Browse" Grid.Column="1" Command="{Binding BrowseCommand}" />
And
public RelayCommand BrowseCommand
{
get;
private set;
}
public LoadFilesViewModel()
{
BrowseCommand = new RelayCommand(executeBrowse, () => true);
}
private void executeBrowse()
{
// Do something
}

If you look at the code you provided, the RelayCommand constructor comes with 2 parameters.
public RelayCommand BrowseCommand
{
get;
private set;
}
public LoadFilesViewModel()
{
BrowseCommand = new RelayCommand(executeBrowse, () => _canExecuteMyCommand);
}
private void executeBrowse()
{
// Do something
}
Checking the source code (that's the learning a code base and open-source makes this possible) or Visual Studio IntelliSense, you'll see this signature:
public RelayCommand(Action execute, Func<bool> canExecute)
So the first parameter is an action to be executed, and the second parameter is a check if it can execute. You've correctly identified the executeBrowse as a method to "do something". The _canExecuteMyCommand parameter is a class variable of the type bool that can be either true or false (set somewhere else).
In your own solution (posted in question), you replaced this by true (hardcoded). Note that you can also drop the second parameter in this case:
public LoadFilesViewModel()
{
BrowseCommand = new RelayCommand(executeBrowse); // will always execute
}
Bonus
Note that instead of using a local variable, you can also use a method to defined whether the method can execute (or write the check logic inline with the delegate syntax).
public LoadFilesViewModel()
{
BrowseCommand = new RelayCommand(ExecuteBrowse, CanExecuteBrowse);
}
private void ExecuteBrowse()
{
// Do something
}
private bool CanExecuteBrowse()
{
// Check if we are allowed to browser
return true; // or false :)
}

Related

Triggering "CanExecute" on postsharp [Command] when a document changes?

I am currently migrating a project to PostSharp to remove a lot of boilerplate code, most of it is going very smoothly but I'm left confused about how to force a command to recheck if it CanExecute. I expected PostSharp would inspect the command like it does properties to check for dependencies, here is a minimalist sample
[NotifyPropertyChanged]
public class MyWindowViewModel
{
/// Anything bound to this refreshes just fine as expected
public ObservableCollection<SomeType> Documents = new ObservableCollection<SomeType>();
[Command]
public ICommand AddDocumentCommand { get; set; }
public void ExecuteAddDocument () { Documents.Add(new SomeType()); }
[Command]
public ICommand CloseDocumentCommand { get; set; }
public bool CanExecuteCloseDocument () => Documents.Any();
public void ExecuteCloseDocument () { Documents.Remove(Documents.Last()); }
}
At start the collection is empty and the button attached to the close command is greyed as expected, however adding a document through the button attached to AddDocument doesn't activate the close document button, what is the appropriate way to accomplish what I need? Is PostSharp only considering assignments and not method calls as changes or is it something else entirely?
According to their Command documentation
CanExecuteCloseDocument should be a property
public bool CanExecuteCloseDocument => Documents.Any();
The method option is used when the command requires parameters,
The command availability check that depends on the input argument can be implemented as a method.
for example
public bool CanExecuteCloseDocument (int blah) => Documents.Any();
public void ExecuteCloseDocument (int blah) { Documents.Remove(Documents.Last()); }
That aside the main issue here is that the view is unaware of the changes to the collection to know to refresh property changes.
Refer to this http://www.postsharp.net/blog/post/Announcing-PostSharp-42-RC
Dependencies to collections
When you add the [AggregateAllChanges] attribute to a field or
automatic property, any change to a property of the object assigned to
this field/property will be interpreted as a change to the
field/property itself. The attribute now works only for collections.
[NotifyPropertyChanged]
public class MyWindowViewModel {
/// Anything bound to this refreshes just fine as expected
[AggregateAllChanges] // <-- when the collection changes to cause notification
public ObservableCollection<SomeType> Documents { get; } = new ObservableCollection<SomeType>();
[Command]
public ICommand AddDocumentCommand { get; set; }
public void ExecuteAddDocument () { Documents.Add(new SomeType()); }
[Command]
public ICommand CloseDocumentCommand { get; set; }
public bool CanExecuteCloseDocument => Documents.Any();
public void ExecuteCloseDocument () { Documents.Remove(Documents.Last()); }
}
With PostSharp LINQ expression (or virtual calls, delegates, external methods) wouldn't work well for CanExecute's.
But expression on properties that implement INotifyPropertyChanged work fantastic (even for nested properties). ObservableCollection implements INotifyPropertyChanged, we don't need LINQ:
public bool CanExecuteCloseDocument => Documents.Count > 0;

Action on ComboBox selection changed

I'm using MVVM to bind a ComboBox to a ViewModel, and I have few question about heavy actions and selection change.
I want to trigger some actions when the selected item is changed, my initial approach was to put the logic in the setter of the field to which the selected item is binded.
So my first question is, is this good practice or there is a better approach?
Those actions may be very expensive in time and resources (need to retrieve data through a web service) and I don't want the UI to freeze, so lately I've started to send a message from the set which is received in the view's code-behind and that call a ViewModel command asynchronously.
Am I just wasting time or does this make any sense?
The problem is that when I'm debugging the UI sometimes freeze anyway (it doesn't happened on release). Reading here and there I've come to know that it may be debugger related, can anyone confirm this behavior on VS 2015?
Additional information
As requested I provide some examples. This is my first approach:
(XAML)
<ComboBox SelectedItem="{Binding SelectedField}"/>
(ViewModel)
public class ViewModel
{
private MyObject _selectedField = null;
public MyObject SelectedField
{
get
{
return _selectedField;
}
set
{
if(_selectedField != value)
{
// Expensive action
_selectedField = value;
RaisePropertyChanged(() => SelectedField);
}
}
}
}
The expensive action make some web service calls and may take long, is this design good or is there a better way to achieve this?
My second approach is through messages, as shown in this example:
(ViewModel)
public class ViewModel
{
private MyObject _selectedField = null;
public MyObject SelectedField
{
get
{
return _selectedField;
}
set
{
if(_selectedField != value)
{
Messenger.Default.Send(new DoStuffMessage());
_selectedField = value;
RaisePropertyChanged(() => SelectedField);
}
}
}
private RelayCommand _doStuffCommand = null;
public ICommand DoStuffCommand
{
get
{
if (_doStuffCommand == null)
_doStuffCommand = new RelayCommand(async () => await DoStuff());
return _doStuffCommand;
}
}
private async Task DoStuff()
{
// Expensive action
}
}
(Code-behind)
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<DoStuffMessage>(this, DoStuffMessage_Handler);
}
private void DoStuffMessage_Handler(DoStuffMessage msg)
{
(DataContext as ViewModel).DoStuffCommand.Execute(null);
}
}
Is this approach better or is just bad and useless?
For MVVM, I prefer to use RelayCommands to bind an EventTrigger in XAML to an ICommand in the viewmodel. I feel this creates the best separation of code and is clearer than adding a lot of logic to my setters, where it might be overlooked during troubleshooting. Here is an overview of the process: https://msdn.microsoft.com/en-us/magazine/dn237302.aspx
This is to wire up a button and pass in a parameter, so obviously you would need to modify it for your use case, but it will show the basic technique. In XAML:
<Button Content="Click Me">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<Custom:EventToCommand Command="{Binding MyCommand}" CommandParameter="foo"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
In your VM:
public static ICommand MyCommand { get; set; } // declare an ICommand - bind to this!
public MainViewModel(IDataService dataService)
{
// associate your ICommand with a method. If you don't use a parameter, you don't need the lambda expression here.
MyCommand = new RelayCommand<string>((paramater) => MyCommandMethod(parameter));
}
public void MyCommandMethod(string parameter)
{
Debug.WriteLine("This is the code I want to run in my VM. The parameter is " + parameter);
}
I use the [free] MVVMLight toolkit for my applications, which was written by the guy who wrote the article that I linked to, but a lot of this is baked into .Net also. Using Expression Blend can make it easier to wire this stuff up when you are designing.
You can do whatever you like in setter as long as it is async.
private string _test;
public string Test
{
get { return _test; }
set
{
Task.Run(() =>
{
//do stuff
});
_test = value;
}
}
If you don't want to place logic in setter, because for example the Single Responsibility principle is violated, you should use interactions to catch the SelectionChange event and call a command in VM which should call an async method.
Here you have a sample that uses interactions : cute link
That's it!

Open a new Window in MVVM

Lets say I have a MainWindow and a MainViewModel, I'm not using MVVM Light or Prism in this example.
In this MainWindow I want to click a MenuItem or Button to open a NewWindow.xaml not a UserControl.
I know how to use this with UserControl to open a new UserControl in my existing Window in a ContrntControl or a Frame.
<ContentControl Content="{Binding Path=DisplayUserControl,UpdateSourceTrigger=PropertyChanged}" />
Code
public ViewModelBase DisplayUserControl
{
get
{
if (displayUserControl == null)
{
displayUserControl = new ViewModels.UC1iewModel();
}
return displayUserControl;
}
set
{
if (displayUserControl == value)
{
return;
}
else
{
displayUserControl = value;
OnPropertyChanged("DisplayUserControl");
}
}
}
In the ResourceDitionary for MainWindow I have :
<DataTemplate DataType="{x:Type localViewModels:UC1ViewModel}">
<localViews:UC1 />
</DataTemplate>
<DataTemplate DataType="{x:Type localViewModels:UC2ViewModel}">
<localViews:UC2 />
</DataTemplate>
The thing is that I want to open a new Window, not a UserControl. So I use some code like this :
private ICommand openNewWindow;
public ICommand OpenNewWindow
{
get { return openNewWindow; }
}
public void DoOpenNewWindow()
{
View.NewWindowWindow validationWindow = new View.NewWindow();
NewWindowViewModel newWindowViewModel = new NewWindowViewModel();
newWindow.DataContext = ewWindowViewModel;
newWindow.Show();
}
and then a bind OpenNewWindow to a MenuItem or Button.
I know this is not the right way, but what is the right way to do this ?
Thanks!
There are two problems you need to solve with this type of application.
Firstly, you do not want to have the View-Model creating and displaying UI components directly. One of the motivations for using MVVM is to introduce test-ability in to your View-Model, and having this class pop up new windows makes this class harder to test.
The second problem you need to solve is how to resolve the dependencies in your application, or in this instance – how to you “hook up” the View-Model to the corresponding View? A maintainable solution to this latter problem is given by the use of a DI container. A very good reference to this subject is given by Mark Seemann’s Dependency Injection in .NET. He actually also discusses how to solve the first problem too!
To solve the former problem, you need to introduce a layer of indirection to your code, to make the View-Model not dependent on a concrete implementation of creating a new window. A very simple example is given in the code below:
public class ViewModel
{
private readonly IWindowFactory m_windowFactory;
private ICommand m_openNewWindow;
public ViewModel(IWindowFactory windowFactory)
{
m_windowFactory = windowFactory;
/**
* Would need to assign value to m_openNewWindow here, and associate the DoOpenWindow method
* to the execution of the command.
* */
m_openNewWindow = null;
}
public void DoOpenNewWindow()
{
m_windowFactory.CreateNewWindow();
}
public ICommand OpenNewWindow { get { return m_openNewWindow; } }
}
public interface IWindowFactory
{
void CreateNewWindow();
}
public class ProductionWindowFactory: IWindowFactory
{
#region Implementation of INewWindowFactory
public void CreateNewWindow()
{
NewWindow window = new NewWindow
{
DataContext = new NewWindowViewModel()
};
window.Show();
}
#endregion
}
Note that you take an implementation of IWindowFactory in the constructor of your View-Model, and it is to this object that the creation of the new window is delegated to. This allows you to substitute the production implementation for a different one during testing.

How to enable a Button with its CanExecute method

I am developing an application in WPF using MVVM, but I am stuck with the ICommand objects.
I have a windows which contains some buttons, so, I bind them to their respective ICommand in XAML as below:
<Button Command="{Binding DoSomethingCommand}" Content="Do Something" />
Then, In my view-model class I have written the following:
public class MyViewModel : ObservableObject
{
private bool isDoSomethingButtonEnabled = false;
....
public ICommand DoSomethingCommand
{
get;
private set;
}
....
....
public MyViewModel()
{
DoSomethingCommand = new DelegateCommand<String>(this.OnDoSomething, this.CanDoSomething);
}
private void OnDoSomething(String arg)
{
}
private bool CanDoSomething(String arg)
{
return isDoSomethingButtonEnabled;
}
....
}
So, Since I need that my button is not enabled the first time the window opens, I set my variable isDoSomethingButtonEnabled to false. And it works, the button is disabled at the beginning, but my problem is that when I change the variable isDoSomethingButtonEnabled to true at run time my button is still disabled.
I have even done some tests after changing the variable isDoSomethingButtonEnabled to true, printing the result of DoSomethingCommand.CanExecute() and it shows "true"!
so, what Should I do in order to enable my button??
Thank you in advance
There is an event called CanExecuteChanged on the ICommand interface which:
Occurs when changes occur that affect whether or not the command
should execute.
With the Prism DelegateCommand you can raise this event with the RaiseCanExecuteChanged method:
public void SomeMethod()
{
//do some work
isDoSomethingButtonEnabled = true;
DoSomethingCommand.RaiseCanExecuteChanged();
}

Best / neatest way to declare RelayCommands

I've been trying to find a nice neat and succinct way to declare RelayCommands in my ViewModels.
The best I can come up with is:
public class MyViewModel
{
public ICommand StopCommand { get; private set; }
public MyViewModel()
{
StopCommand = new RelayCommand(OnStop);
}
private OnStop(object sender)
{
//hammertime
}
}
What I'd really like to do it remove the two stage declaration/construction, something like:
public class MyViewModel
{
public readonly ICommand StopCommand = new RelayCommand(OnStop);
private OnStop(object sender)
{
//hammertime
}
}
However, this fails to compile with
error CS0236: A field initializer cannot reference the non-static
field, method, or property 'MyViewModel.OnStop(object)'
It there a neater / "standard" way that people use?
I've used the first format you specified quite a bit and it works fine for me.
Also - if you're using WPF, binding doesn't work with fields anyway so even if you can get the second approach to compile, it won't hook up to your UI.
One option is to abandon commanding which has it's limitations, and use another mechanism such as Actions provided by Caliburn.Micro. Then, you just need your view model verb:
public void Save()
{
}
<Button x:Name="Save">Save</Button>
I was using something like:
public ICommand StopCommand
{
get{return new RelayCommand(OnStop);}
}

Categories