I want to pass assignment_id which is a command parameter of myButton to .cs file to update values in database. I wrote this code but when i clicked at myButton it showed a Message Box that Method and Operation is failed. How can i solve this Problem?
private void Command(Int32 parameter)
{
p = parameter;
}
private void btnUpdate_Click(Object sender, RoutedEventArgs e)
{
try
{
SqlConnection con = new SqlConnection("server=(local); Integrated Security=true; Database=nrcs");
con.Open();
SqlCommand comm = new SqlCommand("UPDATE Assignments SET assignment_title='myassignment' WHERE assignment_id=" + p + ";", con);
comm.ExecuteNonQuery();
con.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
Here Xaml Code:
<Button x:Name="myButton" Content="update" Command="{Binding Command}" CommandParameter="{Binding assignment_id}" Click="btnUpdate_Click" ></Button>
It seems like you might be trying to mix commanding and event handlers. If you want to use commanding, you should first create a viewmodel with the command defined there. Then set your datacontext in xaml to that viewmodel. Then you should be able to bind the commands the way you want to. A common approach would be to first define a RelayCommand class.
A simple relay class might look something like this:
public class RelayCommand<T> : ICommand
{
#region Fields
readonly Action<T> _execute = null;
readonly Predicate<T> _canExecute = null;
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of <see cref="DelegateCommand{T}"/>.
/// </summary>
/// <param name="execute">Delegate to execute when Execute is called on the command. This can be null to just hook up a CanExecute delegate.</param>
/// <remarks><seealso cref="CanExecute"/> will always return true.</remarks>
public RelayCommand(Action<T> 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<T> execute, Predicate<T> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion
#region ICommand Members
///<summary>
///Defines the method that determines whether the command can execute in its current state.
///</summary>
///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
///<returns>
///true if this command can be executed; otherwise, false.
///</returns>
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute((T)parameter);
}
///<summary>
///Occurs when changes occur that affect whether or not the command should execute.
///</summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
///<summary>
///Defines the method to be called when the command is invoked.
///</summary>
///<param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to <see langword="null" />.</param>
public void Execute(object parameter)
{
_execute((T)parameter);
}
#endregion
}
That example came from this question Why RelayCommand
Once you have a RelayCommand class, in your ViewModel you can define commands such as
public class MainWindowViewModel
{
private ICommand _Command;
public ICommand Command
{
get
{
if (_Command == null)
_Command = new RelayCommand<object>((x) => ExecuteCommand(x));
return _Command;
}
set
{
_Command = value;
}
}
private void ExecuteCommand(object parameter)
{
try
{
if (!string.IsNullOrEmpty(parameter.ToString()){
//Do sql call here. parameter.ToString() is a string representation of the parameter that was bound on the xaml
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
}
Now that you have that set up, just set your view's data context to the view model. You can do this in the code behind, or in the xaml, whatever you want. Something like
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
Now your binding in xaml to the command should look like:
<Button x:Name="myButton" Content="update" Command="{Binding Command}" CommandParameter="{Binding assignment_id}"></Button>
However while we're here, please change your sql command to use parameters instead of concatenating it as a string like that. Doing it the way you are now will leave you open to sql injection.
Edit: it should be noted that the object you are binding to the CommandParameter should exist in your view model as well. Ideally you would have a model containing the property for assignment_id then you would instantiate a type of that model in the view model, then bind that instance of the model to your View.
I think it's the order in which the click event (btnUpdate_Click) and the command are called. If the event is called first, the variable 'p' doesn't have a value, and the query would fail.
I think you should use either the event or the command to perform the query, not both of them. if you intend to keep using the MVVM pattern (which I recommend), use the command in another class that would be your viewmodel.
Related
I am developing something using wpf mvvm.
Currently, my implementation style is a bit unusual.
This is to prevent the sauce from gathering in one place.
Async void should be avoided, but I don't know how to avoid it.
Any solution?
Below is the source I used. (RelayCommand is generic and I need to create a new async command.)
<View.xaml>, <View.xaml.cs>
<BusyIndicator IsBusy="{IsBusy, mode=TwoWay}">
<Listbox ItemsSource="{Binding Models}"/>
<Button Command="{Binding OnClickButtonCommand}"
CommandParameter="whether the value(string) exists or not"/>
</BusyIndicator>
this.DataContext = new ViewModel();
<ViewModel.cs>
public ViewModel() { }
private isBusy;
public isBusy
{
get => isBusy;
set => SetField(ref isBusy, value);
}
public ObservableCollection<Model> Models { get; set; }
public ICommand OnClickButtonCommand { get => new ButtonCommand(this); }
<ButtonCommand.cs>
public ButtonCommand : RelayCommand
{
private ViewModel viewModel;
public ViewModel(ViewModel viewModel)
{
this.viewModel = viewModel
}
public override async void Execute(object obj)
{
viewModel.IsBusy = true;
await Task.Delay(1500);
viewModel.Models = new ObservableCollection<Model>(await Data());
viewModel.IsBusy = false;
}
private async Task<List<Model>> Data()
{
await Task.Delay(100);
var data = new List<Model>();
...
return data;
}
}
*Inserting await delay is to prevent debugging warning.
<CommandBase.cs>
public interface IRelayCommand : ICommand
{
new void Execute(object obj);
}
public abstract class RelayCommand : IRelayCommand
{
public RelayCommand() { }
public event EventHandler CanExecuteChanged
{
add => CommandManager.RequerySuggested += value;
remove => CommandManager.RequerySuggested -= value;
}
bool ICommand.CanExecute(object obj)
{
if (obj?.ToString().Length == 0)
{
return false;
}
else
{
return true;
}
//return canExecute == null || this.canExecute.Invoke(parameter);
}
public abstract void Execute(object obj);
}
And if my code is weird or there is better code, please let me know!
Thanks. :)
https://johnthiriet.com/mvvm-going-async-with-async-command/
I've seen a lot of this site and other samples, but it's an example using a typical fuction,
so I can't apply it...
I'm using the AsyncCommand implementation from James Montemagno's MVVM-Helpers library (currently also implemented in the Xamarin Community Toolkit), you can take a look at the implementation here: AsyncCommand implementation
You can use it like this:
Install the Refactored.MvvmHelpers nuget package.
MyViewModel.cs
public class MyViewModel {
//other code goes here (Models collection, IsBusy, etc)
public ICommand DoWorkCommand => new AsyncCommand(DoWorkAsync);
public ICommand DoWorkWithParameterCommand => new AsyncCommand<bool>(DoWorkWithParameterAsync);
private async Task DoWorkAsync(){
//simulate work
await Task.Delay(1000);
}
private async Task DoWorkWithParameterAsync(bool itemExists){
//simulate work
await Task.Delay(1000);
}
}
MyView.cs
<BusyIndicator IsBusy="{IsBusy, mode=TwoWay}">
<Listbox ItemsSource="{Binding Models}"/>
<Button Command="{Binding DoWorkCommand}" />
<Button Command="{Binding DoWorkWithParameterCommand}" CommandParameter="False"/>
</BusyIndicator>
An example of my implementation of base classes:
BaseInpc,
RelayCommand, RelayCommandAsync,
RelayCommand<T>, RelayCommandAsync<T>.
There are a lot of verbose documentation tags in the code.
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Simplified
{
/// <summary>Base class with implementation of the <see cref="INotifyPropertyChanged"/> interface.</summary>
public abstract class BaseInpc : INotifyPropertyChanged
{
/// <inheritdoc cref="INotifyPropertyChanged"/>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>The protected method for raising the event <see cref = "PropertyChanged"/>.</summary>
/// <param name="propertyName">The name of the changed property.
/// If the value is not specified, the name of the method in which the call was made is used.</param>
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary> Protected method for assigning a value to a field and raising
/// an event <see cref = "PropertyChanged" />. </summary>
/// <typeparam name = "T"> The type of the field and assigned value. </typeparam>
/// <param name = "propertyFiled"> Field reference. </param>
/// <param name = "newValue"> The value to assign. </param>
/// <param name = "propertyName"> The name of the changed property.
/// If no value is specified, then the name of the method
/// in which the call was made is used. </param>
/// <remarks> The method is intended for use in the property setter. <br/>
/// To check for changes,
/// used the <see cref = "object.Equals (object, object)" /> method.
/// If the assigned value is not equivalent to the field value,
/// then it is assigned to the field. <br/>
/// After the assignment, an event is created <see cref = "PropertyChanged" />
/// by calling the method <see cref = "RaisePropertyChanged (string)" />
/// passing the parameter <paramref name = "propertyName" />. <br/>
/// After the event is created,
/// the <see cref = "OnPropertyChanged (string, object, object)" />
/// method is called. </remarks>
protected void Set<T>(ref T propertyFiled, in T newValue, [CallerMemberName] in string propertyName = null)
{
if (!Equals(propertyFiled, newValue))
{
T oldValue = propertyFiled;
propertyFiled = newValue;
RaisePropertyChanged(propertyName);
OnPropertyChanged(propertyName, oldValue, newValue);
}
}
/// <summary> The protected virtual method is called after the property has been assigned a value and after the event is raised <see cref = "PropertyChanged" />. </summary>
/// <param name = "propertyName"> The name of the changed property. </param>
/// <param name = "oldValue"> The old value of the property. </param>
/// <param name = "newValue"> The new value of the property. </param>
/// <remarks> Can be overridden in derived classes to respond to property value changes. <br/>
/// It is recommended to call the base method as the first operator in the overridden method. <br/>
/// If the overridden method does not call the base class, then an unwanted change in the base class logic is possible. </remarks>
protected virtual void OnPropertyChanged(in string propertyName, in object oldValue, in object newValue) { }
}
}
using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
namespace Simplified
{
/// <summary> A class that implements <see cref = "ICommand" />. <br/>
/// Implementation taken from <see href="https://www.cyberforum.ru/wpf-silverlight/thread2390714-page4.html#post13535649"/>
/// and added a constructor for methods without a parameter.</summary>
public class RelayCommand : ICommand
{
protected readonly CanExecuteHandler<object> canExecute;
protected readonly ExecuteHandler<object> execute;
private readonly EventHandler requerySuggested;
/// <inheritdoc cref="ICommand.CanExecuteChanged"/>
public event EventHandler CanExecuteChanged;
/// <summary> Command constructor. </summary>
/// <param name = "execute"> Command method to execute. </param>
/// <param name = "canExecute"> Method that returns the state of the command. </param>
public RelayCommand(ExecuteHandler<object> execute, CanExecuteHandler<object> canExecute = null)
: this()
{
this.execute = execute ?? throw new ArgumentNullException(nameof(execute));
this.canExecute = canExecute;
}
/// <inheritdoc cref="RelayCommand(ExecuteHandler{object}, CanExecuteHandler{object})"/>
public RelayCommand(ExecuteHandler execute, CanExecuteHandler canExecute = null)
: this
(
p => execute(),
p => canExecute?.Invoke() ?? true
)
{ }
private readonly Dispatcher dispatcher = Application.Current.Dispatcher;
/// <summary> The method that raises the event <see cref="CanExecuteChanged"/>.</summary>
public void RaiseCanExecuteChanged()
{
if (dispatcher.CheckAccess())
{
invalidate();
}
else
{
_ = dispatcher.BeginInvoke(invalidate);
}
}
private readonly Action invalidate;
private RelayCommand()
{
invalidate = () => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
requerySuggested = (o, e) => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
CommandManager.RequerySuggested += requerySuggested;
}
/// <inheritdoc cref="ICommand.CanExecute(object)"/>
public bool CanExecute(object parameter)
{
return canExecute?.Invoke(parameter) ?? true;
}
/// <inheritdoc cref="ICommand.Execute(object)"/>
public void Execute(object parameter)
{
execute?.Invoke(parameter);
}
}
}
using System;
using System.Collections;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Simplified
{
/// <summary>A class that implements asynchronous commands with the execution
/// of the <see cref="RelayCommand.Execute(object)"/> method in Task. </summary>
/// <remarks>Only one call to the <see cref="RelayCommand.Execute(object)"/>
/// method is allowed at a time.
/// During the execution of the method, the <see cref="IsBusy"/> flag is set and
/// the <see cref="RelayCommand.CanExecute(object)"/> method of the command will return false.
/// The <see cref="INotifyDataErrorInfo"/> interface is implemented to notify about
/// an erroneous call to the <see cref="RelayCommand.Execute(object)"/> method before
/// the previous execution completes and about exceptions during the
/// execution of <see cref="RelayCommand.Execute(object)"/>.
/// To notify about changes in property values, the <see cref="INotifyPropertyChanged"/>
/// interface is implemented.</remarks>
public class RelayCommandAsync : RelayCommand, ICommand, INotifyPropertyChanged, INotifyDataErrorInfo
{
/// <inheritdoc cref="INotifyPropertyChanged.PropertyChanged"/>
public event PropertyChangedEventHandler PropertyChanged;
/// <inheritdoc cref="INotifyDataErrorInfo.ErrorsChanged"/>
public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;
/// <summary>The command is in the execution state of the <see cref="RelayCommand.Execute(object)"/> method.</summary>
public bool IsBusy { get; private set; }
/// <inheritdoc cref="INotifyDataErrorInfo.HasErrors"/>
public bool HasErrors { get; private set; }
/// <summary>Exception from the last execution of the <see cref="RelayCommand.Execute(object)"/> method.</summary>
public Exception ExecuteException { get; private set; }
// A flag indicating a "call to execute busy a command" error.
private bool isBusyExecuteError;
/// <summary>Sets a value to the <see cref="IsBisy"/> property and notifies of its change.</summary>
/// <param name="isBusy">The value for the property.</param>
protected void SetIsBusy(bool isBusy)
{
if (IsBusy != isBusy)
{
IsBusy = isBusy;
PropertyChanged?.Invoke(this, Args.IsBusyPropertyEventArgs);
RaiseCanExecuteChanged();
}
}
/// <summary>Sets the HasErrors property and reports an entity-level error.</summary>
/// <param name="hasErrors">The value for the property.</param>
protected void SetEntityHasErrors(bool hasErrors)
=> SetHasErrors(hasErrors, Args.EntityLevelErrorsEventArgs);
/// <summary>Sets the HasErrors property and reports an entity or property level error.</summary>
/// <param name="hasErrors">The value for the property.</param>
/// <param name="args">Argument with data about the error level.</param>
protected void SetHasErrors(bool hasErrors, DataErrorsChangedEventArgs args)
{
if (HasErrors != hasErrors)
{
HasErrors = hasErrors;
PropertyChanged?.Invoke(this, Args.HasErrorsPropertyEventArgs);
}
ErrorsChanged?.Invoke(this, args);
}
/// <summary>Sets a value to the <see cref="ExecuteException"/> property and notifies of its change.</summary>
/// <param name="exception">The value for the property.</param>
protected void SetExecuteException(Exception exception)
{
if (ExecuteException != exception)
{
ExecuteException = exception;
PropertyChanged?.Invoke(this, Args.ExecuteExceptionPropertyEventArgs);
}
}
/// <inheritdoc cref="RelayCommand(ExecuteHandler{object}, CanExecuteHandler{object})"/>
public RelayCommandAsync(ExecuteHandler<object> execute, CanExecuteHandler<object> canExecute = null)
: this(new AsyncData(execute, canExecute))
{ }
/// <inheritdoc cref="RelayCommand(ExecuteHandler, CanExecuteHandler)"/>
public RelayCommandAsync(ExecuteHandler execute, CanExecuteHandler canExecute = null)
: this(new AsyncData(execute, canExecute))
{ }
// The field for storing additional, auxiliary data generated
// during the generation of the asynchronous method, wrapping
// the one obtained in the constructor.
private readonly AsyncData data;
/// <inheritdoc cref="RelayCommand(ExecuteHandler{object}, CanExecuteHandler{object})"/>
protected RelayCommandAsync(AsyncData data)
: base(data.ExecuteAsync, data.CanExecuteAsync)
{
this.data = data;
this.data.commandAsync = this;
}
/// <inheritdoc cref="INotifyDataErrorInfo.GetErrors(string)"/>
public IEnumerable GetErrors(string propertyName)
{
if (string.IsNullOrEmpty(propertyName))
{
if (isBusyExecuteError)
{
yield return Args.BusyExecuteErrorMessage;
}
if (ExecuteException != null)
{
yield return ExecuteException;
}
}
IEnumerable errors = GetErrorsOverride(propertyName);
if (errors != null)
{
foreach (var error in errors)
{
yield return error;
}
}
}
/// <summary>Method overridden in derived classes to add error information</summary>
/// <param name="propertyName">The name of the property to retrieve validation
/// errors for; or null or Empty, to retrieve entity-level errors.</param>
/// <returns>The validation errors for the property or entity.</returns>
protected virtual IEnumerable GetErrorsOverride(string propertyName)
=> null;
/// <summary>A class with persistent elements to avoid re-creating them frequently.</summary>
public static class Args
{
public const string BusyExecuteErrorMessage = "Called the execution of a command when it is busy.";
public static readonly PropertyChangedEventArgs IsBusyPropertyEventArgs = new PropertyChangedEventArgs(nameof(IsBusy));
public static readonly PropertyChangedEventArgs HasErrorsPropertyEventArgs = new PropertyChangedEventArgs(nameof(HasErrors));
public static readonly PropertyChangedEventArgs ExecuteExceptionPropertyEventArgs = new PropertyChangedEventArgs(nameof(ExecuteException));
public static readonly DataErrorsChangedEventArgs EntityLevelErrorsEventArgs = new DataErrorsChangedEventArgs(string.Empty);
}
/// <summary>A class for storing additional, auxiliary data and methods that are generated
/// when generating asynchronous methods that wrap the synchronous methods received
/// in the constructor.</summary>
protected class AsyncData
{
public RelayCommandAsync commandAsync;
public async void ExecuteAsync(object parameter)
{
if (commandAsync.IsBusy)
{
commandAsync.isBusyExecuteError = true;
commandAsync.SetEntityHasErrors(true);
}
else
{
commandAsync.SetIsBusy(true);
try
{
await Task.Run(() => execute(parameter));
commandAsync.isBusyExecuteError = false;
commandAsync.SetExecuteException(null);
commandAsync.SetEntityHasErrors(false);
}
catch (Exception ex)
{
commandAsync.SetExecuteException(ex);
commandAsync.SetEntityHasErrors(true);
}
finally
{
commandAsync.SetIsBusy(false);
}
}
}
public CanExecuteHandler<object> CanExecuteAsync { get; }
private bool canExecuteNullAsync(object parameter) => !commandAsync.IsBusy;
private bool canExecuteAsync(object parameter) => !commandAsync.IsBusy && canExecute(parameter);
private readonly ExecuteHandler<object> execute;
private readonly CanExecuteHandler<object> canExecute;
/// <inheritdoc cref="AsyncData(ExecuteHandler, CanExecuteHandler)"/>
public AsyncData(ExecuteHandler<object> execute, CanExecuteHandler<object> canExecute)
{
this.execute = execute ?? throw new ArgumentNullException(nameof(execute));
if (canExecute == null)
{
CanExecuteAsync = canExecuteNullAsync;
}
else
{
this.canExecute = canExecute;
CanExecuteAsync = canExecuteAsync;
}
}
/// <summary>Creates an instance.</summary>
/// <param name="execute">Synchronous Execute method.</param>
/// <param name="canExecute">Synchronous CanExecute method.</param>
public AsyncData(ExecuteHandler execute, CanExecuteHandler canExecute)
{
if (execute == null)
{
throw new ArgumentNullException(nameof(execute));
}
this.execute = p => execute();
if (canExecute == null)
{
CanExecuteAsync = canExecuteNullAsync;
}
else
{
this.canExecute = p => canExecute();
CanExecuteAsync = canExecuteAsync;
}
}
}
}
}
Some of the classes did not fit into the answer.
Take them from here.
An example of using RelayCommand for your code.
public class ViewModel : BaseInpc
{
private bool _isBusy;
public bool IsBusy
{
get => _isBusy;
set => Set(ref _isBusy, value);
}
public ObservableCollection<Model> Models { get; set; }
// Under no circumstances should you get an instance of a command in this way.
// As such, each time the property is accessed, a NEW instance of the command is created.
// As a result, in each element of the Window, in the ViewMode, etc. there are different instances everywhere.
// This is a very common cause of various bugs.
//
//public ICommand OnClickButtonCommand { get => new ButtonCommand(this); }
// One of the options for the correct team creation.
private RelayCommand _onClickButtonCommand;
public RelayCommand OnClickButtonCommand => _onClickButtonCommand
?? (_onClickButtonCommand = new RelayCommand(ExecuteOnClick));
// Executing command method
private async void ExecuteOnClick(object parameter)
{
IsBusy = true;
await Task.Delay(1500);
Models = new ObservableCollection<Model>(await Data());
IsBusy = false;
}
private async Task<List<Model>> Data()
{
await Task.Delay(100);
var data = new List<Model>();
// Some code
return data;
}
}
In such an implementation, you can create a method for each command, its own IsBusy property and other members of which it needs.
But to a large extent, all this (except for the synchronous executing method) can be encapsulated in the command itself.
Encapsulating the execution method does not make sense, since it is because of it that the command instances differ from each other.
My example using asynchronous commands
In the example, a blinking background, in order to understand at what moment the GUI lags occur - blinking during lags stops.
There are also several buttons to demonstrate the difference between synchronous and asynchronous method execution, and how errors and exceptions in them affect applications.
Click them in various combinations.
When errors and exceptions appear, they are displayed under the buttons at the bottom of the window.
If you have any questions during testing - ask.
using Simplified;
using System;
using System.Threading;
namespace RelayCommandAsyncTest
{
public class TestViewModel : BaseInpc
{
private int _count;
// Command execution counter (common for all commands)
public int Count { get => _count; set => Set(ref _count, value); }
// Аsynchronous command
public RelayCommandAsync TestCommandAsync { get; }
// Synchronous command
public RelayCommand TestCommand { get; }
// Synchronous command with Lags
public RelayCommand TestLagsCommand { get; }
// Synchronous command executing asynchronous command
// without checking it IsBusy.
public RelayCommand TestExecuteCommandAsync { get; }
// Long running execution method for validating
// an asynchronous command
private void ExecuteForAsync(object obj)
{
Thread.Sleep(2000);
Count++;
// If the command parameter is not passed,
// then there will be an exception
Thread.Sleep(Convert.ToInt32(obj));
}
// Long running execution method for creating
// lags in a synchronous command
private void ExecuteLags(object obj)
{
isExecuteLagsCommand = true;
TestLagsCommand.RaiseCanExecuteChanged();
Thread.Sleep(2000);
Count++;
// If the command parameter is not passed,
// then there will be an exception
Thread.Sleep(Convert.ToInt32(obj));
isExecuteLagsCommand = false;
TestLagsCommand.RaiseCanExecuteChanged();
}
bool isExecuteLagsCommand;
// Fast executing method for checking lags with
// a synchronous command
private void Execute(object obj)
{
Count++;
// If the command parameter is not passed,
// then there will be an exception
Thread.Sleep(Convert.ToInt32(obj));
}
// A command that executes an asynchronous command without checking if it is busy.
// If the asynchronous command is busy, it will generate a validation error.
private void ExecuteCommandAsync(object obj)
{
Count++;
TestCommandAsync.Execute(obj);
}
public TestViewModel()
{
TestCommandAsync = new RelayCommandAsync(ExecuteForAsync);
TestCommand = new RelayCommand(Execute);
TestLagsCommand = new RelayCommand(ExecuteLags, p => !isExecuteLagsCommand);
TestExecuteCommandAsync = new RelayCommand(ExecuteCommandAsync);
timer = new Timer
(
_ => TestCommandAsync.RaiseCanExecuteChanged(),
null,
0,
5000
);
}
// Timer to check for raising CanExecuteChanged from any thread.
private readonly Timer timer;
}
}
<Window x:Class="RelayCommandAsyncTest.CommandTestWindow"
x:Name="main"
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"
xmlns:local="clr-namespace:RelayCommandAsyncTest" xmlns:common="clr-namespace:Common;assembly=Common"
mc:Ignorable="d"
Title="CommandTestWindow" Height="1000" Width="800"
FontSize="30">
<FrameworkElement.Triggers>
<EventTrigger RoutedEvent="Loaded" SourceName="main">
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="FlashingBrush"
Storyboard.TargetProperty="Color"
Duration="0:0:0.5"
To="LightYellow"
AutoReverse="True"
RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</FrameworkElement.Triggers>
<FrameworkElement.DataContext>
<local:TestViewModel/>
</FrameworkElement.DataContext>
<FrameworkElement.Resources>
<ControlTemplate x:Key="validationTemplate">
<Border BorderThickness="5"
BorderBrush="Red">
<Grid IsHitTestVisible="False">
<Viewbox>
<TextBlock Foreground="Red" FontSize="80"
Text="! ! !"/>
</Viewbox>
<AdornedElementPlaceholder/>
</Grid>
</Border>
</ControlTemplate>
<Style TargetType="Button">
<Setter Property="Validation.ErrorTemplate" Value="{DynamicResource validationTemplate}"/>
</Style>
</FrameworkElement.Resources>
<Grid>
<Grid.Background>
<SolidColorBrush x:Name="FlashingBrush" Color="LightGreen"/>
</Grid.Background>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto" />
<RowDefinition/>
</Grid.RowDefinitions>
<UniformGrid Columns="2">
<Button x:Name="button" Content="Async throw Exception"
Command="{Binding TestCommandAsync}"
CommandParameter="qwerty" Margin="15"/>
<Button Content="Async Normal"
Command="{Binding TestCommandAsync}"
CommandParameter="2000" Margin="15"/>
<Button Content="Sync Lags"
Command="{Binding TestLagsCommand}"
CommandParameter="2000" Margin="15"/>
<Button Content="IsBusy is not checked"
Command="{Binding TestExecuteCommandAsync}" Margin="15"
CommandParameter="0">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding TestCommandAsync.HasErrors}"
Value="True">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="ExtraBold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Content="Sync throw Exception"
Command="{Binding TestCommand}"
CommandParameter="qwerty" Margin="15"/>
<Button Content="Sync Normal"
Command="{Binding TestCommand}" Margin="15"
CommandParameter="2000"/>
</UniformGrid>
<TextBlock Grid.Row="1" Margin="5">
<Run Text="IsBusy:"/>
<Run Text="{Binding TestCommandAsync.IsBusy, Mode=OneWay}"/>
<Run Text=" "/>
<Run Text="HasErrors:"/>
<Run Text="{Binding TestCommandAsync.HasErrors, Mode=OneWay}"/>
<Run Text=" "/>
<Run Text="Count:"/>
<Run Text="{Binding Count}"/>
</TextBlock>
<ScrollViewer Grid.Row="2"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding ElementName=button, Path=(Validation.Errors)}"
DisplayMemberPath="ErrorContent"
FontSize="15"/>
</ScrollViewer>
</Grid>
</Window>
I was trying to undestand how RoutedUICommand Works. I have worked with MVVM in Wpf, and was trying to under stand ICommand implementation actually works in wpf. However, I am unable to understand something.
public class RelayCommand : ICommand
{
private Action<Task> callBack;
private bool runOnUi;
private Action<object> whatToExecute;
private Func<object, bool> whenToExecute;
public RelayCommand(Func<object, bool> whenToExecute, Action<object> whatToExecute,
bool runOnUi = true, Action<Task> callBack = null)
{
this.whenToExecute = whenToExecute;
this.whatToExecute = whatToExecute;
this.runOnUi = runOnUi;
this.callBack = callBack;
}
/// <summary>
/// This is an event from Interface
/// </summary>
public event EventHandler CanExecuteChanged;
/// <summary>
/// Decides whether the command can execute or not.
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public bool CanExecute(object parameter)
{
if (this.whenToExecute != null)
{
try
{
return this.whenToExecute(parameter);
}
catch
{
return false;
}
}
return true;
}
/// <summary>
/// Called when CanExecute is true and command is fired.
/// </summary>
/// <param name="parameter"></param>
public void Execute(object parameter)
{
if (this.whatToExecute != null)
{
if (this.runOnUi)
{
this.whatToExecute(parameter);
}
else
{
var parallelTask = Task.Run(() =>
{
this.whatToExecute(parameter);
});
if (this.callBack != null)
parallelTask.ContinueWith(this.callBack);
}
}
}
}
This is the custom implementation of ICommand interface.
I have added two menuitems to context menu of label. First with ApplicationCommands.Close and second on with my customer RelayCommand type.
this.Label1.ContextMenu = new ContextMenu();
var menuItem1 = new MenuItem();//Source
menuItem1.Command = ApplicationCommands.Close; //Command
var commandBindingObject = new CommandBinding(menuItem1.Command);
commandBindingObject.CanExecute += this.MenuItem1Close_CanExecute;
commandBindingObject.Executed += this.MenuItem1Close_Executed;
menuItem1.CommandBindings.Add(commandBindingObject);
var menuItem2 = new MenuItem();//Source
menuItem2.Header = "Custom Command";
menuItem2.Command = new RelayCommand(o =>
{
return true;
},
o =>
{
}); //Command
var commandBindingObject2 = new CommandBinding(menuItem2.Command);
commandBindingObject2.CanExecute += this.MenuItem2Close_CanExecute;
commandBindingObject2.Executed += this.MenuItem2Close_Executed;
menuItem2.CommandBindings.Add(commandBindingObject2);
this.Label1.ContextMenu.Items.Add(menuItem1);
this.Label1.ContextMenu.Items.Add(menuItem2);
Whenever I click the first menu item handler is menuitem1 handler for executed is called, but not for the menuitem2. I am just trying to understand how command pattern is implemented in wpf, so in addition any links to such would also be of great help.
Routed Commands
The ApplicationCommands.Close command is a RoutedUICommand. Routed commands raise the PreviewExecuted and Executed routed events which traverse the element tree until they find a command binding. If one is found the corresponding ExecutedRoutedEventHandler is called. In case of menuItem1 the executed handler is MenuItem1Close_Executed. The same applies to the PreviewCanExecute and CanExecute events and the MenuItem1Close_CanExecute handler as CanExecuteRoutedEventHandler.
Relay Commands
Your menuItem2 uses a RelayCommand. This type of command uses delegates for CanExecute and Execute, instead of traversing the element tree in search for command bindings.
In your case the following lines are unnecessary.
var commandBindingObject2 = new CommandBinding(menuItem2.Command);
commandBindingObject2.CanExecute += this.MenuItem2Close_CanExecute;
commandBindingObject2.Executed += this.MenuItem2Close_Executed;
menuItem2.CommandBindings.Add(commandBindingObject2);
Instead pass methods or anonymous methods for whenToExecute and whatToExecute.
menuItem2.Command = new RelayCommand(CanExecute, Execute);
You cannot pass MenuItem1Close_CanExecute or MenuItem1Close_Executed here, because they have incompatible signatures:
void CanExecuteRoutedEventHandler(object sender, CanExecuteRoutedEventArgs e)
void ExecutedRoutedEventHandler(object sender, ExecutedRoutedEventArgs e)
CanExecute has to be a Func<object, bool> and Execute has to be an Action<object> delegate.
bool CanExecute(object obj)
void Execute(object obj)
I have the following process:
Create a new object in a view/viewmodel and save it in the database via a database handler
Database Handler implements INotifyProperyChanged. So after 1. the database handler notifies other viewmodels about the change. One of the corresponding view contains a datagrid which is bound to a ObservableCollection, which is stored in the database. So the view/viewmodel access the database actively.
The creation of the new object (see 1.) changes the database content and should also updating the datagrid view. So the viewmodel is informed about the change via the notification. So the next step would be to access the database again and filling or renew the observable with these new data.
So how to force the refresh of the datagrid content?
Tried following:
Assigning the ObservableCollection temporarly to null does not refresh the datagrid, because it does not notify the datagrid view.
Clearing the Collection and adding all items to the new collection (works, but sounds a bit weired because in most cases I will add simply one object to the database)
Here is some sample code:
First the database handler, which handles the data exchange between viewmodels and database. The DBHandler implements the INotifyPropertyChanged to qualify the viewmodels to react on changes in the database. Currently the DBHandler notifies only if the Names List is changed:
public class DBHandler:INotifyPropertyChanged
{
#region Singleton Pattern
private static DBHandler instance;
private DBHandler()
{
}
public static DBHandler GetInstance()
{
if (instance == null)
instance = new DBHandler();
return instance;
}
#endregion
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
// Represents Sample Data of the database
private List<string> names = new List<string>() { "Sample1", "Sample2" };
public List<string> Names
{
get { return names; }
}
/// <summary>
/// Saves a new name in the database
/// </summary>
/// <param name="name"></param>
public void AddNewName(string name)
{
names.Add(name);
NotifyPropertyChanged();
}
}
The MainWindowViewModels can save a new name via the DBHandler, and listens for changes of List DBHandler.Names by using
public class MainWindowViewModel
{
#region Constructors
public MainWindowViewModel()
{
// Initialize the command for the add button click
addName = new AddNameCommand();
// Assign database collection entries
names = new ObservableCollection<string>(DBHandler.GetInstance().Names);
DBHandler.GetInstance().PropertyChanged += MainWindowViewModel_PropertyChanged_Names;
}
/// <summary>
/// Listen for the DBHandler.Names change for updating the datagrid view.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MainWindowViewModel_PropertyChanged_Names(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == "Names")
{
// Try to update the datagrid view
// First Try: Reassign
names = new ObservableCollection<string>(DBHandler.GetInstance().Names);
}
}
#endregion
private ObservableCollection<string> names;
public ObservableCollection<string> Names
{
get { return names; }
set { names = value; }
}
#region Commands
/// <summary>
/// Command for adding the textbox content as new name to the database
/// </summary>
public class AddNameCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
Debug.WriteLine("CanExecute");
return ((string)parameter) != "" || parameter != null;
}
public void Execute(object parameter)
{
// Save the name in the database
DBHandler.GetInstance().AddNewName((string)parameter);
}
}
AddNameCommand addName; // Instance of the command which will be intialized in the constructor
public ICommand btnClickAdd
{
get {
Debug.WriteLine("btnClickAdd");
return (ICommand) addName; }
}
#endregion
}
Last the view contains a TextBox for the name which will be saved by a button click and a DataGrid for displaying all names in the database. So the DataGrid is bounded to the ObservableCollection of Names in the viewmodel.
<Window.Resources>
<local:MainWindowViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid>
<DataGrid x:Name="dataGrid" ItemsSource="{Binding Source={StaticResource ViewModel}, Path=Names}" HorizontalAlignment="Left" Margin="48,142,0,0" VerticalAlignment="Top" Height="127" Width="422"/>
<Button x:Name="button_AddName" Command="{Binding Source={StaticResource ViewModel}, Path=btnClickAdd}" Content="Add" HorizontalAlignment="Left" Margin="331,61,0,0" VerticalAlignment="Top" Width="75" CommandParameter="{Binding Text, ElementName=textBox_Name}"/>
<TextBox x:Name="textBox_Name" HorizontalAlignment="Left" Height="23" Margin="160,58,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
</Grid>
Delete most of DBHandler, it's just confusing you. All you want to do is retrieve DB stuff when requested, and save it when told. It sits there waiting for orders from MainWindowViewModel. The main viewmodel is always in charge. It uses the model (that's DBHandler) to store and retrieve information, which it exposes in its properties. It also exposes commands. The view is how the user observes the the viewmodel and talks to it. The viewmodel doesn't know the view exists. All it knows is that somebody out in the darkness occasionally calls the getters and setters on its properties, or calls Execute on one of its commands.
Give MainWindowViewModel a public Names property that's an ObservableCollection<String>. Bind that to the DataGrid or whatever in the UI. Never use List<T> for anything in the viewmodel if there is the slightest chance that you'll be adding or removing items in it.
Write a new Command class called DelegateCommand, something like this:
public class DelegateCommand<T> : ICommand
{
public DelegateCommand(Action<T> action, Func<T, bool> canExecute = null)
{
_action = action;
_canExecute = canExecute;
}
private Action<T> _action;
private Func<T, bool> _canExecute;
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
{
return _canExecute((T)parameter);
}
return true;
}
public void Execute(object parameter)
{
if (_action != null)
{
_action((T)parameter);
}
}
}
Use it:
public MainWindowViewModel()
{
// Initialize the command to add a name
_addNameCommand = new DelegateCommand<string>(DoAddName);
// Assign database collection entries
Names = new ObservableCollection<string>(DBHandler.GetInstance().Names);
}
public void DoAddName(String name)
{
Names.Add(name);
/*
Update database here
*/
}
ICommand _addName;
// Don't name anything "button" in your viewmodel; it's a bad habit to think
// that way. It's just a command. If the view wants to use a button to invoke
// it, that's the view's business. The viewmodel just makes stuff available.
public ICommand AddNameCommand
{
get {
Debug.WriteLine("getting AddNameCommand");
return _addNameCommand;
}
}
// Never, never, NEVER NEVER NEVER NEVER touch _names other than in the
// get and set blocks of Names.
// And make the set private. It should be kept in sync with the database, so
// don't let any other class but this one mess with it.
private ObservableCollection<string> _names = new ObservableCollection<string>();
public ObservableCollection<string> Names
{
get { return _names; }
private set {
if (_names != value)
{
_names = value;
NotifyPropertyChanged();
}
}
}
I don't know what you're doing to bind Names to the DataGrid, but I infer that it's working OK.
use ICollectionView - bind your Datagrid to it - and call .Refresh() in your Viewmodel whenever you wanna refresh your Datagrid
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"/>
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.