I'm trying to pass a command parameter with my command. I have commands in general working but passing a parameter doesn't seem to be going to well for me.
I'm trying to pass the UserName Property from the Hierarchical Data in my XAML. What am I doing wrong here.
I recieve and error trying to compile with the commands statement:
cannot convert from 'lambda expression' to 'System.Action'
<HierarchicalDataTemplate
DataType="{x:Type viewModel:UsersViewModel}"
ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding UserName}">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Edit" Command="{Binding EditCommand}" CommandParameter="{Binding UserName}"/>
<MenuItem Header="Delete"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
private RelayCommand _editCommand;
public ICommand EditCommand
{
get
{
if (_editCommand== null)
{
_editCommand= new RelayCommand(param => this.LoadUser(object parameter));
}
return _editCommand;
}
}
public void LoadUser(object username)
{
}
RelayCommand Class
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
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
}
Thanks for the help!
new RelayCommand(param => this.LoadUser(object parameter));
Shouldn't this be:
new RelayCommand(param => this.LoadUser(param));
You should not invoke the method, you should pass it as a parameter. Just replace new RelayCommand(param => this.LoadUser(object parameter)); for new RelayCommand(this.LoadUser);
Similar question here:
RelayCommand lambda syntax problem
Related
I am trying to bind an on click action, to a button i have in my view. As i'm trying to followe the MVVM guidelines, i want my ButtonAddBook method, in the ViewModel, but with my button like this:
<Button x:Name="SurnamaeBox" Content="Add book" HorizontalAlignment="Left" Margin="646,42,0,0" VerticalAlignment="Top" Padding="2" Click="ButtonAddBook"/>
Nothing really happens when i click the button. I tried also Click="{Binding ButtonAddBook}" and that just makes the program crash with exception:
InvalidCastException: Unable to cast object of type 'System.Reflection.RuntimeEventInfo' to type 'System.Reflection.MethodInfo'.
So what is the workaround, for having button methods in ViewModel, instead of View?
Add a RelayCommand class to your viewmodel which inherites from ICommand like
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");
_execute = execute;
_canExecute = canExecute;
}
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);
}
}
Then also add
private ICommand _buttonAddBook;
public ICommand ButtonAddBook
{
get
{
if (_buttonAddBook == null)
_buttonAddBook = new RelayCommand(param => DoStuff(), param => CanDoStuff());
return _buttonAddBook;
}
}
Where DoStuff() will be the method to be executed by calling the Command="{Binding ButtonAddBook}" not Click="{Binding ButtonAddBook}" and in CanDoStuff() you can set the validation for when you can run the DoStuff().
I`m trying to do a simple WPF application using MVVM pattern. I wrote a class implementing ICommand interface:
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
then I use it, when I click on the button in the view, to show a new page, by assigning a page to the current page
public ICommand bFirst_Click
{
get
{
return new RelayCommand(o => CurrentPage = first);
}
}
XAML code in view
<StackPanel>
<Button Command="{Binding bFirst_Click}" Content="First"/>
</StackPanel>
<Frame
Grid.Column="1"
Content="{Binding CurrentPage}"
NavigationUIVisibility="Hidden"
Opacity="{Binding FrameOpacity}"
/>
But nothing happens. Please help me, did I miss something, or doing it in the wrong way?
I am brand new to basically all of this and am trying to learn C# in the context of MVVM. This will be a simple CRUD program and right now I am stuck on the deletion of a ComboBox's SelectedItem from the corresponding collection.
Relevant ViewModel code:
public class AlbumViewModel
{
private ObservableCollection<AlbumModel> albums;
public AlbumViewModel()
{
this.albums = new ObservableCollection<AlbumModel>();
LoadAlbums();
}
public ObservableCollection<AlbumModel> Albums
{
get { return this.albums; }
}
public void LoadAlbums()
{
albums.Add(new AlbumModel("No Love/Deep Web", "Death Grips"));
albums.Add(new AlbumModel("In Defense of the Genre", "Say Anything"));
albums.Add(new AlbumModel("Picaresque", "The Decemberists"));
albums.Add(new AlbumModel("In Evening Air", "Future Islands"));
albums.Add(new AlbumModel("You're Gonna Miss It All", "Modern Baseball"));
}
#region RelayCommand
private RelayCommand _deleteCommand;
public ICommand DeleteCommand
{
get
{
if (_deleteCommand == null)
{
_deleteCommand = new RelayCommand(param => DeleteItem());
}
return _deleteCommand;
}
}
#endregion
#region DeleteItem()
private AlbumModel SelectedItem { get; set; }
private void DeleteItem()
{
if (SelectedItem != null)
{
this.albums.Remove(SelectedItem);
this.SelectedItem = null;
}
}
#endregion
}
Relevant Model code:
public class AlbumModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
// AlbumModel members, properties, constructor
}
#region RelayCommand
public class RelayCommand : ICommand
{
// fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
// ctors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
// 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);
}
Relevant XAML:
<Window x:Class="AlbumsCRUD2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AlbumsCRUD2.ViewModels"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:AlbumViewModel />
</Window.DataContext>
<Window.Resources>
<local:AlbumViewModel x:Key="albums" />
</Window.Resources>
<Grid>
<GroupBox Grid.Row="1" Grid.Column="1" HorizontalContentAlignment="Center" Header="View Existing">
<StackPanel>
<Label Content="Album" />
<ComboBox Name="albumComboBox"
ItemsSource="{Binding Path=Albums}"
DisplayMemberPath="AlbumName"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Label Content="Artist" />
<TextBox Text="{Binding ElementName=albumComboBox, Path=SelectedItem.ArtistName}"
IsEnabled="False" />
<Button Name="deleteBtn" Width="100" Margin="30"
Command="{Binding DeleteItem}"
Content="Delete" />
</StackPanel>
</GroupBox>
</Grid>
</Window>
And, of course, the errors in Output:
System.Windows.Data Error: 40 : BindingExpression path error: 'SelectedItem' property not found on 'object' ''AlbumViewModel' (HashCode=12507741)'. BindingExpression:Path=SelectedItem; DataItem='AlbumViewModel' (HashCode=12507741); target element is 'ComboBox' (Name='albumComboBox'); target property is 'SelectedItem' (type 'Object')
System.Windows.Data Error: 40 : BindingExpression path error: 'DeleteItem' property not found on 'object' ''AlbumViewModel' (HashCode=12507741)'. BindingExpression:Path=DeleteItem; DataItem='AlbumViewModel' (HashCode=12507741); target element is 'Button' (Name='deleteBtn'); target property is 'Command' (type 'ICommand')
I suspect it's an error with my data bindings, but I'm having a hard time deciphering the meaning of the errors. I would appreciate any perspective on what is going wrong!
SelectedItem should be public. Bindings need this.
You try to bind to the method (DeleteItem), not the command (DeleteCommand).
I want to add a method to my custom control which I can call from a button using command binding, in my MainWindow.xaml. I've come across a few solutions online, however one of them didn't appear to work and the other did. Can someone explain to me the correct way to set this up. The first solution produces and error as mentioned below. The second solution works but I'm not sure of any pros/cons.
Solution 1 - broken
public partial class MyControl : Control
{
...
public static readonly RoutedCommand AlignLeftCommand = null;
static MyControl()
{
binding = new CommandBinding();
binding.Command = AlignLeftCommand;
binding.Executed += new ExecutedRoutedEventHandler(AlignLeft_Executed);
CommandManager.RegisterClassCommandBinding(typeof(MyControl), binding);
}
}
Error:
Severity Code Description Project File Line
Error CS0120 An object reference is required for the non-static field, method, or property...
Solution 2
public partial class MyControl : Control
{
...
public static readonly RoutedCommand AlignLeftCommand = new RoutedCommand();
public MyControl()
{
this.CommandBindings.Add(new CommandBinding(MyControl.AlignLeftCommand, AlignLeft_Executed, null));
}
}
Here is the button calling the method.
<StackPanel Orientation="Horizontal">
<Button Content="Left Edges" FontSize="8"
Command="{x:Static JM:MyControl.AlignLeftCommand}"
CommandTarget="{Binding ElementName=mycontrol}"/>
</StackPanel>
At first, you should define a command binding on the Window like that(create handlers for Executed and CanExecuteevents):
<Window x:Class="CommandBindingWPF.MainWindow"
...The code omitted for the brevity...
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed" CanExecute="CommandBinding_CanExecute" />
</Window.CommandBindings>
and declare your Button ix xaml:
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Command="ApplicationCommands.New">New</Button>
</StackPanel>
Handlers should be created in code-behind after you command binding created:
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Hello from Command");
}
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{ }
Update:
For MVVM application:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_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
}
Then create a property in your viewModel. For instance:
public class YourViewModel
{
public RelayCommand YourCommand { get; set; }
public YourViewModel()
{
YourCommand = new RelayCommand(DoSmth, CanDoSmth);
}
private void DoSmth(object obj)
{
Message.Box("Hello from viewModel");
}
private bool CanDoSmth(object obj)
{
//you could implement your logic here. But by default it should be
//set to true
return true;
}
}
And XAML should be look like:
<Button Content="Click me!" Command="{Binding YourCommand}"/>
To get acquainted with MVVM, I recommend you to read Rachel Lim's blog. She has a talent to teach people and she can explain by simple terms. Read Rachel Lim's blog.
To get acquainted with MVVM commands see that post
Hi i want to bind button with other listView.Item. What i want is to have something like we have on stackoverflow.
But i have problem with having increasing/decreasing value. I have event Click but i dont knew how to get corresponding item on list and increase/decrease value.
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label Width="706" Height="75" Content="{Binding feedback}"/>
<StackPanel Orientation="Vertical">
<Button Name="buttonUp" Content="^" Command="{Binding upVoteCommand}" />
<Label HorizontalContentAlignment="Center" Width="50" Content="{Binding grade}"/>
<Button Name="buttonDown" Content="v" Command="{Binding upVoteCommand}"/>
</StackPanel>
</StackPanel>
<Label>-</Label>
</StackPanel >
EDIT
class A {
public string feedback {
get;
set;
}
public int grade {
get;
set;
}
private ICommand _upVoteCommand;
private ICommand _downVoteCommand;
public ICommand upVoteCommand {
get {
return _upVoteCommand;
}
set {
_upVoteCommand = value;
}
}
public ICommand downVoteCommand {
get {
return _downVoteCommand;
}
set {
_downVoteCommand = value;
}
}
}
EDIT I used this button.Commmand but still it not working. I dont knew what to do with this commands.
First you'll need your implementation of ICommand so you can bind commands from view model to controls, something like this:
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");
_execute = execute;
_canExecute = canExecute;
}
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); }
}
then in you class, where you publish Feedback, you'll need to publish 2 new RelayCommand for up/down vote that will modify Feedback property accordingly. Below you can find my class that I used for tests:
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private int _feedback = 0;
public int Feedback
{
get { return _feedback; }
set
{
if (_feedback == value) return;
_feedback = value;
OnPropertyChanged("Feedback");
}
}
private RelayCommand _upVoteCmd;
public ICommand UpVoteCmd
{
get
{
if (_upVoteCmd == null) _upVoteCmd = new RelayCommand(o => Feedback += 1);
return _upVoteCmd;
}
}
private RelayCommand _downVoteCmd;
public ICommand DownVoteCmd
{
get
{
if (_downVoteCmd == null) _downVoteCmd = new RelayCommand(o => Feedback -= 1);
return _downVoteCmd;
}
}
}
and then you bind your new commands in XAML like this:
<Button Content="+" Command="{Binding Path=UpVoteCmd}"/>
<TextBlock Text="{Binding Path=Feedback}"/>
<Button Content="-" Command="{Binding Path=DownVoteCmd}"/>
RoutedEvents don't work so easily with DataTemplates, because you don't have a code behind where your event code could be placed. While there are ways to do that, you can just use Commands to do the same. In the view model for each item (i just assume you use MVVM) create properties called UpVoteCommand and DownVoteCommand of type ICommand, DelegateCommands are quiet handy for this. Bind them to the Command property and remove the Click handler in your DataTemplate.
[EDIT]
Small example of a possible Viewmodel for one entry in the list, which can be up or downvoted.
class MyEntryViewModel : INotifyPropertyChanged
{
public MyEntryViewModel()
{
UpVoteCommand = new DelegateCommand(OnUpVoteCommand);
}
public int Votes
{
get {return mVotes;}
set {mVotes = value; RaiseProperty("Votes");}
}
public ICommand UpVoteCommand
{
get; private set;
}
void OnUpVoteCommand(object aParameter)
{
Votes++;
}
}
i left the implementation of INotifyPropertyChanged and the down vote command for sake of simplicity.