Prism Delegate command not enabling button - wpf

I am trying to enable and disable a button in a WPF (PRISM) user control based on data entered by the user.
In the constructor I do
SubmitCommand = new DelegateCommand<object>(OnSubmit, CanSubmit);
public ICommand SubmitCommand { get; private set; }
private void OnSubmit(object arg)
{
_logger.Log(arg.ToString());
}
private bool CanSubmit(object arg)
{
return Title.Length > 0;
}
private string _title="";
public string Title
{
get { return _title; }
set
{
if (_title != value)
{
_title = value;
this.RaisePropertyChanged();
}
}
}
I bound the SubmitCommand in the Xaml as below
<Button Content="Submit" Width="100" Command="{Binding Path=SubmitCommand}" CommandParameter="{Binding ElementName=TitleText, Path=Text}" />
The issue is when title value changes, the button does not get enabled. May be I am missing something. Thanks for your help!

It sounds like you're needing to raise the CanExecuteChanged event on your command. For more details, see http://wpftutorial.net/DelegateCommand.html and http://msdn.microsoft.com/en-us/library/system.windows.input.icommand.canexecutechanged.aspx
Note that the first link is to an implementation of DelegateCommand, and is probably not what you're actually using. For the prism DelegateCommand, you simply need to call the RaiseCanExecuteChanged() method when you want to determine whether the button should be reenabled.
Good luck!
Nate

Add:
SubmitCommand.RaiseCanExecuteChanged();
After:
this.RaisePropertyChanged();

Related

WPF prism: using command on toggleswitch

I want to use a delegate command with a Horizontaltoggleswitch that is fired when the switch is toggled. I use delegate commands with button and also with textboxes (where the Event is fired when the enterkey is pressed), but I cant find how to solve that with toggleswitch? I tried this:
XAML:
<toggleSwitch:HorizontalToggleSwitch CheckedContent="Open" UncheckedContent="Closed" IsChecked="{Binding SubstrateValveOpen,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
<toggleSwitch:HorizontalToggleSwitch.InputBindings>
<KeyBinding Command="{Binding SubstrateCommand}" Key="??"/>
</toggleSwitch:HorizontalToggleSwitch.InputBindings>
</toggleSwitch:HorizontalToggleSwitch>
ViewModel:
private bool _substrateValveOpen;
public bool SubstrateValveOpen
{
get => _substrateValveOpen;
set => SetProperty(ref _substrateValveOpen, value);
}
public DelegateCommand SubstrateCommand => new DelegateCommand(Substrate, CanSubstrate);
...
but I dont really know what to do with the Event.
thanks for any idea!
The command is completely superfluous here. The property should do everything here.
Reminder: a property ist not just a field with PropertyChanged...
private bool _substrateValveOpen;
public bool SubstrateValveOpen
{
get => _substrateValveOpen;
set
{
if (value && !_substrateValveOpen && !CanSubstrate())
return;
if (SetProperty(ref _substrateValveOpen, value) && value)
Substrate();
}
}

WPF: Changing tabs makes Windowsformshost child disappear

if you can spare the time, I am working on a problem for which I can't find a solution on the internet.
I need two tabs' richtextboxes to bind the same property. Both RichtextBoxes are hosted in WPF via Windowsformshost. But if I alternate between tabs, one RichtTextBox will simply dissapear (always the first one that was visible). I am migrating an app and so far, I am forced to use the Windowsforms RichtextBox.
I hope I managed to properly convey my problem - sorry, I am not a native speaker.
Thanks in advance
Edit:
I was asked to provide a clear example of my problem. Thanks for the note. I completely rewrote my question. Further, I have uploaded a micro app where I have isolated the problem. Just click the two tab buttons alternately and one Richtextbox will dissapear.
Below, I will provide the code if this serves:
This is my Mainwindow (XAML):
<StackPanel Orientation="Horizontal" Height="35" Margin="0,35,0,0" VerticalAlignment="Top" >
<Button x:Name="Tab1" Command="{Binding LeftCommand}" Content="Left" MinWidth="100" />
<Button x:Name="Tab2" Command="{Binding RightCommand}" Content="Right" MinWidth="100" />
</StackPanel>
<Frame x:Name="MyFrame"
Content="{Binding Path=CurrentTab, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="5,70,0,0" NavigationUIVisibility="Hidden" />
This is its viewmodel:
class MainWindowViewModel : INotifyPropertyChanged
{
public ICommand LeftCommand { get; }
public ICommand RightCommand { get; }
private TabViewModel MyTabViewModel { get; set; }
private PageLeft MyPageLeft { get; set; }
private PageRight MyPageRight { get; set; }
public MainWindowViewModel()
{
this.LeftCommand = new ModelCommand(p => this.SetSelectedTab("left"));
this.RightCommand = new ModelCommand(p => this.SetSelectedTab("right"));
this.MyTabViewModel = new TabViewModel();
this.MyPageLeft = new PageLeft() { DataContext = this.MyTabViewModel };
this.MyPageRight = new PageRight() { DataContext = this.MyTabViewModel };
//initial view on something
//this.SetSelectedTab("left");
}
private void SetSelectedTab(string param)
{
switch (param)
{
case "left":
this.CurrentTab = this.MyPageLeft;
break;
case "right":
this.CurrentTab = this.MyPageRight;
break;
}
}
private object _CurrentTab;
public object CurrentTab
{
get { return _CurrentTab; }
set
{
if (value != _CurrentTab)
{
_CurrentTab = value;
NotifyPropertyChanged_MainViewModel();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged_MainViewModel([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Furthermore, I have two pages (MyPageLeft, MyPageRight) that use the same viewmodel (TabViewModel) and use the same bit of XAML code:
<ContentControl Content="{Binding Path=MyWindowsFormsHost, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Both Pages use the same TabViewModel:
class TabViewModel : INotifyPropertyChanged
{
private WindowsFormsHost _MyWindowsFormsHost;
public WindowsFormsHost MyWindowsFormsHost
{
get { return _MyWindowsFormsHost; }
set
{
if (value != _MyWindowsFormsHost)
{
_MyWindowsFormsHost = value;
NotifyPropertyChanged_TabViewModel();
}
}
}
public TabViewModel()
{
this.MyWindowsFormsHost = new WindowsFormsHost() { Child = new RichTextBox() { Text = "test" } };
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged_TabViewModel([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The Problem: If I start the app and click on the two tab buttons alternatingly, one of the framed RichtextBoxes will dissapear.
If anyone might need it, I used a dirty solution - although it might not be recommendable.
I extended the event of the switch tab buttons. It takes the RTF property of the currently selected Tab's Richtextbox and infuses it in the other Richtextbox. It goes kinda like this:
if (Tab2 Button is clicked)
this.MyRTF = Tab1.Richtextbox.RTF;
Tab2.Richttextbox.Rtf = this.MyRTF;
Note that this is a beginner's hack on a probably overall questionable approach.
Thanks to anyone who read my question!

WPF MVVM cancel Window.Closing event

In WPF application together with MVVMLight Toolkit, I would like to see your opinion, what is the best way to implement if I need to Cancel the Window Close event.
In Window.Closing event I can set the e.Cancel = true, which prevents closing the form. To identify if the Close is allowed, or should be prevented is in the ViewModel context.
One solution could be if I define an Application variable, and I can query this in the normal event handler in view code behind?
thanks
With MVVM Light you got EventToCommand:
So you could in xaml wire up the closing event to the VM.
<Window ...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="http://www.galasoft.ch/mvvmlight">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<command:EventToCommand Command="{Binding ClosingCommand}"
PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
and in the VM:
public RelayCommand<CancelEventArgs> ClosingCommand { get; private set; }
ctor() {
ClosingCommand = new RelayCommand<CancelEventArgs>(args => args.Cancel = true);
}
If you do not want to pass CancelEventArgs to the VM:
You could always take the similar approach with a Behavior and just use a simple bool from the VM(bind this bool to the Behavior) to indicate the closing event should be cancelled.
Update:
Download Link for following example
To do this with a Behavior you could just have a Behavior such as:
internal class CancelCloseWindowBehavior : Behavior<Window> {
public static readonly DependencyProperty CancelCloseProperty =
DependencyProperty.Register("CancelClose", typeof(bool),
typeof(CancelCloseWindowBehavior), new FrameworkPropertyMetadata(false));
public bool CancelClose {
get { return (bool) GetValue(CancelCloseProperty); }
set { SetValue(CancelCloseProperty, value); }
}
protected override void OnAttached() {
AssociatedObject.Closing += (sender, args) => args.Cancel = CancelClose;
}
}
Now in xaml:
<i:Interaction.Behaviors>
<local:CancelCloseWindowBehavior CancelClose="{Binding CancelClose}" />
</i:Interaction.Behaviors>
Where CancelClose is a bool property from the VM which indicates if the Closing event should be cancelled or not. In the attached example I have a Button to toggle this bool from the VM that should let you test the Behavior
You could to control this using Messages, for instance:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Messenger.Default.Register<CloseApplicationMessage>(this, m => Close());
Loaded += MainWindowLoaded;
Closing += MainWindowClosing;
}
private void MainWindowClosing(object sender, CancelEventArgs e)
{
//Ask for saving
var closingMessage = new ClosingApplicationMessage();
Messenger.Default.Send(closingMessage);
if (closingMessage.Cancel)
e.Cancel = true;
}
...
The mvvm message:
public class ClosingApplicationMessage
{
public bool Cancel { get; set; }
}
In this way, in any place you are listening to the ClosingApplicationMessage, you can control when the application is going to close, and may to cancel it.
Hope this helps...

Simple WPF MVVM Commanding Issue - What's Wrong With This Code?

I'm trying to set up a command on a button in my UI using MVVM. The command doesn't execute when I click the button, though. The code is based off of Jason Dolinger's example (link in 3rd paragraph).
It seems like it should be pretty simple, so I'm sure I'll feel silly once I find out what's wrong.
Relevant code bits follow. The command is as follows (very simple):
public class NavigateCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
MessageBox.Show("Executed.");
}
}
The XAML looks like:
<Button x:Name="btn_ProjectManager" Command="{Binding Navigate}" Content="Test Button">
The ViewModel looks like:
public class HomeScreenViewModel : DependencyObject
{
public ICommand Navigate;
public HomeScreenViewModel()
{
this.Navigate = new NavigateCommand();
}
}
Navigate should be a property. Binding works only with properties

Refresh WPF Command

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(); }));
}
}

Resources