Pass parameter with a command into the viewmodel - c#

I have trouble sending a parameter from my view to my viewmodel.
View.xaml:
In my view, I have the following:
<TextBox
MinWidth="70"
Name="InputId"/>
<Button
Command="{Binding ButtonCommand}"
CommandParameter="{Binding ElementName=InputId}"
Content="Add"/>
View.xaml.cs:
public MyView()
{
InitializeComponent();
}
public MyView(MyViewModel viewModel) : this()
{
DataContext = viewModel;
}
MyViewModel.cs:
public class MyViewModel : BindableBase
{
public ICommand ButtonCommand { get; private set; }
public MyViewModel()
{
ButtonCommand = new DelegateCommand(ButtonClick);
}
private void ButtonClick()
{
//Read 'InputId' somehow.
//But DelegateCommand does not allow the method to contain parameters.
}
}
Any suggestions to, how I can pass the InputId when I click the button to my viewmodel?

You need to add <object> to your delegate command like this :
public ICommand ButtonCommand { get; private set; }
public MyViewModel()
{
ButtonCommand = new DelegateCommand<object>(ButtonClick);
}
private void ButtonClick(object yourParameter)
{
//Read 'InputId' somehow.
//But DelegateCommand does not allow the method to contain parameters.
}
Do you want to get the text of textbox change your xaml to :
CommandParameter="{Binding Text,ElementName=InputId}"

For proper implementation of ICommand/RelayCommand have a look at this MSDN page.
Summary:
public class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Func<bool> _canExecute;
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute.Invoke();
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
public MyViewModel()
{
ButtonCommand = new RelayCommand(ButtonClick);
}
private void ButtonClick(object obj)
{
//obj is the object you send as parameter.
}

Related

WPF MVVM. Left DoubleClick on ListViewItem

What I want: If I do a Left DoubleClick on a ListViewItem I want to invoke a Method.
What I have:
Xaml:
<ListView ScrollViewer.HorizontalScrollBarVisibility="Hidden"
Name="StructListView"
Margin="15"
Grid.Row="2"
BorderThickness="0"
SelectedItem="{Binding SelectedStructObject}"
ItemsSource="{Binding StructObjects, Mode=TwoWay}"
HorizontalAlignment="Left"
HorizontalContentAlignment="Left">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction
Command="{Binding Command}"
CommandParameter="{Binding ElementName=StructListView, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
...
MainViewModel:
public class MainViewModel : BaseViewModel
{
private StructElement _selectedStructObject;
public StructElement SelectedStructObject
{
get { return _selectedStructObject; }
set
{
if (_selectedStructObject != value)
{
_selectedStructObject = value;
OnPropertyChanged();
}
}
}
public RelayCommand Command { get; set; }
public ObservableCollection<StructElement> StructObjects { get; set; }
private StructElement _structElement;
public StructElement StructObject
{
get { return _structElement; }
set
{
if (_structElement != value)
{
_structElement = value;
OnPropertyChanged();
}
}
}
public MainViewModel()
{
Command = new RelayCommand(o => { //Method; });
StructObjects = new ObservableCollection<StructElement>();
}
}
RelayCommand:
public class RelayCommand : ICommand
{
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
if (canExecute == null)
{
return true;
}
return canExecute(parameter);
}
public void Execute(object parameter)
{
execute(parameter);
}
}
I know that if I work with a Event I could use something like:
void OnMouseDoubleClick(Object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
// Method
}
}
But since I am working with a RelayCommand, I dont have a Event like MouseButtonEventArgs?
I thought about "triggering" the OnMouseDoubleClick with the RelayCommand?
I am quite knew to Programming and WPF, so if you have further Information on Things I should read into to understand the solution, I would be thankful for everything.
Thanks.
I came across a different Solution from the Link.
In my XAML I use "EventToCommand" from MVVMLight:
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<cmd:EventToCommand Command="{Binding LightCommand}"
PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
I made my RelayCommand generic so I can pass an Event (I hope I got the Description right):
public class RelayCommand : ICommand
{
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
if (canExecute == null)
{
return true;
}
return canExecute(parameter);
}
public void Execute(object parameter)
{
execute(parameter);
}
}
In my MainViewModel I declare a RelayCommand with a MouseButtonEventArgs and Pass a Method that checks If the MouseButton was a Leftclick and does something:
public RelayCommand<MouseButtonEventArgs> LightCommand { get; set; }
public void dosomething(MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
MessageBox.Show(e.OriginalSource.GetType().ToString());
}
}
public MainViewModel()
{
LightCommand = new RelayCommand<MouseButtonEventArgs>(dosomething);
}

How can I send both a string and an int to an ICommand?

I have a ViewModel command that looks like this:
// Category Page
private ICommand openPageCmd;
public ICommand OpenPageCmd =>
openPageCmd ??
(openPageCmd = new Command<string>((pageType) => settingsPage.OpenPage(pageType)));
But instead of sending a string I would like to send an ID which is an int and a Name which is a string. Can someone tell me how I can do this?
You can create your own command by implementing ICommand. Create a command having a constructor with the required parameters.
See:
ICommand Interface (System.Windows.Input).
ICommand Interface in WPF (Code Project).
public class MyCommand : ICommand
{
private int _id;
private string _name;
public MyCommand(int id, string name)
{
_id = id;
_name = name;
}
public bool CanExecute(object parameter) => true;
public void Execute(object parameter)
{
//TODO: Add your implementation
}
public event EventHandler CanExecuteChanged;
}
Or better, create your own relay command (generic reusable command):
public class RelayCommand<T> : ICommand
{
private readonly T _argument;
private readonly Action<T> _execute;
public RelayCommand(T argument, Action<T> execute)
{
_execute = execute;
_argument = argument;
}
public bool CanExecute(object parameter) => true;
public void Execute(object parameter)
{
_execute(_argument);
}
public event EventHandler CanExecuteChanged;
}
The view model
public class MyViewModel
{
private SettingsPage settingsPage = new ...;
public int Id { get; set; }
public string Name { get; set; }
public MyViewModel()
{
OpenPageCmd =
new RelayCommand<MyViewModel>(this, vm => settingsPage.OpenPage(vm.Id, vm.Name));
}
public ICommand OpenPageCmd { get; }
}
This allows the command to use the current values of the view model at any time.

WPF - MVVM Bindings

I have problem with my application, especially with Bindings in WPF MVVM.
I created Model, ViewModel and View, this is part of my code (only this connected with my problem) When I click the button nemed : PointUp i want to see the amount of Team1 points. Can anyone tell me what i'm doing wrong?
View
<Window x:Class="Tabu.Game
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:Tabu"
xmlns:vm="clr-namespace:Tabu.ViewModel"
mc:Ignorable="d"
Title="Game" Height="600" Width="900" Background="Beige">
<Window.DataContext>
<vm:TeamStatistic />
</Window.DataContext>
<Grid>
<Button x:Name="PointUp" Command="{Binding AddPoints }" Content="+"/>
<Label x:Name="PointsTeam1_label" Content="{Binding Team1.TeamPoints, UpdateSourceTrigger=PropertyChanged }"/>
</Grid>
Model
'namespace Tabu.Model
{
public class Team
{
public bool IsTeamActive { get; set; }
public int TeamMiss { get; set; }
public int TeamPoints { get; set; }
public int TeamMistake { get; set; }
}
}'
ViewModel
namespace Tabu.ViewModel
{
class TeamStatistic : INotifyPropertyChanged
{
public Team Team1 = new Team();
public int TeamPoints
{
get { return TeamPoints; }
set { TeamPoints = value; OnPropertyChanged("TeamPoints"); }
}
public ICommand AddPoints
{
get { return new RelayCommand(() => Add_Points()); }
}
public void Add_Points()
{
Team1.TeamPoints++;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(params string[] propsName)
{
if (PropertyChanged!=null)
{
foreach(string propName in propsName)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
public class RelayCommand : ICommand
{
private readonly Func<bool> canExecute;
private readonly Action execute;
public RelayCommand(Action execute)
: this(execute, null) { }
public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null) throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { if (this.canExecute != null) CommandManager.RequerySuggested += value; }
remove { if (this.canExecute != null) CommandManager.RequerySuggested -= value; }
}
public Boolean CanExecute(object parameter) { return this.canExecute == null ? true : this.canExecute(); }
public void Execute(object parameter) { this.execute(); }
}
}
The problem is here:
public int TeamPoints
{
get { return TeamPoints; } //should be Team1.TeamPoints
set { TeamPoints = value; OnPropertyChanged("TeamPoints"); } //should be Team1.TeamPoints
}
Inside your TeamPoints property in ViewModel you return and set the same property TeamPoints from ViewModel but you should set from Model (Team1). You should return and set Team1.TeamPoints.
public int TeamPoints
{
get { return Team1.TeamPoints; }
set { Team1.TeamPoints = value; OnPropertyChanged("TeamPoints"); }
}
And Add_Points():
public void Add_Points()
{
Team1.TeamPoints++;
OnPropertyChanged("TeamPoints");
}
You have to update your Binding like this.
<Label x:Name="PointsTeam1_label" Content="{Binding TeamPoints, UpdateSourceTrigger=PropertyChanged }"/>
When you bind to Team1.TeamPoints you will not get the Notification from OnPropertyChanged which is inside your TeamPoints property.
I reckon it is because of AddPoints (the command binding). Since this command is binded & you are creating an instance of RelayCommand & returning each time it might be breaking the binding.
A better alternative with CommandBindings are to declare the property and initialize them in the Constructor of the view model.
Ex:
namespace Tabu.ViewModel
{
class TeamStatistic : INotifyPropertyChanged
{
public Team Team1 = new Team();
public int TeamPoints
{
get { return Team1.TeamPoints; }
set { Team1.TeamPoints = value; OnPropertyChanged("TeamPoints"); }
}
private ICommand _AddPoints;
public ICommand AddPoints
{
get { return _AddPoints; }
set { _AddPoints = value; }
}
public void Add_Points()
{
Team1.TeamPoints++;
}
public TeamStatistic ()
{
_AddPoinss = new RelayCommand(Add_Points);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(params string[] propsName)
{
if (PropertyChanged!=null)
{
foreach(string propName in propsName)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}

Unsure what I'm doing wrong when trying to create / add this command:

XAML Button:
<Button Content="Test Connection" Name="btnTestConnection" Command="{Binding Path=TestCommand}" CommandParameter="{Binding ElementName=someObject}"/>
View Model:
public ICommand TestCommand
{
get;
internal set;
}
private bool CanExecuteTestCommand()
{
return !String.IsNullOrEmpty(txtControl);
}
private void CreateTestCommand()
{
TestCommand = new TestCommand(TestExecute);
}
public void TestExecute(object parameter)
{
//do stuff with parameter
obj.TestConnection(parameter);
}
I would like to point out that CreateTestCommand() is called in my VM constructor.
And finally, my implementation of TestCommand:
class TestCommand : ICommand
{
private Action<object> execute;
private Predicate<object> canExecute;
private event EventHandler CanExecuteChangedInternal;
public TestCommand(Action<object> execute)
: this(execute, DefaultCanExecute)
{
}
public TestCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
if (canExecute == null)
{
throw new ArgumentNullException("canExecute");
}
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
this.CanExecuteChangedInternal += value;
}
remove
{
CommandManager.RequerySuggested -= value;
this.CanExecuteChangedInternal -= value;
}
}
public bool CanExecute(object parameter)
{
return this.canExecute != null && this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
public void OnCanExecuteChanged()
{
EventHandler handler = this.CanExecuteChangedInternal;
if (handler != null)
{
handler.Invoke(this, EventArgs.Empty);
}
}
public void Destroy()
{
this.canExecute = _ => false;
this.execute = _ => { return; };
}
private static bool DefaultCanExecute(object parameter)
{
return true;
}
}
I set a breakpoint in CreateTestCommand and it looks like it's configured properly:
But when I click on btnTestConnection, nothing happens. TestExecute in my View Model isn't called (which calls TestConnection on the actual model). I must be missing something, but I can't for the life of me figure out what...
EDIT Including the rest of my view model;
class FormProcessorViewModel
{
FormProcessorModel obj;
public FormProcessorViewModel()
{
obj = new FormProcessorModel();
CreateTestCommand();
}
public FormProcessorViewModel(string server, string database, string username, bool specifyDateRange, DateTime startDate, DateTime endDate, string operation, string preprocessed, string processed, string failed) :this()
{
txtServer = server;
txtDatabase = database;
txtUsername = username;
chkSpecifyDateRange = specifyDateRange;
dpStartDate = startDate;
dpEndDate = endDate;
txtOperation = operation;
txtPreprocessed = preprocessed;
txtProcessed = processed;
txtFailed = failed;
}
public ICommand TestCmd
{
get;
internal set;
}
private bool CanExecuteTestCommand()
{
return !String.IsNullOrEmpty(txtUsername);
}
private void CreateTestCommand()
{
TestCmd = new TestCommand(TestExecute);
}
private void TestExecute(object parameter)
{
var passwordBox = parameter as PasswordBox;
var password = passwordBox.Password;
obj.TestConnection(password);
}
}
I left out all the properties that get set in the second constructor just because they don't really do anything but refer to the corresponding values on the model object.
EDIT
View Model
class ViewModel : INotifyPropertyChanged
{
private string _test;
public string TestValue
{
get { return _test; }
set { _test = value; RaisePropertyChanged("TestValue"); }
}
public ICommand MyCommand { get; internal set; }
public ViewModel()
{
TestValue = "Test";
CreateTestCommand();
}
private void CreateTestCommand()
{
MyCommand = new TestCommand(ExecuteButton);
}
private void ExecuteButton(object obj)
{
TestValue = "Cool";
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propName)
{
var pc = PropertyChanged;
if (pc != null)
{
pc(this, new PropertyChangedEventArgs(propName));
}
}
}
Xaml
<Button Content="{Binding TestValue}" Command="{Binding Path=MyCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Self}}"/>
Xaml Code behind.
public MainWindow()
{
InitializeComponent();
this.DataContext = new ViewModel();
}

How to use the CanExecute Method from ICommand on WPF

How does one use the CanExecute Method from the ICommand interface?
In my example i have a SaveCommand which i only what to be enable when the object is saveable. The XAML Code of my Savebutton looks like this:
<Button Content="Save" Command="{Binding SaveCommand, Mode=TwoWay}" />
This is the code of my save class:
class Save : ICommand
{
public MainWindowViewModel viewModel { get; set; }
public Save(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
if (viewModel.IsSaveable == false)
return false;
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
viewModel.Save();
}
}
The save property in the ViewModel looks like this:
public ICommand SaveCommand
{
get
{
saveCommand = new Save(this);
return saveCommand;
}
set
{
saveCommand = value;
}
}
This construct didn't work. The button does not enable its self when isSaveable is true.
Instead of defining your own implementation of ICommand, use a RelayCommand.
In the below sample code, the save Button is enabled when the user types something in the TextBox.
XAML:
<Window x:Class="RelayCommandDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel HorizontalAlignment="Center">
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Margin="5" Width="120"/>
<Button Content="Save" Command="{Binding SaveCommand}" Margin="3"/>
</StackPanel>
</Window>
Code behind:
using System;
using System.Windows;
using System.Windows.Input;
namespace RelayCommandDemo
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new VM();
}
}
public class VM
{
public String Name { get; set; }
private ICommand _SaveCommand;
public ICommand SaveCommand
{
get { return _SaveCommand; }
}
public VM()
{
_SaveCommand = new RelayCommand(SaveCommand_Execute, SaveCommand_CanExecute);
}
public void SaveCommand_Execute()
{
MessageBox.Show("Save Called");
}
public bool SaveCommand_CanExecute()
{
if (string.IsNullOrEmpty(Name))
return false;
else
return true;
}
}
public class RelayCommand : ICommand
{
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private Action methodToExecute;
private Func<bool> canExecuteEvaluator;
public RelayCommand(Action methodToExecute, Func<bool> canExecuteEvaluator)
{
this.methodToExecute = methodToExecute;
this.canExecuteEvaluator = canExecuteEvaluator;
}
public RelayCommand(Action methodToExecute)
: this(methodToExecute, null)
{
}
public bool CanExecute(object parameter)
{
if (this.canExecuteEvaluator == null)
{
return true;
}
else
{
bool result = this.canExecuteEvaluator.Invoke();
return result;
}
}
public void Execute(object parameter)
{
this.methodToExecute.Invoke();
}
}
}

Categories