I have a simple user control that I'm building (something akin to the illustrations below) which is essentially a user control containing a number of buttons.
The main control itself does not have a command or command parameter property, but the four buttons inside it do and I wish to be able to access those from the view model of whatever views I happen to place this control on.
Put simply what is the best way to do this. I simply want to know which button was clicked. Each button is named (so that would I presume take care of the identification side).
Thanks for any suggestions you might have.
If I understood you correctly, you want to get a method called within your viewmodel when one of those 4 buttons is clicked.
A simplified view on the general relationship between viewmodel and view could help. From a view perspective the viewmodel is typically accessed via the datacontext property, i.e. the datacontext propery contains the viewmodel object.
Therefore, set the datacontext in xaml or code behind of your main user control to the viewmodel object that you want to use and bind from the buttons command propeties to the appropriate ICommand properties on the viewmodel that you set before.
namespace UserControlTest
{
/// <summary>
/// Interaktionslogik für MainUserControl.xaml
/// </summary>
public partial class MainUserControl : UserControl
{
public MainUserControl()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ViewModel()
{
Command = new RelayCommand((obj) => Debug.Print("done"));
}
private ICommand _command;
public ICommand Command
{
get
{
return _command;
}
set
{
_command = value;
RaisePropertyChanged("Command");
}
}
public void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
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
}
}
<UserControl x:Class="UserControlTest.MainUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<Button Height="50" Command="{Binding Command}"></Button>
</StackPanel>
</Grid>
</UserControl>
Related
I am trying to attach a command and a commandparameter to a textbox on return key but without success. The parameter is the current text in the same textbox.
<TextBox x:Name="txtSearch">
<TextBox.InputBindings>
<KeyBinding Command="{Binding SearchCommand}"
CommandParameter="{Binding Path=Text, ElementName=txtSearch}" Key="Return" />
</TextBox.InputBindings>
</TextBox>
Basically I want to execute the command when user clicks on return/enter key and pass as a parameter the current text in the textbox.
I have found this link where it is said that in .NET 3.5 command parameter for keybinding is not accepting bindings. So a solution is proposed by code in code-behind but how can I pass a parameter to the command from the code?
First, you'll need to add the KeyBinding to your TextBox and set its Command on code-behind. Just add this in the constructor of your View:
public MainWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
KeyBinding kb = new KeyBinding();
kb.Command = (DataContext as MyViewModel).SearchCommand;
kb.Key = Key.Enter;
txtSearch.InputBindings.Add(kb);
}
Then, you can bind the Text property of the TextBox named txtSearch to a property of your ViewModel. This way you don't need to pass a parameter as you can use the value of that property in your ViewModel inside the code that executes your Command.
Your ViewModel should look like this:
public class MyViewModel : ObservableObject
{
private string _txtSearch;
public string TxtSearch
{
get { return _txtSearch; }
set
{
if (value != _txtSearch)
{
_txtSearch = value;
OnPropertyChanged("TxtSearch");
}
}
}
private ICommand _searchCommand;
public ICommand SearchCommand
{
get
{
if (_searchCommand == null)
{
_searchCommand = new RelayCommand(p => canSearch(), p => search());
}
return _searchCommand;
}
}
private bool canSearch()
{
//implement canExecute logic.
}
private void search()
{
string text = TxtSearch; //here you'll have the string that represents the text of the TextBox txtSearch
//DoSomething
}
}
If you have access to C# 6 (Visual Studio 2015 and later versions), you can alter the call to the OnPropertyChanged to: OnPropertyChanged(nameof(TxtSearch));. This way you get rid of the "magic string" and eventual renaming of the property won't cause any problem for you.
And then your XAML should look like this: (Notice that you need to specify that te UpdateSourceTrigger must be PropertyChanged, so that your TxtSearch property of your ViewModel stays up to date when you hit the Enter key on your TextBox.
<TextBox Text="{Binding TxtSearch, UpdateSourceTrigger=PropertyChanged}" x:Name="txtSearch"/>
Your ViewModel needs to implement INotifyPropertyChanged and you need a proper ICommand implementation. Here I'll use the RelayCommand.
Those implementations are shown below.
Since your framework is .NET 3.5, implement it like this:
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
This is a implementation of the RelayCommand:
public class RelayCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
I have tried two different methods of commanding in WPF from the ViewModel and come up short. The top method works great if I put it into the View however I was told this is bad practice. The second method I was told is the proper way to do custom commanding in MVVM however I get stuck on how to actually call/bind the command from the View.
View Model:
class MainViewModel : INotifyPropertyChanged
{
#region INPC
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
public readonly static RoutedUICommand myCommand;
static MainViewModel()
{
myCommand = new RoutedUICommand("customCommand","My Command",typeof(MainViewModel));
}
private void ExecutemyCommand(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("textBox1 cleared");
}
private void myCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
}
On my View the code I have this which gives me an error
<Window x:Class="ConfigManager2.View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:con="clr-namespace:ConfigManager2.Converters"
xmlns:vm="clr-namespace:ConfigManager2.ViewModel"
xmlns:local="clr-namespace:ConfigManager2.View"
.
.
.
<Window.CommandBindings>
<CommandBinding
Command="{x:Static vm:MainViewModel.myCommand}"
CanExecute="myCommandCanExecute"
Executed="ExecutemyCommand" />
</Window.CommandBindings>
.
.
.
<Button Content="COMMAND ME" Height="50px" Command="{x:Static vm:MainViewModel.myCommand}" />
The Error I am getting is 'ConfigManager2.View.MainView' does not contain a definition for 'ExecutemyCommand' and no extension method 'ExecutemyCommand' accepting a first argument of type 'ConfigManager2.View.MainView' could be found (are you missing a using directive or an assembly reference?)
I tried another method using ICommand and got stumped on how to bind this to the above button from the XAML
ViewModel:
public ICommand ClearCommand { get; private set; }
public MainViewModel()
{
ClearCommand= new ClearCommand(this);
}
class ClearCommand : ICommand
{
private MainViewModel viewModel;
public ClearCommand(MainViewModel viewModel)
{
this.viewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
viewModel.vmTextBox1 = String.Empty;
MessageBox.Show("Textbox1 Cleared");
}
}
With the ICommand version (which I would prefer) you can bind directly to the command:
<Button Command="{Binding ClearCommand}"/>
There is no Window.CommandBinding necessary. This works, if an instance of the MainViewModel is set as the window's or button's DataContext.
I have a class named Data with some public members: Name, Age, Address.
I have also window with text boxes Name, Age, Address.
The Data object can change any time.
How can I bind the Data object to the text boxes and follow after object changes?
I know there is INotifyPropertyChanged and "dependency-properties" but I do not know how to use them.
Edit
public class MyData : INotifyPropertyChanged
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
if (_name != value)
{
_name = value;
OnPropertyChnged("Name");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
ProppertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(name));
}
}
XAML code:
xmlns:myApp="clr-namespace:MyApp"
<Window.Resources><myApp:MyData x:key = data/></WindowResources>
<TextBox><TextBox.Text><Binding Source="{StaticResource data}" Path="Name" UpdateSourceTrigger="PropertyChanged"/></TextBox.Text></TextBox>
class OtherClass
{
private MyData data;
//the window that have the binding textbox
private MyWindow window;
public OtherClass()
{
data = new MyData();
data.Name = "new name"
window = new MyWindow();
window.show();
}
}
This link from MSDN explains it well.
MSDN link is dead, adding link to a similar article.
When your class property is changed, your property should raise a OnPropertyChanged event with the name of the property so that the View knows to refresh it's binding.
public String Name
{
get { return _name; }
set
{
if (_name != value)
{
_name = value;
this.OnPropertyChanged("Name");
}
}
}
And your textbox should have a binding such as:
<TextBox Text="{Binding Name}"/>
I have a ViewModelBase class which is where I have implemented my OnPropertyChandedEvent for all derived models to call:
/// <summary>
/// An event for when a property has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Virtual method to call the Property Changed method
/// </summary>
/// <param name="propertyName">The name of the property which has changed.</param>
protected virtual void OnPropertyChanged(String propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
Let's the Data class implements INotifyPropertyChanged . Raise the event when someone change the property value on the instances of Data. Then set the proper DataContext to your UI, and bind the single ui element as for example:
<TextBox Text="{Binding Age}"/>
I have a problem. It seems a simple thing but it isn't that easy. I have two listboxes, with objects in it. One is full with available objects and the other is empty, and I can fill this up with drag and drop. If you fill this empty listbox, the items are also added to a list of, this is a property of another object. But now my question is how I can easily delete an object in this listbox by clicking on a button.
I tried something but it wouldn't run.
Here is my xaml:
<ListBox ItemsSource="{Binding AvailableQuestions}"
DisplayMemberPath="Description"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True" Margin="0,34,0,339" Background="#CDC5CBC5"
dd:DragDrop.DropHandler="{Binding}"/>
<ListBox SelectedItem="{Binding Path=SelectedQuestionDropList, UpdateSourceTrigger=PropertyChanged,Mode=TwoWay}" ItemsSource="{Binding SelectedQuestions}"
DisplayMemberPath="Description"
dd:DragDrop.IsDragSource="True"
dd:DragDrop.IsDropTarget="True" Margin="0,201,0,204" Background="#CDC5CBC5"
dd:DragDrop.DropHandler="{Binding}" />
<Button Content="Delete" Height="23"
HorizontalAlignment="Left"
Margin="513,322,0,0"
Name="button1"
VerticalAlignment="Top" Width="75" Command="{Binding Commands}"
CommandParameter="Delete" />
here is my viewmodel:
// other stuff and properties..
public ICommand Commands { get; set; }
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested += value; }
}
public void Execute(Object parameter)
{
foreach (ExaminationQuestion exaq in this.Examination.ExaminationQuestions)
{
if (exaq.Question.Guid == SelectedQuestionDropList.Guid)
{
this.Examination.ExaminationQuestions.Remove(exaq);
SelectedQuestions.Remove(SelectedQuestionDropList);
OnPropertyChanged("SelectedQuestions");
}
}
}
can somebody please help me out?
You need to know two things:
First, Don't forget to bind your ListBox to an ObservableCollection rather than to a classic IList. Otherwise, if your command is actually fired and removes an item from the list, the UI wouldn't change at all
Second, your command here is never created. Let me explain. You are binding the Delete button to your command Commands. It has a getter. However, if you call get, what will it return? Nothing, the command is null...
Here is the way I'd advise you to work: Create a generic command class (I called it RelayCommand, following a tutorial... but the name doesn't matter):
/// <summary>
/// Class representing a command sent by a button in the UI, defines what to launch when the command is called
/// </summary>
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]
/// <summary>
/// Defines if the current command can be executed or not
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
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, in your ViewModel, create a property command, and define the actual getter (you don't have to use the setter, you can just forget it):
private RelayCommand _deleteCommand;
public ICommand DeleteCommand
{
get
{
if (_deleteCommand == null)
{
_deleteCommand = new RelayCommand(param => DeleteItem());
}
return _deleteCommand;
}
}
This means that when you call your command, it will call the function DeleteItem
The only thing left to do is:
private void DeleteItem() {
//First step: copy the actual list to a temporary one
ObservableCollection<ExaminationQuestion> tempCollection = new ObservableCollection<ExaminationQuestion>(this.Examination.ExaminationQuestions);
//Then, iterate on the temporary one:
foreach (ExaminationQuestion exaq in tempCollection)
{
if (exaq.Question.Guid == SelectedQuestionDropList.Guid)
{
//And remove from the real list!
this.Examination.ExaminationQuestions.Remove(exaq);
SelectedQuestions.Remove(SelectedQuestionDropList);
OnPropertyChanged("SelectedQuestions");
}
}
}
And there you go :)
I'm having some difficulty with Context Menu commands on my View Model.
I'm implementing the ICommand interface for each command within the View Model, then creating a ContextMenu within the resources of the View (MainWindow), and using a CommandReference from the MVVMToolkit to access the current DataContext (ViewModel) Commands.
When I debug the application, it appears that the CanExecute method on the command is not being called except at the creation of the window, therefore my Context MenuItems are not being enabled or disabled as I would have expected.
I've cooked up a simple sample (attached here) which is indicative of my actual application and summarised below. Any help would be greatly appreciated!
This is the ViewModel
namespace WpfCommandTest
{
public class MainWindowViewModel
{
private List<string> data = new List<string>{ "One", "Two", "Three" };
// This is to simplify this example - normally we would link to
// Domain Model properties
public List<string> TestData
{
get { return data; }
set { data = value; }
}
// Bound Property for listview
public string SelectedItem { get; set; }
// Command to execute
public ICommand DisplayValue { get; private set; }
public MainWindowViewModel()
{
DisplayValue = new DisplayValueCommand(this);
}
}
}
The DisplayValueCommand is such:
public class DisplayValueCommand : ICommand
{
private MainWindowViewModel viewModel;
public DisplayValueCommand(MainWindowViewModel viewModel)
{
this.viewModel = viewModel;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (viewModel.SelectedItem != null)
{
return viewModel.SelectedItem.Length == 3;
}
else return false;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show(viewModel.SelectedItem);
}
#endregion
}
And finally, the view is defined in Xaml:
<Window x:Class="WpfCommandTest.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfCommandTest"
xmlns:mvvmtk="clr-namespace:MVVMToolkit"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<mvvmtk:CommandReference x:Key="showMessageCommandReference" Command="{Binding DisplayValue}" />
<ContextMenu x:Key="listContextMenu">
<MenuItem Header="Show MessageBox" Command="{StaticResource showMessageCommandReference}"/>
</ContextMenu>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ListBox ItemsSource="{Binding TestData}" ContextMenu="{StaticResource listContextMenu}"
SelectedItem="{Binding SelectedItem}" />
</Grid>
</Window>
To complete Will's answer, here's a "standard" implementation of the CanExecuteChanged event :
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
(from Josh Smith's RelayCommand class)
By the way, you should probably consider using RelayCommand or DelegateCommand : you'll quickly get tired of creating new command classes for each and every command of you ViewModels...
You have to keep track of when the status of CanExecute has changed and fire the ICommand.CanExecuteChanged event.
Also, you might find that it doesn't always work, and in these cases a call to CommandManager.InvalidateRequerySuggested() is required to kick the command manager in the ass.
If you find that this takes too long, check out the answer to this question.
Thank you for the speedy replies. This approach does work if you are binding the commands to a standard Button in the Window (which has access to the View Model via its DataContext), for example; CanExecute is shown to be called quite frequently when using the CommandManager as you suggest on ICommand implementing classes or by using RelayCommand and DelegateCommand.
However, binding the same commands via a CommandReference in the ContextMenu
do not act in the same way.
In order for the same behaviour, I must also include the EventHandler from Josh Smith's RelayCommand, within CommandReference, but in doing so I must comment out some code from within the OnCommandChanged Method. I'm not entirely sure why it is there, perhaps it is preventing event memory leaks (at a guess!)?
public class CommandReference : Freezable, ICommand
{
public CommandReference()
{
// Blank
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference), new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
#region ICommand Members
public bool CanExecute(object parameter)
{
if (Command != null)
return Command.CanExecute(parameter);
return false;
}
public void Execute(object parameter)
{
Command.Execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandReference commandReference = d as CommandReference;
ICommand oldCommand = e.OldValue as ICommand;
ICommand newCommand = e.NewValue as ICommand;
//if (oldCommand != null)
//{
// oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
//}
//if (newCommand != null)
//{
// newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
//}
}
#endregion
#region Freezable
protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}
#endregion
}
However, binding the same commands via a CommandReference in the
ContextMenu do not act in the same way.
That's a bug in CommandReference implementation. It follows from these two points:
It is recommended that the implementers of ICommand.CanExecuteChanged hold only weak references to the handlers (see this answer).
Consumers of ICommand.CanExecuteChanged should expect (1) and hence should hold strong references to the handlers they register with ICommand.CanExecuteChanged
The common implementations of RelayCommand and DelegateCommand abide by (1). The CommandReference implementation doesn't abide by (2) when it subscribes to newCommand.CanExecuteChanged. So the handler object is collected and after that CommandReference no longer gets any notifications that it was counting on.
The fix is to hold a strong ref to the handler in CommandReference:
private EventHandler _commandCanExecuteChangedHandler;
public event EventHandler CanExecuteChanged;
...
if (oldCommand != null)
{
oldCommand.CanExecuteChanged -= commandReference._commandCanExecuteChangedHandler;
}
if (newCommand != null)
{
commandReference._commandCanExecuteChangedHandler = commandReference.Command_CanExecuteChanged;
newCommand.CanExecuteChanged += commandReference._commandCanExecuteChangedHandler;
}
...
private void Command_CanExecuteChanged(object sender, EventArgs e)
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, e);
}
In order for the same behaviour, I must also include the EventHandler
from Josh Smith's RelayCommand, within CommandReference, but in doing
so I must comment out some code from within the OnCommandChanged
Method. I'm not entirely sure why it is there, perhaps it is
preventing event memory leaks (at a guess!)?
Note that your approach of forwarding subscription to CommandManager.RequerySuggested also eliminates the bug (there's no more unreferenced handler to begin with), but it handicaps the CommandReference functionality. The command with which CommandReference is associated is free to raise CanExecuteChanged directly (instead of relying on CommandManager to issue a requery request), but this event would be swallowed and never reach the command source bound to the CommandReference. This should also answer your question as to why CommandReference is implemented by subscribing to newCommand.CanExecuteChanged.
UPDATE: submitted an issue on CodePlex
An easier solution for me, was to set the CommandTarget on the MenuItem.
<MenuItem Header="Cut" Command="Cut" CommandTarget="
{Binding Path=PlacementTarget,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ContextMenu}}}"/>
More info: http://www.wpftutorial.net/RoutedCommandsInContextMenu.html