The following code is working for usercontrols but not in the Mainwindows. Setting Focusable="True" for the mainwindow.
<Window.InputBindings>
<KeyBinding Modifiers="Ctrl" Key="S" Command="{Binding SaveCommand}" />
</Window.InputBindings>
private ICommand _saveCommand;
public ICommand SaveCommand
{
get
{
if (_saveCommand == null)
{
_saveCommand = new RelayCommand(
param => this.SaveObject(),
param => this.CanSave()
);
}
return _saveCommand;
}
}
private bool CanSave()
{
return (Project != null);
}
private void SaveObject()
{
// Code here
}
Got fixed by using the below code from the link.
Keyboard shortcuts in WPF
public YourWindow() //inside any WPF Window constructor
{
...
//add this one statement to bind a new keyboard command shortcut
InputBindings.Add(new KeyBinding( //add a new key-binding, and pass in your command object instance which contains the Execute method which WPF will execute
new WindowCommand(this)
{
ExecuteDelegate = TogglePause //REPLACE TogglePause with your method delegate
}, new KeyGesture(Key.P, ModifierKeys.Control)));
...
}
Create a simple WindowCommand class which takes an execution delegate to fire off any method set on it.
public class WindowCommand : ICommand
{
private MainWindow _window;
//Set this delegate when you initialize a new object. This is the method the command will execute. You can also change this delegate type if you need to.
public Action ExecuteDelegate { get; set; }
//You don't have to add a parameter that takes a constructor. I've just added one in case I need access to the window directly.
public WindowCommand(MainWindow window)
{
_window = window;
}
//always called before executing the command, mine just always returns true
public bool CanExecute(object parameter)
{
return true; //mine always returns true, yours can use a new CanExecute delegate, or add custom logic to this method instead.
}
public event EventHandler CanExecuteChanged; //i'm not using this, but it's required by the interface
//the important method that executes the actual command logic
public void Execute(object parameter)
{
if (ExecuteDelegate != null)
{
ExecuteDelegate();
}
else
{
throw new InvalidOperationException();
}
}
}
Related
my goal is filter a dataGrid with a textbox input, so i have textbox to filter a datagrid. for all the other commands i used one of these constractors in my **relayCommand ** as follows:
// Constructors
public RelayCommand(Action<object> action)
{
_execute = action;
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
now to do filter datagrid based on a textbox input i make a searchMethod in my viewModel as follows:
private ObservableCollection<Coach> searchMethod()
{
return CoachManage.GetCoachBySearch(TextToFilter);
}
and also a Command in my viewModel as follows:
public ICommand SearchCommand
{
get
{
if (_searchCommand == null)
{
_searchCommand = new RelayCommand(new Action<object>(searchMethod()));
}
return _searchCommand;
}
set
{
SetProperty(ref _searchCommand, value);
}
}
and finally my textbox binded to the property of the one in my viewModel in my View:
<TextBox x:Name="txtSearch"
Margin="7,3,3,3" Grid.Row="1" Grid.Column="1"
Text="{Binding TextToFilter, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource TextBoxStyle}"
/>
but the error says in following code:
*_searchCommand = new RelayCommand(new Action<object>(SearchCoach()));*
Method name expected
it seems that I should make new constructor in my relayCommand class which takes parameter to pass it in my Method. is that right and how could i do that?
You must register the delegate properly (the issue is not related to the constructor).
Here are some variations that all yield the same result, but more or less elegant. Some use the shortest form, method groups. Some use lambda expressions:
// Very verbose using method groups
var searchCommand = new RelayCommand(new Action<object>(SearchCoach));
// Very compact using method groups
var searchCommand = new RelayCommand(SearchCoach);
// Using a lambda expression
var searchCommand = new RelayCommand(commandParameter => SearchCoach(commandParameter));
// Using method groups
Action<object> commandDelegate = SearchCoach;
var searchCommand = new RelayCommand(commandDelegate);
// Using a lambda expression
Action<object> commandDelegate = commandParameter => SearchCoach(commandParameter);
var searchCommand = new RelayCommand(commandDelegate);
And many more, for example using an anonymous method.
If you want to use a parameterless void delegate you would have to add a corresponding constructor overload to your RelayCommand:
private Action _executeParameterless;
public RelayCommand(Action parameterlessAction)
{
_executeParameterless = parameterlessAction;
}
public void Execute(object commandParameter)
{
_execute?.Invoke(commandParameter);
_executeParameterless?.Invoke();
}
I have a WPF application and I want the Start button control only enabled if they have to have specified a value in the text box for 'Download Path'.
My ViewModel contains a property for my model "ConfigurationSettings" and an ICommand implementation (CommandImp) for the button:
public class MainWindowViewModel : BaseNotifyPropertyChanged
{
private ConfigurationSettings _configurationSettings { get; set; }
public ConfigurationSettings ConfigurationSettings
{
get
{
return _configurationSettings;
}
set
{
if (_configurationSettings != value)
{
_configurationSettings = value;
RaisePropertyChanged("ConfigurationSettings");
}
}
}
public CommandImp StartCommand { get; set; } // this is an implementation of ICommand
public MainWindowViewModel()
{
StartCommand = new CommandImp(OnStart, CanStart);
_configurationSettings = new ConfigurationSettings();
_configurationSettings.PropertyChanged += delegate (object o,
PropertyChangedEventArgs args)
{
StartCommand.RaiseCanExecuteChanged(); // break point here is never reached
};
}
private bool CanStart()
{
if (!String.IsNullOrEmpty(ConfigurationSettings.DownloadPath))
{
return true;
}
return false;
}
}
In my XAML I have a Start button and the with Command = "{Binding StartCommand}".
My ConfigurationSettings class just has a string for the DownloadPath which is bound to a textbox in the XAML:
public class ConfigurationSettings : BaseNotifyPropertyChanged
{
private string _downloadPath { get; set; }
public string DownloadPath
{
get { return _downloadPath; }
set
{
if (_downloadPath != value)
{
_downloadPath = value;
RaisePropertyChanged("DownloadPath"); // break point here IS reached
}
}
}
}
When the user enters a DownloadPath, I expect it to be triggering the PropertyChanged Event, and running my delegate method defined in the ViewModel constructor.
If I move the Command Button inside the ConfigurationSettings class I can do away with event subscription and just use StartCommand.RaiseCanExecuteChanged() right beneath RaisePropertyChanged("DownloadPath");. But I don't want the ICommand as part of my Model.
How can I trigger CanStart() when one of the properties of ConfigurationSettings changes?
UPDATE:
Here is the XAML for the text box binding:
<TextBlock Text="{Binding ConfigurationSettings.DownloadPath, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" TextWrapping="WrapWithOverflow" />
And the button:
<Button Content="Start" Command="{Binding StartCommand}"></Button>
I should note that the bindings are working correctly. When I update the textblock, I can see in the ViewModel that ConfigurationSettings.DownloadPath is correctly being updated.
BaseNotifyPropertyChanged is an implementation of INotifyPropertyChanged like so:
public class BaseNotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
I don't seem to be having any issues with the property changed event. I can put a break point in here and it is hit when I update the DownloadPath text box. It's when I subscribe to this PropertyChanged event in my ViewModel constructor, my delegate method isn't firing.
Hate to answer my own question but the people commenting made me think about restructuring my question - which led me to the answer before needing to make another update.
The solution was to move my event subscription inside the 'set' function for ConfigurationSettings:
private ConfigurationSettings _configurationSettings { get; set; }
public ConfigurationSettings ConfigurationSettings
{
get
{
return _configurationSettings;
}
set
{
if (_configurationSettings != value)
{
_configurationSettings = value;
_configurationSettings = new Model.ConfigurationSettings();
_configurationSettings.PropertyChanged += (o, args) =>
{
StartCommand.RaiseCanExecuteChanged();
};
RaisePropertyChanged("ConfigurationSettings");
}
}
}
The problem was where I was setting my Data Context which I did not originally suspect was at all the problem. I load the view model from an XML file on disk. And when the application is closed, I overwrite that file with the latest ViewModel.
In the constructor I was reading and setting the DataContext:
public MainWindowView()
{
InitializeComponent();
string appPath = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase);
DataSourcePath = new Uri(System.IO.Path.Combine(appPath, DataFileName));
if (File.Exists(DataSourcePath.LocalPath))
{
XmlReader reader = XmlReader.Create(DataSourcePath.LocalPath);
DataContext = (MainWindowViewModel)serialize.Deserialize(reader);
reader.Close();
}
else
{
WriteDataViewModelToDisk(); // new empty view model written to disk
}
}
If this was the first time I ran the code, with no pre-existing file, my delegate event handler actually worked. The issue was when this code loaded a pre-existing XML file, it overwrote the ConfigurationSettings property in my view model - thus destroying the event subscription.
I am just after some information regarding ICommands,
On my WPF application, I have onClick events that adds to a ObservableCollection. So (ObservableCollection.Add()
However, I have 2 similar events to add to a collection also. So I hear I could use the ICommand interface to "execute" for adding/Editing/Removing etc, so I don't need these separate events.
Can someone provide me a example of how I can do this in MVVM. (All adds are in my ViewModel)
Thanks
You might want to look into the "RelayCommand" - it's a common implementation of an ICommand that will simplify your view-model code, allowing you to specify delegates for the ICommand's "Execute" and "CanExecute" methods. You'll find plenty of implementations on the web, but this is the one I use:
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this._execute = execute;
this._canExecute = canExecute;
}
public virtual bool CanExecute(object parameter)
{
return this._canExecute == null || this._canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public virtual void Execute(object parameter)
{
this._execute(parameter);
}
}
In your VM, expose a command like this:-
public ICommand FooCommand
{
get
{
if (_fooCommand == null)
{
_fooCommand = new RelayCommand(ExecuteFooCommand, CanDoFooCommand);
}
return _fooCommand;
}
}
private void ExecuteFooCommand(object commandParameter)
{
// Code to execute the command.
}
private bool CanDoFooCommand()
{
// Code that indicates whether the command can be executed.
// This will manifest itself in the view by enabling/disabling the button.
}
As the RelayCommand ctr parameters are delegates, you can of course do stuff like this:-
new RelayCommand(o => { // do something }, o => true);
Finally, bind your command to your view button:-
<Button Content="Click me" Command="{Binding FooCommand}" ... />
You can also pass parameters to the command delegate:-
<Button Content="Click me" Command="{Binding FooCommand}" CommandParamter="123" />
(Written out from memory so may not be 100% syntactically correct!)
Going one step further...
To simplify things even more, I use a dynamic property to expose VM commands to the view. In my VM base class I have the following property:-
public dynamic Commands
{
get
{
return _commands;
}
}
Then in a VM's constructor I can create all of its commands like this:-
Commands.FooCommand = new RelayCommand(.....
Commands.BarCommand = ..etc..
In my XAML I bind commands like this:- Command={Binding Commands.FooCommand}.
It's a timesaver as it just means I can hang as many commands off of a single property as I want, rather than expose each one as a separate property as in my earlier example.
What's the right approach to open a child window (for example, to modify a selected item on the main window) keeping MVVM in mind?
Here's what I have: MainWindow.xaml (and in MainWindow.xaml.cs it assigns MainVM as its own DataContext)
I would also like to have: ChildWindow.xaml and barebones ChildWindow.xaml.cs with ChildVM behind controls.
So, now:
How can I popup ChildWindow and pass some object Data to its
ChildVM?
Get the result (true/false) and result data (some complex
object) back to MainVM?
As a bonus, can changes in Data be observed
by MainVM while they are being worked on by ChildVM?
Here's what I tried - it doesn't solve everything, but is this even the right direction?
For (2), I created a subclass of Window, called DialogWindow, which has 3 DependencyProperties: Data (for input data), ResultData (for output data) and ResultValue (for a bool result).
ResultData and ResultValue are both set by the ChildVM of DialogWindow using Binding, and when ResultValue is set, the DialogWindow closes.
At the moment, the ChildWindow is launched (for all intents and purposes) from MainWindow.xaml.cs - kinda bad. I can then pass some input data, like so:
ChildDialogWindow w = new ChildDialogWindow();
w.Data = myDataObj;
So, now I need to have a property Data on ChildVM, and set in ChildDialogWindow.xaml.cs. Again, making .xaml.cs thicker.
I thought that maybe a better approach that avoids MainWindow.xaml.cs would be some kind of DialogService which is passed to MainVM as a dependency. But then, how can I pass values to the ChildVM?
Try this.
Make a DialogService.cs
public class DialogService
{
public void Show(FrameworkElement view, ChildViewModel ChildVM)
{
Window window = new Window();
window.Content = view;
window.DataContext = ChildVM;
// For closing this dialog using MVVM
ChildVM.RequestClose += delegate
{
window.Close();
};
window.Show();
}
}
Now in ChildVm class, add this
public ICommand CloseCommand
{
get
{
if (_closeCommand == null)
_closeCommand = new RelayCommand(param => this.OnRequestClose());
return _closeCommand;
}
}
public event EventHandler RequestClose;
void OnRequestClose()
{
EventHandler handler = this.RequestClose;
if (handler != null)
handler(this, EventArgs.Empty);
}
Now, this the way to launch this
public void OpenChildDailog()
{
DialogService service = new DialogService();
ChildViewModel childVM = new ChildViewModel();
childVM.Data = ; // Assign whatever you want
childVM.ResultData = ;
service.Show(new ChildView(), childVM);
// Now get the values when the child dailog get closed
var retVal = childVM.ResultValue;
}
I'm using the ICommand helper "RelayCommand," and pushing an IntPtr datatype to the new ViewModel (or use any other object.) Lots of cookie cutter stuff.
Main View:
<Button Command="{Binding DataContext.ShowObjectInfoCommand}" CommandParameter="{Binding ObjectOffset}" Content="{Binding Name}"/>
MainViewModel:
private RelayCommand _showObjectInfoCommand;
public RelayCommand ShowObjectInfoCommand { get { return _showObjectInfoCommand ?? (_showObjectInfoCommand = new RelayCommand(ExeShowObjectInfoCommand)); } set { } } //Draw Specific Item Table
void ExeShowObjectInfoCommand(object parameter)
{
ViewObjectInfo objInfo = new ViewObjectInfo();
IObjectOffsetParameter viewModel = objInfo.DataContext as IObjectOffsetParameter;
viewModel.ObjectOffset = (IntPtr)parameter;
objInfo.Show();
}
New ViewModel + interface:
interface IObjectOffsetParameter
{
IntPtr ObjectOffset { get; set; }
}
class ViewModelObjectInfo : ViewModelBase, IObjectOffsetParameter
{
public ViewModelObjectInfo()
{
}
private IntPtr _objectOffset; //Entity Offset
public IntPtr ObjectOffset
{
get { return _objectOffset; }
set { if (_objectOffset != value) { _objectOffset = value; RaisePropertyChanged("Offset"); } }
}
}
New View code-behind:
InitializeComponent();
ViewModelObjectInfo viewModel = new ViewModelObjectInfo();
this.DataContext = viewModel;
New View xaml:
<TextBlock Text="{Binding ObjectOffset}"/>
Does anyone know how I can force CanExecute to get called on a custom command (Josh Smith's RelayCommand)?
Typically, CanExecute is called whenever interaction occurs on the UI. If I click something, my commands are updated.
I have a situation where the condition for CanExecute is getting turned on/off by a timer behind the scenes. Because this is not driven by user interaction, CanExecute is not called until the user interacts with the UI. The end result is that my Button remains enabled/disabled until the user clicks on it. After the click, it is updated correctly. Sometimes the Button appears enabled, but when the user clicks it changes to disabled instead of firing.
How can I force an update in code when the timer changes the property that affects CanExecute? I tried firing PropertyChanged (INotifyPropertyChanged) on the property that affects CanExecute, but that did not help.
Example XAML:
<Button Content="Button" Command="{Binding Cmd}"/>
Example code behind:
private ICommand m_cmd;
public ICommand Cmd
{
if (m_cmd == null)
m_cmd = new RelayCommand(
(param) => Process(),
(param) => EnableButton);
return m_cmd;
}
// Gets updated from a timer (not direct user interaction)
public bool EnableButton { get; set; }
Calling System.Windows.Input.CommandManager.InvalidateRequerySuggested() forces the CommandManager to raise the RequerySuggested event.
Remarks: The CommandManager only pays attention to certain conditions in determining when the command target has changed, such as change in keyboard focus. In situations where the CommandManager does not sufficiently determine a change in conditions that cause a command to not be able to execute, InvalidateRequerySuggested can be called to force the CommandManager to raise the RequerySuggested event.
I was aware of CommandManager.InvalidateRequerySuggested() a long time ago, and used it, but it wasn't working for me sometimes. I finally figured out why this was the case! Even though it doesn't throw like some other actions, you HAVE to call it on the main thread.
Calling it on a background thread will appear to work, but sometimes leave the UI disabled. I really hope this helps somebody, and saves them the hours I just wasted.
A workaround for that is binding IsEnabled to a property:
<Button Content="Button" Command="{Binding Cmd}" IsEnabled="{Binding Path=IsCommandEnabled}"/>
and then implement this property in your ViewModel. This also makes it a bit easier for the UnitTesting to work with the properties rather than commands to see if the command can be executed at a certain point of time.
I, personally, find it more convenient.
Probably this variant will suit you:
public interface IRelayCommand : ICommand
{
void UpdateCanExecuteState();
}
Implementation:
public class RelayCommand : IRelayCommand
{
public event EventHandler CanExecuteChanged;
readonly Predicate<Object> _canExecute = null;
readonly Action<Object> _executeAction = null;
public RelayCommand( Action<object> executeAction,Predicate<Object> canExecute = null)
{
_canExecute = canExecute;
_executeAction = executeAction;
}
public bool CanExecute(object parameter)
{
if (_canExecute != null)
return _canExecute(parameter);
return true;
}
public void UpdateCanExecuteState()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
public void Execute(object parameter)
{
if (_executeAction != null)
_executeAction(parameter);
UpdateCanExecuteState();
}
}
Using simple:
public IRelayCommand EditCommand { get; protected set; }
...
EditCommand = new RelayCommand(EditCommandExecuted, CanEditCommandExecuted);
protected override bool CanEditCommandExecuted(object obj)
{
return SelectedItem != null ;
}
protected override void EditCommandExecuted(object obj)
{
// Do something
}
...
public TEntity SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
//Refresh can execute
EditCommand.UpdateCanExecuteState();
RaisePropertyChanged(() => SelectedItem);
}
}
XAML:
<Button Content="Edit" Command="{Binding EditCommand}"/>
Thanks guys for the tips. Here's a bit of code on how to marshal that call from a BG thread to the UI thread:
private SynchronizationContext syncCtx; // member variable
In the constructor:
syncCtx = SynchronizationContext.Current;
On the background thread, to trigger the requery:
syncCtx.Post( delegate { CommandManager.InvalidateRequerySuggested(); }, null );
Hope that helps.
-- Michael
To update only a single GalaSoft.MvvmLight.CommandWpf.RelayCommand you could use
mycommand.RaiseCanExecuteChanged();
and for me i've created an Extension method:
public static class ExtensionMethods
{
public static void RaiseCanExecuteChangedDispatched(this RelayCommand cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
public static void RaiseCanExecuteChangedDispatched<T>(this RelayCommand<T> cmd)
{
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() => { cmd.RaiseCanExecuteChanged(); }));
}
}