System.Windows.Input.KeyBinding doesn't check CanExecute - wpf

So I have a simple KeyBinding on an input element that executes a command to kick off some analysis.
<dxe:TextEdit.InputBindings>
<KeyBinding Key="Enter" Command="{Binding StartAnalysisCommand}" />
</dxe:TextEdit.InputBindings>
There are a few other simple input elements that, when changed, call RaiseCanExecuteChanged on the command. This propagates to the UI button, disabling it and preventing it from executing. But this CanExecute state seems to be entirely ignored by the KeyBinding event, before and after the RaiseCanExecuteChanged is called.

Tested using a normal WPF TextBox, and it calls CanExecute once you press Enter. Must indeed be an issue in the 3rd party control.
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" CanExecute="CommandBinding_CanExecute" Executed="CommandBinding_Executed"/>
</Window.CommandBindings>
<TextBox>
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="ApplicationCommands.New"/>
</TextBox.InputBindings>
</TextBox>
Edit: Example with a RelayCommand:
public class ViewModel
{
private RelayCommand _cmd;
public RelayCommand Cmd {
get { return _cmd ?? (_cmd = new RelayCommand(Executed, CanExecute)); }
}
public void Executed() { throw new NotImplementedException(); }
public bool CanExecute()
{
return true;
}
}
And the binding with the ViewModel as the context.
<KeyBinding Key="Enter" Command="{Binding Cmd}"/>

Okay, I figured out what the problem was. Thanks everyone for helping out--your answers caused me to realize the problem. Turns out it wasn't a matter of the CanExecute being called, but rather the timing of when the binding was updated. CanExecute was being called, but with the previous value of the binding.
I used the solution found on this SO answer to accept the value on Enter and the program now works as I had originally expected.

Related

MVVM: run view-only code after calling command

What I have:
using MVVM pattern
a view written in XAML
a command MyCommand in the ViewModel which gets called from several places in the view
a method DoSthInView that operates on the view, defined in codebehind
My Goal:
Whenever the command is executed, I want to call DoSthInView, no matter which control executed the command.
Question:
Since in MVVM the ViewModel does not know the View, I cannot call DoSthInView from the ViewModel. So how do call this code?
Own thoughts:
To be less abstract, this is my use case: We have one Button and one TextBox. The command takes the text which is currently in the TextBox and writes it somewhere into the model data. When this writing is done, I want to animate a green checkmark appearing and fading out (this is DoSthInView), so that the user gets a visual confirmation that the data was updated.
There are two ways of running the command:
Click the Button
Press "Enter" while the TextBox is focused
For the Button I know a way to call DoSthInView:
<Button Content="run command" Command="{Binding MyCommand}" Click={Binding DoSthInView}" />
For the TextBox, I have a KeyBinding to take the Enter key:
<TextBox>
<TextBox.InputBindings>
<KeyBinding Command="{Binding MyCommand}" Key="Enter" />
</TextBox.InputBindings>
</TextBox>
But InputBindings seem not to support events, only commands. So here I have no idea how to call DoSthInView.
But even if I found a way to call DoSthInView from within the input binding (analog to the Button), it wouldn't feel right. I am looking for a way to say "whenever MyCommand is executed, run DoSthInView" So that not every caller of MyCommand has to care for it individually, but there is just one place to handle that. Maybe this can be done in the root FrameworkElement?
What you are asking for is possible. You need to implement RelayCommand.
You can also see my other SO answer where there is an example.
Once you have RelayCommand implemented then you can do the following:
In ViewModel:
public ICommand MyCommand { get; set; }
public MyViewModel()
{
MyCommand = new RelayCommand(MyCommand_Execute);
}
private void MyCommand_Execute(object sender)
{
var myView = sender as MyView;
myView?.DoSthInView();
}
In View:
<TextBox>
<TextBox.InputBindings>
<KeyBinding Command="{Binding Path=MyCommand}" CommandParameter="{Binding}" Key="Enter"/>
</TextBox.InputBindings>
</TextBox>
While it is not recommended to mix view and viewModel, there can be scenarios where otherwise is not possible. and sometimes it can be requirements. But again this is NOT recommended.
While I am still interested in an answer to my original question (calling codebehind-code after a command is executed), Kirenenko's advice helped me to solve my actual problem regarding the animation. This answer doesn't fit the original question any more because there is no codebehind-code (the animation is solely written in XAML, leaving no codebehind-code to execute). I still put it here because it is partially useful for me.
In the ViewModel, I have this:
...
private bool _triggerBool;
public bool TriggerBool
{
get { return _triggerBool; }
set
{
if (_triggerBool != value)
{
_triggerBool = value;
NotifyPropertyChanged(nameof(TriggerBool));
}
}
}
...
public DelegateCommand MyCommand; // consists of MyCommandExecute and MyCommandCanExecute
...
public void MyCommandExecute()
{
... // actual command code
TriggerBool = true;
TriggerBool = false;
}
...
And here is the animation written in XAML and called by the DataTrigger:
<Image Source="myGreenCheckmark.png" Opacity="0">
<Image.Style>
<Style TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding TriggerBool}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1" To="0" Duration="0.0:0:0.750"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
Looks really stupid to set TriggerBool to true and then false again, but works...
Based on Leonid Malyshev's hint here is a quite clean solution using an event ("clean" regarding seperation of View and ViewModel):
ViewModel code:
public class MyViewModel
{
...
public event Action MyCommandExecuted;
public DelegateCommand MyCommand; // consists of MyCommandExecute, MyCommandCanExecute
...
private void MyCommandExecute()
{
... // actual command code
MyCommandExecuted.Invoke();
}
...
}
View Codebehind:
public partial class MyView : Window
{
public MyView(MyViewModel vm)
{
InitializeComponent();
DataConext = vm;
vm.MyCommandExecuted += DoSthInView();
}
...
private void DoSthInView()
{
...
}
...
}
Usually you have a better more MVVM-ish way to these this thing but since I don't know your case I'll assume that there is no other way.
So to this you need a dependency property in your view. A Boolean would be great and you handle it's on changed event and run DoSthInView in it. In your view model you set the value of this property and on changed gets called.
I can give you demonstration if you need. Also keep in mind that this is event driven coding which defiles MVVM. Try to use bindings and move your DoSthInView to ViewModel if possible.

WPF - clicking on a button inside an user control doesn't call the command, and neither does clicking on the control itself

I have a serious issue with binding any command to my user control. Everything compiles, but the command is never called. I have tried two approaches - first, I tried to bind the command to a button inside my control, and when I was unable to do it, I tried to bind the command to an inputcommand of the control itself to see if it would work. It didn't. The control itself is within an ItemsControl, in case that matters.
Here's a simplified version of what I did. In the xaml.cs file of the control:
public static readonly DependencyProperty CloseCommandProperty = DependencyProperty.Register(
"CloseCommand",
typeof(ICommand),
typeof(Thumbnail),
new UIPropertyMetadata(null)
);
public ICommand CloseCommand
{
get { return (ICommand)GetValue(CloseCommandProperty); }
set { SetValue(CloseCommandProperty, value); }
}
In the UserControl's xaml file, the offending button (the UserControl has Name="Control", and Hash is another dependency property):
<Button Command="{Binding ElementName=Control, Path=CloseCommand}" CommandParameter="{Binding ElementName=Control, Path=Hash}">
<TextBlock Text="X"/></Button>
Now, a simplified (irrelevant properties not included) datatemplate part of the xaml file of the view (which has a datacontext, if that matters), where I use this control:
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:Thumbnail Hash="{Binding Hash}"
CloseCommand="{Binding ElementName=Control, Path=DataContext.RemoveImageCommand}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
Just for the sake of completeness, I'll include the command from the viewmodel.
private bool CanRemoveImageCommandExecute(string hash)
{
return true;
}
private void RemoveImageCommandExecute(string hash)
{
MessageBox.Show("ABC","ABC");
}
public ICommand RemoveImageCommand
{
get { return new RelayCommand<string>(RemoveImageCommandExecute, CanRemoveImageCommandExecute);}
}
The RelayCommand class comes from MicroMVVM, and it just creates a command from two functions (and works everywhere else).
Can you tell me why clicking the button does nothing and how to fix it?
It seems that, even though I wasted a few hours on that, I was too quick to ask the question. Literally a few minutes after posting it, I realized that my binding in ItemTemplate is wrong.
The problem was that I used ElementName instead of RelativeSource:
CloseCommand="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:AddImage}
Where local:AddImage is the name of the view which has the DataContext set to the viewmodel..

Mvvm Light & EventToCommand - Textbox LostFocus firing twice

I have a few textboxes on a form that, when focus is lost, I'd like to call a setter stored procedure to save the data, then in my callback function call a getter stored proc which will update a job costing summary on my form. I'm using Mvvm light & when I try & bind an EventToCommand on a LostFocus EventTrigger, my command is fired twice.
I understand this is due to event bubbling, but I'm not sure how to make sure my method is only actually fired once. Here's my xaml:
<TextBox x:Name="txtMiles" Grid.Row="1" Width="80" Grid.Column="2" Margin="2" Text="{Binding Miles, Mode=TwoWay}" HorizontalAlignment="Center" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<cmd:EventToCommand Command="{Binding UpdateJobCost}" CommandParameter="{Binding Text, ElementName=txtMiles}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
And my ViewModel:
public RelayCommand<string> UpdateJobCost { get; set; }
public WorkOrderControlViewModel(TSMVVM.Services.IWorkOrderService workOrderService)
{
WorkOrderService = workOrderService;
RegisterCommands();
LoadData();
}
private void RegisterCommands()
{
UpdateJobCost = new RelayCommand<string>((value) => updateJC(value));
}
private void updateJC(string value)
{
//Handle Setter service call here
}
Many thanks,
Scott
I haven't seen that problem before with EventToCommand. There might be something funky in your app that's causing the problem.
In general I don't rely on the UI to do the right thing. If updateJC shouldn't execute before a previous call has finished, consider adding an "isUpdatingJC" flag in your class. Only update the JC when the flag is false, and set it to true before you get started with the update. That way you don't get in a tight spot because some UI has issues.
Hope that helps...
Cheers!
The problem wasn't with updateJC firing async and not being complete when it fires again. I want it to only fire once. I ended up just creating a class for this form which contained a property for each of the fields. Whenever I update the property, I call updateJC which gathers the object & sends along for processing

XAML reference control and properties in x:Array

<RichTextBox x:Name="OrigText" Margin="0,0,8,0" d:LayoutOverrides="Width"/>
<Button x:Name="OrigFileBrowse" Command="{Binding BrowseCommand}" CommandParameter="{Binding ElementName=OrigText, Path=Document}" HorizontalAlignment="Center" Margin="0,0,8,2.442" Width="75" Content="Browse" Grid.Row="1" d:LayoutOverrides="Height"/>
<RichTextBox x:Name="ModifiedText" Grid.Column="1" Margin="8,0,0,0"/>
<Button x:Name="ModifiedFileBrowse" Command="{Binding BrowseCommand}" CommandParameter="{Binding ElementName=ModifiedText, Path=Document}" HorizontalAlignment="Center" Width="75" Content="Browse" Grid.Row="1" Grid.Column="1" Margin="0,0,0,2.442" d:LayoutOverrides="Height"/>
<Button x:Name="Compare" Command="{Binding CompareCommand}" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75" Content="Compare" Grid.Row="2" Grid.ColumnSpan="2">
<Button.CommandParameter>
<x:Array Type="RichTextBox">
<local:CompareTextView/>
</x:Array>
</Button.CommandParameter>
</Button>
Trying to get 2 items to be passed when the Compare button is clicked as it will then execute a compare command. Attempted to make use of MultiBinding however that is firing on instantiation and therefore the converter then fires accordingly. It does NOT fire when I click compare and the compare command is executed.
With that not working, I am attempting to now reference the controls within XAML to pass within an ArrayExtension. Not sure of the syntax or if it is even possible as I know you cannot bind within the ArrayExtension. The above fails since it can not construct a new CompareTextView view, which has no default constructor since I am making use of Prism...
Pretty frustrating, hopefully someone can help me out...
EDIT:
Want to clear some things up. The issue is not that I want CanExecute called again. The issue is that at instantiation of the controls, the converter is called and executed and the values are returned...but where they go I have no clue? The converter is never called again. If I could get the initial references to the FlowDocument this would all be a moot point...but it doesn't return things anywhere per se...since this is a command...if that makes sense...when making use of MultiBinding.
<Button x:Name="Compare" Command="{Binding CompareCommand}" HorizontalAlignment="Center" VerticalAlignment="Top" Width="75" Content="Compare" Grid.Row="2" Grid.ColumnSpan="2">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource FlowDocumentConverter}">
<Binding ElementName="OrigText" Path="Document"/>
<Binding ElementName="ModifiedText" Path="Document"/>
</MultiBinding>
</Button.CommandParameter>
</Button>
UPDATE:
Tried what refereejoe mentions here, scroll down a little bit to see his posting. While CanExecute continually fires, this does nothing to resolve the issue. In addition I switched the MultiBinding to be a single item, it is coming back null. Again when the converter fires on instantiation the FlowDocument references are there...
ANSWER:
Abe's mention that it was being cached led me to try something else. Since I knew that the FlowDocument references were available when the converter was called I knew they were there. Something was getting fouled up. The key piece appears to be in the converter itself. I was simply returning the object[]. Then when the command fired the arg was indeed an object[] but the two items were null. I created a class called Docs, which had two properties, one for each FlowDocument reference. When the converter fired I set the properties appropriately and then returned the Docs object. Now when I initiated the compare command, the Docs object was the args and it had the reference to the FlowDocuments just as I needed! Not sure if this is by design, but the fact that the items get lost when using the object[] doesn't make sense to me.
The proper way to do this is indeed with a MultiBinding on the CommandParameter. You won't see it call your CanExecute method unless WPF is informed that the method could return a different value than it had already cached (via the CanExecuteChanged event).
Since you are relying on the parameter passed in to determine this, we have to raise the event when the parameter changes. Since we can't really determine that in the command, we can use another technique: tell WPF to poll our command anytime it polls UICommands. This is done by implementing your ICommand like so:
public class MyCommand : ICommand
{
public void Execute(object parameter) { /* do stuff */ }
public bool CanExecute(object parameter { /* determine if we can do stuff */ }
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
Obviously, this prevents you from using the Prism DelegateCommand, but this will respond to changes in the command parameters.
UPDATE
Another thing to consider is that the Document property on the RichTextBox isn't actually changing. Instead, when you type into it, the content of the FlowDocument changes. Since the property instances don't change, the converter won't get fired again, and the originally converted value will get stored in the CommandParameter property.
One of the ways to force the converter to be called again is to add a Binding to the MultiBinding that is bound to a property that will change every time the text of the RichTextBox changes.
A somewhat hacky solution would be to use the IsKeyboardFocusWithin property, as that will mimic the default binding behavior of TextBox.Text (i.e. when the TextBox loses focus, the Binding updates):
<MultiBinding Converter="{StaticResource FlowDocumentConverter}">
<Binding ElementName="OrigText" Path="Document" />
<Binding ElementName="ModifiedText" Path="Document" />
<Binding ElementName="OrigText" Path="IsKeyboardFocusWithin" />
<Binding ElementName="ModifiedText" Path="IsKeyboardFocusWithin" />
</MultiBinding>
Obviously, in your converter, you will need to ignore these additional values, as they aren't relevant to your conversion.

Enable button based on TextBox value (WPF)

This is MVVM application. There is a window and related view model class.
There is TextBox, Button and ListBox on form. Button is bound to DelegateCommand that has CanExecute function. Idea is that user enters some data in text box, presses button and data is appended to list box.
I would like to enable command (and button) when user enters correct data in TextBox. Things work like this now:
CanExecute() method contains code that checks if data in property bound to text box is correct.
Text box is bound to property in view model
UpdateSourceTrigger is set to PropertyChanged and property in view model is updated after each key user presses.
Problem is that CanExecute() does not fire when user enters data in text box. It doesn't fire even when text box lose focus.
How could I make this work?
Edit:
Re Yanko's comment:
Delegate command is implemented in MVVM toolkit template and when you create new MVVM project, there is Delegate command in solution. As much as I saw in Prism videos this should be the same class (or at least very similar).
Here is XAML snippet:
...
<UserControl.Resources>
<views:CommandReference x:Key="AddObjectCommandReference"
Command="{Binding AddObjectCommand}" />
</UserControl.Resources>
...
<TextBox Text="{Binding ObjectName, UpdateSourceTrigger=PropertyChanged}"> </TextBox>
<Button Command="{StaticResource AddObjectCommandReference}">Add</Button>
...
View model:
// Property bound to textbox
public string ObjectName
{
get { return objectName; }
set {
objectName = value;
OnPropertyChanged("ObjectName");
}
}
// Command bound to button
public ICommand AddObjectCommand
{
get
{
if (addObjectCommand == null)
{
addObjectCommand = new DelegateCommand(AddObject, CanAddObject);
}
return addObjectCommand;
}
}
private void AddObject()
{
if (ObjectName == null || ObjectName.Length == 0)
return;
objectNames.AddSourceFile(ObjectName);
OnPropertyChanged("ObjectNames"); // refresh listbox
}
private bool CanAddObject()
{
return ObjectName != null && ObjectName.Length > 0;
}
As I wrote in the first part of question, following things work:
property setter for ObjectName is triggered on every keypress in textbox
if I put return true; in CanAddObject(), command is active (button to)
It looks to me that binding is correct.
Thing that I don't know is how to make CanExecute() fire in setter of ObjectName property from above code.
Re Ben's and Abe's answers:
CanExecuteChanged() is event handler and compiler complains:
The event
'System.Windows.Input.ICommand.CanExecuteChanged'
can only appear on the left hand side
of += or -=
there are only two more members of ICommand: Execute() and CanExecute()
Do you have some example that shows how can I make command call CanExecute().
I found command manager helper class in DelegateCommand.cs and I'll look into it, maybe there is some mechanism that could help.
Anyway, idea that in order to activate command based on user input, one needs to "nudge" command object in property setter code looks clumsy. It will introduce dependencies and one of big points of MVVM is reducing them.
Edit 2:
I tried to activate CanExecute by calling addObjectCommand.RaiseCanExecuteChanged() to ObjectName property setter from above code. This does not help either. CanExecute() is fired few times when form is initialized, but after that it never gets executed again. This is the code:
// Property bound to textbox
public string ObjectName
{
get { return objectName; }
set {
objectName = value;
addObjectCommand.RaiseCanExecuteChanged();
OnPropertyChanged("ObjectName");
}
}
Edit 3: Solution
As Yanko Yankov and JerKimball wrote, problem is static resource. When I changed button binding like Yanko suggested:
<Button Command="{Binding AddObjectCommand}">Add</Button>
things started to work immediately. I don't even need RaiseCanExecuteChanged(). Now CanExecute fires automatically.
Why did I use static resource in first place?
Original code was from WPF MVVM toolkit manual. Example in that manual defines commands as static resource and then binds it to menu item. Difference is that instead of string property in my example, MVVM manual works with ObservableCollection.
Edit 4: Final explanation
I finally got it. All I needed to do was to read comment in CommandReference class. It says:
/// <summary>
/// This class facilitates associating a key binding in XAML markup to a command
/// defined in a View Model by exposing a Command dependency property.
/// The class derives from Freezable to work around a limitation in WPF when
/// databinding from XAML.
/// </summary>
So, CommandReference is used for KeyBinding, it is not for binding in visual elements. In above code, command references defined in resources would work for KeyBinding, which I don't have on this user control.
Of course, sample code that came with WPF MVVM toolkit were correct, but I misread it and used CommandReference in visual elements binding.
This WPF MVVM really is tricky sometimes.
Things look much clearer now with the edits, thanks! This might be a stupid question (I'm somewhat tired of a long day's work), but why don't you bind to the command directly, instead of through a static resource?
<Button Command="{Binding AddObjectCommand}">Add</Button>
Since you are using the DelegateCommand, you can call it's RaiseCanExecuteChanged method when your text property changes. I'm not sure what you are trying to accomplish with your CommandReference resource, but typically you just bind the commands directly to the button element's Command property:
<TextBox Text="{Binding ObjectName, UpdateSourceTrigger=ValueChanged}" />
<Button Command="{Binding AddObjectCommand}" Content="Add" />
This would be the relevant portion of your view model:
public string ObjectName
{
get { return objectName; }
set
{
if (value == objectName) return;
value = objectName;
AddObjectCommand.RaiseCanExecuteChanged();
OnPropertyChanged("ObjectName");
}
}
Try raising CanExecuteChanged when your property changes. The command binding is really distinct from the property binding and buttons bound to commands are alerted to a change in status by the CanExecuteChanged event.
In your case, you could fire a check when you do the PropertyChanged on the bound property that would evaluate it and set the command's internal CanExecute flag and then raise CanExecuteChanged. More of a "push" into the ICommand object than a "pull".
Echoing Abe here, but the "right" path to take here is using:
public void RaiseCanExecuteChanged();
exposed on DelegateCommand. As far as dependencies go, I don't think you're really doing anything "bad" by raising this when the property that the command depends on changes within the ViewModel. In that case, the coupling is more or less contained wholly within the ViewModel.
So, taking your above example, in your setter for "ObjectName", you would call RaiseCanExecuteChanged on the command "AddObjectCommand".
I know this is an old question but I personally think it's easier to bind the textbox Length to button's IsEnabled property, e.g.:
<TextBox Name="txtbox" Width="100" Height="30"/>
<Button Content="SomeButton " Width="100" Height="30"
IsEnabled="{Binding ElementName=txtbox, Path=Text.Length, Mode=OneWay}"></Button>
If ElementName binding does not work, use:
<Entry x:Name="Number1" Text="{Binding Number1Text}" Keyboard="Numeric"></Entry>
<Entry x:Name="Number2" Text="{Binding Number2Text}" Keyboard="Numeric"></Entry>
<Button Text="Calculate" x:Name="btnCalculate" Command="{Binding CalculateCommand}" IsEnabled="{Binding Source={x:Reference Number1, Number2}, Path=Text.Length, Mode=OneWay}"></Button>
or use:
<Entry x:Name="Number1" Text="{Binding Number1Text}" Placeholder="Number 1" Keyboard="Numeric"></Entry>
<Entry x:Name="Number2" Text="{Binding Number2Text}" Placeholder="Number 2" Keyboard="Numeric"></Entry>
<Button VerticalOptions="Center" Text="Calculate" x:Name="btnCalculate" Command="{Binding CalculateCommand}">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding Source={x:Reference Number1, Number2},
Path=Text.Length}"
Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Button.Triggers>

Resources