I am trying to pass CommandParameter to the method in my ViewModel.
How to do this?
private void Open(object sender)
{
if (sender==this.objMainWindow.btnHistory)
{
objMainWindow.Container.Child = objHistory;
}
if (sender == this.objMainWindow.btnNew_Item)
{
objMainWindow.Container.Child = objNewItem;
}
if (sender == this.objMainWindow.btnSide_Effects)
{
objMainWindow.Container.Child = objSideEffect;
}
}
This is my meyhod in ViewModel that I want to pass CommandParameter. I use CommandParameter for button.
"ViewModel" implies MVVM. If you're doing MVVM you shouldn't be passing views into your view models. Typically you do something like this in your XAML:
<Button Content="Edit"
Command="{Binding EditCommand}"
CommandParameter="{Binding ViewModelItem}" >
And then this in your view model:
private ViewModelItemType _ViewModelItem;
public ViewModelItemType ViewModelItem
{
get
{
return this._ViewModelItem;
}
set
{
this._ViewModelItem = value;
RaisePropertyChanged(() => this.ViewModelItem);
}
}
public ICommand EditCommand { get { return new RelayCommand<ViewModelItemType>(OnEdit); } }
private void OnEdit(ViewModelItemType itemToEdit)
{
... do something here...
}
Obviously this is just to illustrate the point, if you only had one property to edit called ViewModelItem then you wouldn't need to pass it in as a command parameter.
Just using Data Binding syntax. For example,
<Button x:Name="btn"
Content="Click"
Command="{Binding ClickCmd}"
CommandParameter="{Binding ElementName=btn,Path=Content}" />
Not only can we use Data Binding to get some data from View Models, but also pass data back to View Models. In CommandParameter, must use ElementName to declare binding source explicitly.
If you are that particular to pass elements to viewmodel You can use
CommandParameter="{Binding ElementName=ManualParcelScanScreen}"
Try this:
public class MyVmBase : INotifyPropertyChanged
{
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler( MyAction));
}
}
public void MyAction(object message)
{
if(message == null)
{
Notify($"Method {message} not defined");
return;
}
switch (message.ToString())
{
case "btnAdd":
{
btnAdd_Click();
break;
}
case "BtnEdit_Click":
{
BtnEdit_Click();
break;
}
default:
throw new Exception($"Method {message} not defined");
break;
}
}
}
public class CommandHandler : ICommand
{
private Action<object> _action;
private Func<object, bool> _canExecute;
/// <summary>
/// Creates instance of the command handler
/// </summary>
/// <param name="action">Action to be executed by the command</param>
/// <param name="canExecute">A bolean property to containing current permissions to execute the command</param>
public CommandHandler(Action<object> action, Func<object, bool> canExecute)
{
if (action == null) throw new ArgumentNullException(nameof(action));
_action = action;
_canExecute = canExecute ?? (x => true);
}
public CommandHandler(Action<object> action) : this(action, null)
{
}
/// <summary>
/// Wires CanExecuteChanged event
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Forcess checking if execute is allowed
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_action(parameter);
}
public void Refresh()
{
CommandManager.InvalidateRequerySuggested();
}
}
And in xaml:
<Button
Command="{Binding ClickCommand}"
CommandParameter="BtnEdit_Click"/>
Related
!!! SOLVED, THANK YOU VERY MUCH !!!
I'm writing my first MVVM application (in WPF C#). Because of that, I want to use commands instead "Click" event defined in a view. The command, which I want to induce is really simple, it should to create and open a view.
I have written RelayCommand class which inherits ICommand interface.
internal class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
I write a method changing value of the field, which represent a view.
private bool openSoundsWindow;
private bool openChordsWindow;
public bool OpenSoundsWindow
{
get { return openSoundsWindow; }
set { openSoundsWindow = value; }
}
public bool OpenChordsWindow
{
get { return openChordsWindow; }
set { openChordsWindow = value; }
}
public void OpenSounds()
{
openSoundsWindow = true;
}
public void OpenChords()
{
OpenChordsWindow = true;
}
I wrote in view model class commands by RelayCommand and OnPropertyChanged event. View model class inherits INotifyPropertyChanged.
private MainModel model = new MainModel();
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public bool OpenSoundsWindow
{
get { return model.OpenSoundsWindow; }
set
{
model.OpenSoundsWindow = value;
OnPropertyChanged(nameof(OpenSoundsWindow));
}
}
private ICommand openSounds = null;
public ICommand OpenSounds
{
get
{
if (openSounds == null)
{
openChords = new RelayCommand(
(object o) =>
{
model.OpenSounds();
OnPropertyChanged(nameof(OpenSoundsWindow));
var newSoundsWindow = new Sounds();
newSoundsWindow.Show();
},
(object o) =>
{
return model.OpenSoundsWindow != null;
});
}
return openSounds;
}
}
I created instance of view model in view's xaml code.
xmlns:vm="clr-namespace:HearingTeacher.ViewModels"
d:DataContext="{d:DesignInstance Type=vm:MainViewModel}"
<Window.Resources>
<vm:MainViewModel x:Key="mainViewModel" />
</Window.Resources>
I binded property command for buttons with created commands in view model.
<Button Grid.Row="0" Content="Sounds" Command="{Binding Path=OpenSounds,
UpdateSourceTrigger=PropertyChanged}" />
Compiler doesn't throw any exception, and .NET starts an application correctly, but commands doesn't work.
Context
Xaml:
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local="clr-namespace:WpfApp2"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
Title="MainWindow" Height="350" Width="400">
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="The ListView:"/>
<ListView
Grid.Row="1"
ItemsSource="{Binding ItemCollection}"
SelectionMode="Single">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction
Command="{Binding Path=ProcessChangeCommand}"
CommandParameter="{Binding Path=SelectedItem,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ItemsControl}}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
</Grid>
</Window>
Code behind:
public partial class MainWindow : Window {
public MainWindow () {
InitializeComponent ();
}
}
public class ViewModel : INotifyPropertyChanged {
bool colorList;
string[] colors = { "blue", "yellow", "green", "orange", "black" };
string[] towns = { "Dakar", "Berlin", "Toronto" };
private ObservableCollection<string> itemCollection;
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<string> ItemCollection {
get => itemCollection;
set {
itemCollection = value;
RaisePropertyChanged (nameof (ItemCollection));
}
}
public ICommand ProcessChangeCommand { get; private set; }
public ViewModel () {
ProcessChangeCommand = new RelayCommand<string> (ProcessChange);
itemCollection = new ObservableCollection<string> (colors);
}
public void ProcessChange (string parameter) {
if (parameter == null) return;
Debug.WriteLine ($"Selected: {parameter}");
ItemCollection = new ObservableCollection<string> (colorList ? colors : towns);
RaisePropertyChanged (nameof (ItemCollection));
colorList = !colorList;
}
void RaisePropertyChanged ([CallerMemberName] string propertyName = null) {
PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (propertyName));
}
}
public class RelayCommand<T> : ICommand {
readonly Action<T> _execute = null;
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand (Action<T> excute) { _execute = excute; }
public bool CanExecute (object parameter) => true;
public void Execute (object parameter) => _execute ((T) parameter);
}
Using .Net Framework 4.8.
Add package Microsoft.Xaml.Behaviors.Wpf to the project.
The ListView displays a list. When a selection is done, its value is shown on the console and the list is swapped (there are two lists shown alternatively).
Problem
The "color" list is longer than the "town" list, and when orange or black are selected, after the selection is shown on the console and the list is swapped (normal), the first item of the town list, Dakar, is triggered (unexpected). When debugging, after clicking orange, ProcessChange is invoked 4 times:
with parameter orange (expected),
with parameter null (unexpected but understandable and discarded in the code. The call is a reentrant call happenning while ProcessChange is processing orange)
with parameter Dakar (unexpected and wrong),
with null(same as second bullet, also reentrant, occurring while processing unexpected Dakar call)
The resulting console output:
Observation: This double event anomaly disappears if the grid rows are set this way:
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/> <!-- height set to * -->
</Grid.RowDefinitions>
(or if a 100ms delay is introduced, or a breakpoint is set, in the event handler).
And the question is:
What is the reason Dakar appears on the console after orange?
You do not take into account that after changing the list, the selection is reset and the command is triggered a second time without completing the current execution.
It is necessary to exclude its repeated execution during the execution of the command.
To recheck a command, you need to call its event from the code.
This requires a slightly different implementation of the command.
Base class:
using System;
using System.Windows;
using System.Windows.Input;
namespace Common
{
#region Delegates for WPF Command Methods
/// <summary>Delegate of the executive team method.</summary>
/// <param name="parameter">Command parameter.</param>
public delegate void ExecuteHandler(object parameter);
/// <summary>Command сan execute method delegate.</summary>
/// <param name="parameter">Command parameter.</param>
/// <returns><see langword="true"/> if command execution is allowed.</returns>
public delegate bool CanExecuteHandler(object parameter);
#endregion
#region Class commands - RelayCommand
/// <summary>A class that implements the ICommand interface for creating WPF commands.</summary>
public class RelayCommand : ICommand
{
private readonly CanExecuteHandler _canExecute;
private readonly ExecuteHandler _onExecute;
private readonly EventHandler _requerySuggested;
public event EventHandler CanExecuteChanged;
/// <summary>Command constructor.</summary>
/// <param name="execute">Executable command method.</param>
/// <param name="canExecute">Method allowing command execution.</param>
public RelayCommand(ExecuteHandler execute, CanExecuteHandler canExecute = null)
{
_onExecute = execute;
_canExecute = canExecute;
_requerySuggested = (o, e) => Invalidate();
CommandManager.RequerySuggested += _requerySuggested;
}
public void Invalidate()
=> Application.Current.Dispatcher.BeginInvoke
(
new Action(() => CanExecuteChanged?.Invoke(this, EventArgs.Empty)),
null
);
public bool CanExecute(object parameter) => _canExecute == null ? true : _canExecute.Invoke(parameter);
public void Execute(object parameter) => _onExecute?.Invoke(parameter);
}
#endregion
}
Command without parameter:
namespace Common
{
#region Delegates for WPF Parameterless Command Methods
/// <summary>Delegate to the execution method of a command without a parameter.</summary>
public delegate void ExecuteActionHandler();
/// <summary>Command state method delegate without parameter.</summary>
/// <returns><see langword="true"/> - if command execution is allowed.</returns>
public delegate bool CanExecuteActionHandler();
#endregion
/// <summary>Class for commands without parameters.</summary>
public class RelayActionCommand : RelayCommand
{
/// <summary>Command constructor.</summary>
/// <param name="execute">Command execution method.</param>
/// <param name="canExecute">Method allowing command execution.</param>
public RelayActionCommand(ExecuteActionHandler execute, CanExecuteActionHandler canExecute = null)
: base(_ => execute(), _ => canExecute()) { }
}
}
Typed Parameter Command
using System.ComponentModel;
namespace Common
{
#region Delegates for WPF Command Methods
/// <summary>Delegate of the executive team method.</summary>
/// <param name="parameter">Command parameter.</param>
public delegate void ExecuteHandler<T>(T parameter);
/// <summary>Command сan execute method delegate.</summary>
/// <param name="parameter">Command parameter.</param>
/// <returns><see langword="true"/> if command execution is allowed.</returns>
public delegate bool CanExecuteHandler<T>(T parameter);
#endregion
/// <summary>Class for typed parameter commands.</summary>
public class RelayCommand<T> : RelayCommand
{
/// <summary>Command constructor.</summary>
/// <param name="execute">Executable command method.</param>
/// <param name="canExecute">Method allowing command execution.</param>
public RelayCommand(ExecuteHandler<T> execute, CanExecuteHandler<T> canExecute = null)
: base
(
p => execute
(
p is T t
? t
: TypeDescriptor.GetConverter(typeof(T)).IsValid(p)
? (T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(p)
: default
),
p =>
canExecute == null
|| (p is T t
? canExecute(t)
: TypeDescriptor.GetConverter(typeof(T)).IsValid(p) && canExecute((T)TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(p))
)
)
{}
}
}
ViewModel
using Common;
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
namespace WpfApp2
{
public class ViewModel
{
bool isColorList;
string[] colors = { "blue", "yellow", "green", "orange", "black" };
string[] towns = { "Dakar", "Berlin", "Toronto" };
private bool isExecutedProcessChange = false;
public ObservableCollection<string> ItemCollection { get; }
= new ObservableCollection<string>();
public RelayCommand ProcessChangeCommand { get; }
public ViewModel()
{
ProcessChangeCommand = new RelayCommand<string>(ProcessChange, CanProcessChange);
Array.ForEach(colors, color => ItemCollection.Add(color));
}
private bool CanProcessChange(string parameter)
=>! isExecutedProcessChange;
public void ProcessChange(string parameter)
{
isExecutedProcessChange = true;
ProcessChangeCommand.Invalidate();
if (parameter != null)
{
Debug.WriteLine($"Selected: {parameter}");
ItemCollection.Clear();
Array.ForEach(isColorList ? colors : towns, color => ItemCollection.Add(color));
isColorList = !isColorList;
}
isExecutedProcessChange = false;
ProcessChangeCommand.Invalidate();
}
}
}
XAML does not require changes.
To find the reasons for double selection, I added a delay to the method:
public async void ProcessChange(string parameter)
{
isExecutedProcessChange = true;
ProcessChangeCommand.Invalidate();
if (parameter != null)
{
Debug.WriteLine($"Selected: {parameter}");
ItemCollection.Clear();
Array.ForEach(isColorList ? colors : towns, color => ItemCollection.Add(color));
isColorList = !isColorList;
await Task.Delay(500);
}
isExecutedProcessChange = false;
ProcessChangeCommand.Invalidate();
}
The double choice is almost over.
But sometimes it happens anyway.
Apparently, this is due to the bounce of the mouse button.
That is, instead of one signal of pressing a key from the mouse, sometimes two are received.
Setting a small delay (500ms) was able to filter out some of these double events.
The filter also helps not to run the command twice, but the item in the new list is still selected.
And you cannot select it again.
For the command to work, you have to select another element.
The solution may be to increase the delay and postpone it before changing the list.
Check how convenient it will be to use in this case:
public async void ProcessChange(string parameter)
{
isExecutedProcessChange = true;
ProcessChangeCommand.Invalidate();
if (parameter != null)
{
Debug.WriteLine($"Selected: {parameter}");
await Task.Delay(1000); // It is necessary to select the optimal delay time
ItemCollection.Clear();
Array.ForEach(isColorList ? colors : towns, color => ItemCollection.Add(color));
isColorList = !isColorList;
}
isExecutedProcessChange = false;
ProcessChangeCommand.Invalidate();
}
I am using MVVM solution provided in my previous question
XAML
<ProgressBar x:Name="StatusProgressBar" IsIndeterminate="{Binding IsIndeterminate, Mode=OneWay}" Height="18" Width="120" VerticalAlignment="Center" Background="White" BorderBrush="#FF05438D" />
ViewModel
Notice here in DoExecuteGetIpCommand() method if i do same thing in code behind on content rendered event works correctly but in mvvm all codes fires at same time so progress bar update after all time consuming process.
So i want to set ProgressBar IsIndeterminate Property true while time consuming method is working after done finally set IsIndeterminate to false. any idea to this and why it is happening but working fine in code behind Content rendered event.
public class MainWindowViewModel : INotifyPropertyChanged
{
public bool _isIndeterminate;
private string _ipAdrress;
private bool _errorOccured;
public event PropertyChangedEventHandler PropertyChanged;
GetPublicIP getPublicIP = new GetPublicIP();
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/*
public bool IpIndeterminate
{
get { return _isIndeterminate; }
set
{
_isIndeterminate = value;
OnPropertyChanged(nameof(IpIndeterminate));
}
}
*/
//OR
//IsIndeterminate here is problem
public bool IsIndeterminate => _isIndeterminate;
public string IpAddress => _ipAdrress;
public Brush IpForeground => _errorOccured ? new SolidColorBrush(Colors.IndianRed) : new SolidColorBrush(Colors.Black);
public FontWeight IpFontWeight => _errorOccured ? FontWeights.SemiBold : FontWeights.Normal;
public ICommand GetIpCommand
{
get { return new RelayCommand(param => DoExecuteGetIpCommand()); }
}
private async void DoExecuteGetIpCommand()
{
_isIndeterminate = true;
try
{
_errorOccured = false;
//_ipAdrress = await MyService.GetIpAddress();
_ipAdrress = await getPublicIP.GetIPAddressAsync();//time consuming method.
}
finally
{
//Commented this because progress bar immediately Is indeterminate go false.
//_isIndeterminate = false;
}
if (await getPublicIP.ExceptionOccursAsync() == true)
{
_errorOccured = true;
}
OnPropertyChanged(nameof(IsIndeterminate));
OnPropertyChanged(nameof(IpAddress));
OnPropertyChanged(nameof(IpForeground));
OnPropertyChanged(nameof(IpFontWeight));
}
}
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute"); //NOTTOTRANS
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion // ICommand Members
}
You have to change your code like this: (as dymanoid said)
private async void DoExecuteGetIpCommand()
{
_isIndeterminate = true;
//Tell the UI that something changed...
OnPropertyChanged(nameof(IsIndeterminate));
try
{
_errorOccured = false;
_ipAdrress = await getPublicIP.GetIPAddressAsync();//time consuming method.
}
finally
{
_isIndeterminate = false;
}
if (await getPublicIP.ExceptionOccursAsync() == true)
{
_errorOccured = true;
}
OnPropertyChanged(nameof(IsIndeterminate));
OnPropertyChanged(nameof(IpAddress));
OnPropertyChanged(nameof(IpForeground));
OnPropertyChanged(nameof(IpFontWeight));
}
All,
Binded complete Model, values are getting displayed in controls but not able to get button click working... any suggestion? what I m missing or doing wrong ? Thanks
<Window x:Class="test" Title="test" Height="350" Width="525">
<StackPanel Name="abc" Orientation="Vertical" DataContext="{Binding Path=EMP, Mode=TwoWay}" Margin="4" Height="153">
<Label Content="Last Name:" Margin="0,0,4,0"/>
<TextBox Width="250" Text="{Binding Path=LastName}" Height="20"/>
<Button Grid.Row="2" Margin="0,0,4,0" Height="40" Width="40"
Command="{Binding Path=SaveCommand}" />
</StackPanel>
</Window>
class EmployeeVM: ViewModelBase
{
private bool _Execute = true;
public EmployeeVM()
{
emp = new Model.Employee { FirstName = "abc", LastName = "xyz" };
}
private string sFirstName;
private string sLastName;
private Model.Employee emp;
public Model.Employee EMP
{
get{return emp;}
set{emp = value;
OnPropertyChanged("EMP");}
}
public string LastName
{
get { return sLastName; }
set
{
sLastName = value;
OnPropertyChanged("LastName");
}
}
#region Commands
private ICommand _SaveCommand;
public ICommand SaveCommand
{
get
{
return _SaveCommand = new CommandHandler(Save, _Execute);
}
}
#endregion
private void Save(object param)
{
ObservableCollection<Model.Employee> newIM = new ObservableCollection<Model.Employee>();
foreach(Model.Employee e in newIM)
{
string a = e.FirstName;
string b = e.LastName;
}
}
}
public class CommandHandler : ICommand
{
Action<object> _act;
bool _canExecute;
public CommandHandler(Action<object> act, bool canExecute)
{
_act = act;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_act(parameter);
}
}
You can write your own command.
Here is the baseclass I use for my commands.
It has some very basic things that make life easier.
the Execute method accepts an object, so you will be able to pass arrays
a viewmodel can be easily passed in, that is the one you will work with in your commands (this is most of the time the case, swipe it out if you do not need that)
the changed handler leverages the CommandManager. This is really very helpful
Perhaps you want to change some things. All I have added is in there because it is very helpful. (especially the viewmodel)
public abstract class CommandBase : ICommand
{
public abstract bool CanExecute(object o);
public abstract void Execute(object o);
public PropertyChangedBase ViewModel { get; set; }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
You would have implementations like
public class ExampleCommand : CommandBase
{
public ExampleCommand (PropertyChangedBase viewModel)
{
this.ViewModel = viewModel;
}
public override void Execute(object o)
{
// something like
var settings = UnityContainer.Resolve<ISettings>();
settings.MagicValue = (this.ViewModel as ConcreteViewModel).MagicValue;
}
public override bool CanExecute(object o)
{
return true;
}
}
in your ViewModel, you expose the command to the view by having a property:
public class ExampleViewModel : PropertyChangedBase
{
public ExampleViewModel ()
{
this.DoThisAndThatCommand = new ExampleCommand(this);
}
public CommandBase DoThisAndThatCommand { get; set; }
}
// and in XAML, you can use it like
<Button x:Name="Ok"
Command="{Binding DoThisAndThatCommand }" />
(Given you have connected the ViewModel and the View correctly by setting the DataContext of the View)
Now, whenever the Button is clicked, the Execute Method of the Command will get called.
You have your ViewModel right in the Command, so you can easily work with it.
It is very unusual to have a button inside a command or inside a ViewModel.
The trick about MVVM is to separate the View from the ViewModel and to not have
UIElements in the ViewModel.
If you do not have the PropertyChangedBase (this one comes with Caliburn.Micro) then I would suggest to use some easy INotifyPropertyChanged implementation.
I found this one here, should be german though
public abstract class NotifyPropertyChangedBase : INotifyPropertyChanged
{
#region < INotifyPropertyChanged > Members
/// <summary>
/// Is connected to a method which handle changes to a property (located in the WPF Data Binding Engine)
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raise the [PropertyChanged] event
/// </summary>
/// <param name="propertyName">The name of the property</param>
protected void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
private Dictionary<string, object> propertyValueStorage;
#region Constructor
public NotifyPropertyChangedBase()
{
this.propertyValueStorage = new Dictionary<string, object>();
}
#endregion
/// <summary>
/// Set the value of the property and raise the [PropertyChanged] event
/// (only if the saved value and the new value are not equal)
/// </summary>
/// <typeparam name="T">The property type</typeparam>
/// <param name="property">The property as a lambda expression</param>
/// <param name="value">The new value of the property</param>
protected void SetValue<T>(Expression<Func<T>> property, T value)
{
LambdaExpression lambdaExpression = property as LambdaExpression;
if (lambdaExpression == null)
{
throw new ArgumentException("Invalid lambda expression", "Lambda expression return value can't be null");
}
string propertyName = this.getPropertyName(lambdaExpression);
T storedValue = this.getValue<T>(propertyName);
if (!object.Equals(storedValue, value))
{
this.propertyValueStorage[propertyName] = value;
this.OnPropertyChanged(propertyName);
}
}
/// <summary> Get the value of the property </summary>
/// <typeparam name="T">The property type</typeparam>
/// <param name="property">The property as a lambda expression</param>
/// <returns>The value of the given property (or the default value)</returns>
protected T GetValue<T>(Expression<Func<T>> property)
{
LambdaExpression lambdaExpression = property as LambdaExpression;
if (lambdaExpression == null)
{
throw new ArgumentException("Invalid lambda expression", "Lambda expression return value can't be null");
}
string propertyName = this.getPropertyName(lambdaExpression);
return getValue<T>(propertyName);
}
/// <summary>
/// Try to get the value from the internal dictionary of the given property name
/// </summary>
/// <typeparam name="T">The property type</typeparam>
/// <param name="propertyName">The name of the property</param>
/// <returns>Retrieve the value from the internal dictionary</returns>
private T getValue<T>(string propertyName)
{
object value;
if (propertyValueStorage.TryGetValue(propertyName, out value))
{
return (T)value;
}
else
{
return default(T);
}
}
/// <summary>
/// Extract the property name from a lambda expression
/// </summary>
/// <param name="lambdaExpression">The lambda expression with the property</param>
/// <returns>The extracted property name</returns>
private string getPropertyName(LambdaExpression lambdaExpression)
{
MemberExpression memberExpression;
if (lambdaExpression.Body is UnaryExpression)
{
var unaryExpression = lambdaExpression.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambdaExpression.Body as MemberExpression;
}
return memberExpression.Member.Name;
}
}
It is very easy to use!
In your ViewModel you have to provide public properties for Binding (this is VERY important) and fire a change notification.
Here is an example of how to use that basic implementation of INPC (INotifyPropertyChanged)
public class LoginViewModel : NotifyPropertyChangedBase
{
public string UserName { get;set; }
}
This INPC implementation makes the NotifyOfPropertyChange call for you, you do not have to care for it! But you will have to inspect what fits your case best.
In your Question you already have a ViewModelBase. Perhaps you want to use this one instead of the above.
Please try to write in English, because I was confused what you wrote(such as "If u c above", "b/c", and so on :P..)
Anyway, as for your problem, this should fix it:
<UserControl.Resources>
<C:MultiValueConverter x:Key="MultiParamConverter"></C:MultiValueConverter>
</UserControl.Resources>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Button Name="Expander" Content="+" Width="25" Margin="4,0,4,0" Command="{Binding ExpanderCommand}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource MultiParamConverter}">
<Binding ElementName="Content"/>
<Binding ElementName="Expander"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
<Label FontWeight="Bold">GENERAL INFORMATION</Label>
</StackPanel>
<StackPanel Name="Content" Orientation="Vertical" Visibility="Collapsed">
<Label>Test</Label>
</StackPanel>
</StackPanel>
Command:
public ICommand ExpanderCommand
{
get
{
return new RelayCommand(delegate(object param)
{
var args = (object[])param;
var content = (UIElement)args[0];
var button = (Button)args[1];
content.Visibility = (content.Visibility == Visibility.Visible) ? Visibility.Collapsed : Visibility.Visible;
button.Content = (content.Visibility == Visibility.Visible) ? "-" : "+";
});
}
}
and the converter:
public class MultiValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return values.ToArray();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("No two way conversion, one way binding only.");
}
}
First of all, your view model class should either be a DependencyObject, or implementing INotifyPropertyChanged interface. You can always find a suitable MVVM library out there and use their base view model class.
Judging from your XAML that your CheckBox can bind to the same context of your button. So, while you are binding your buttonGetDetails into the ClickCommand you can also bind your chkDuplicates into a view model property, lets say CheckDuplicates. Therefore you won't need this as a parameter to your command, because the property will already be inside your view model. Such as:
class TestViewModel : ViewModelBase
{
bool checkDuplicates;
public bool CheckDuplicates
{
get { return checkDuplicates; }
set
{
if(checkDuplicates != value)
{
checkDuplicates = value;
OnPropertyChanged("CheckDuplicates");
}
}
}
//Everything else is same as before
// except the action
public void AnyAction(object param)
{
//no need for param anymore
//var parmValues = (Object)param;
bool test = this.CheckDuplicates;
}
}
Since this is supposed to model your view, you can get rid of any parameters of your command binding and make them part of your view model.
I am trying to use a RoutedCommand on my view so that I can use the CanExecute functionality, but the only way I can get it to work is with a DelegateCommand from Prism. When I try to use the RoutedCommand the button stays inactive and the CanExecute function never gets used.
I've tried putting a CommandBinding on my XAML but that gives a "Only instance methods on the generated or code-behind class are valid." error. Here is that code:
<Window.CommandBindings>
<CommandBinding Command="AddCommand"
Executed="my:SettingsDialogViewModel.AddCommandMethod"
CanExecute="my:SettingsDialogViewModel.AddCommandMethodCanExecute" />
</Window.CommandBindings>
I've also tried setting up a CommandBinding in code, but that doesn't help either. I'm just not sure how to get it to work, short of sticking it in the code-behind, or implementing some ridiculously complicated looking thing I've found on the web.
Thanks for any help :)
EDIT:
Here are the methods I am trying to use:
public void AddCommandMethod()
{
if (SelectedMain != null)
{
SelectedMain.IsDirty = true;
_faveAppList.Add(SelectedMain);
SelectedMain.ListOrder = _faveAppList.Count;
_mainAppList.Remove(SelectedMain);
_listDirty = true;
}
}
public void AddCommandMethodCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
That isn't the proper MVVM notation. I'll provide one way of doing this.
// MyView.cs
public class MyView : UserControl
{
public MyViewViewModel ViewModel
{
get { return (MyViewViewModel) DataContext;}
set { DataContext = value; }
}
}
// DelegateCommand.cs
public class DelegateCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public DelegateCommand(Action<object> execute)
: this(execute, null) {}
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public override bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public override void Execute(object parameter)
{
_execute(parameter);
}
}
// MyViewViewModel.cs
public class MyViewViewModel
{
public ICommand AddCommand {get;set;}
public MyViewViewModel()
{
AddCommand = new DelegateCommand (AddCommandMethod, AddCommandMethodCanExecute);
}
private void AddCommandMethod (object parameter)
{
}
private bool AddCommandMethodCanExecute(object parameter)
{
// Logic here
return true;
}
}
// MyView.xaml
<Button Command="{Binding AddCommand}" />
A better option would be to implement the ICommand interface and write your logic in the implemented methods. Then your view model can return your custom command and you could just bind to it from your view.
This will separate the actual command implementation from your view model but you can still nicely implement the logic within your view model.
Something like this:
public abstract class BaseCommand : ICommand
{
// needed to connect to WPF's commanding system
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public abstract bool CanExecute(object parameter);
public abstract void Execute(object parameter);
}
public class AddCommand : BaseCommand
{
private readonly MyViewModel _vm;
public AddCommand(MyViewModel vm)
{
this._vm = vm;
}
public override bool CanExecute(object parameter)
{
// delegate back to your view model
return _vm.CanExecuteAddCommand(parameter);
}
public override void Execute(object parameter)
{
_vm.ExecuteAddCommand(parameter);
}
}
public class MyViewModel
{
public ICommand AddCommand { get; private set; }
public MyViewModel()
{
AddCommand = new AddCommand(this);
}
public bool CanExecuteAddCommand(object parameter)
{
}
public void ExecuteAddCommand(object parameter)
{
}
}
Then just bind controls that issues the command.
<Button Command="{Binding AddCommand}">...</Button>