Notify user that command could not be executed - c#

I have a TextBox that is tied to a command like this:
<TextBox Text="{Binding Path=TextContent, UpdateSourceTrigger=PropertyChanged}">
<TextBox.InputBindings>
<KeyBinding Command="{Binding Path=MyCommand}" Key="Enter" />
</TextBox.InputBindings>
</TextBox>
The property TextContent is a string defined in the ViewModel. The command MyCommand is also defined in the ViewModel. The ViewModel does not know the View.
The command will be called whenever the TextBox has focus and the enter key is hit. Unfortunately, if CanExecute returns false, the user cannot see (visually) that the command was not executed, because there is no visual change in the TextBox.
I am looking for advice on how to show the user that the command could not be executed after he had pressed enter.
My ideas (and my doubts about them):
Disabling the TextBox when CanExecute returns false: This is no option because the return value of CanExecute can change everytime a letter is typed/changed (the text in the TextBox influences the outcome of CanExecute). When it is disabled for the first time, the user cannot type into it any more, so it will stay disabled forever.
Show a message box saying that the command was not executed: Remember, the ViewModel does not know the View. Is it even possible to open a message box from the ViewModel? Furthermore, where should I put the call to opening a message box? Not inside CanExecute because I only want to get the message box after hitting enter, not everytime CanExecute returns false. Maybe make CanExecute always return true and do the checks inside Execute: If checks are okay, do the command stuff, if not, show some message to the user. But then, the point of having CanExecute is missed entirely...
I want to keep MVVM, but some codebehind for redirecting stuff to the ViewModel seems okay for me.

I suggest the following solution.
Here's an example on how to notify the user which I'm working on at the moment.
I want the user to type in a data limit which is of type int, double or string.
It want to check so the user type in the correct type.
I use a property ValidateLimits which checks the string MyLimits which in your case is TextContent.
Everytime the user type in anything in the TextBox, ValidateLimits will check the string. If it is not a valid string in the textbox then return false otherwise return true.
If false then highlight it with the DataTrigger by setting some Properties on the TextBox which in my case is some Border and Foreground colors, also a ToolTip.
Also in your case you want to call your Validate method in your CanExecute method.
If you already have a function for checking that the command is OK then just add it to the DataTrigger binding.
<TextBox Text="{Binding MyLimit1, UpdateSourceTrigger=PropertyChanged}" Margin="-6,0,-6,0">
<TextBox.Style>
<Style TargetType="TextBox">
<!-- Properties that needs to be changed with the data trigger cannot be set outside the style. Default values needs to be set inside the style -->
<Setter Property="ToolTip" Value="{Binding FriendlyCompareRule}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ValidateLimits}" Value="false">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="ToolTip" Value="Cannot parse value to correct data type"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
public bool ValidateLimits
{
get
{
// Check if MyLimit1 is correct data type
return true/false;
}
}

Use a Property bool IsCommandExecuted in your Commandclass. Set this property accordingly.
Use a ToolTip and bind its IsOpen property to IsCommandExecuted property like this :
<TextBox ...>
<TextBox.ToolTip>
<ToolTip IsOpen="{Binding MyCommand.IsCommandExecuted}">...</ToolTip>
</TextBox.ToolTip>
</TextBox>
This explains the concept, modify it accordingly.

Related

How to always set focus to a button even click on some other textbox or other controls?

I need behavior like in the excel Find and Replace dialogBox that's why i asked like this. In this image you can able to see FindNext is always focused
I could able to set the focus to single element that is either Textbox or Button. But my requirement is always i need to set focus to Button even i click the some other controls within the window.
As I said in the comment, just set IsDefault="True" for your button will give you the result you want in the images you posted.
e.g.
<Button Content="Button" IsDefault="True" />
Result:
You can see the button will be highlighted even when user input into the textbox.
Not sure why you want to do this. But you can always set up a PreviewMouseLeftButtonDown on the Window, and check the e.OriginalSource property to see if it is a Control you want to give focus to. If it is not something you want to give focus to, simply set e.Handled to true.
But, seriously, it is still weird why you want to do this.
Edited because of your edited question
The "Find Next" button only visually looks focused. The focus is still given to the search TextBox or the replace TextBox. When you hit "Enter", the TextBox handles that KeyEvent, and executes the same Command as the "Find Next" button.
If you want to do something like that, you probably can consider this:
Add a property (probably bool ShouldButtonLookFocused) somewhere
Set up a Data Trigger in the button that binds to this ShouldButtonLookFocused property), set trigger value to true (e.g. <DataTrigger Binding="{Binding Path=ShouldButtonLookFocused}" Value="True">
Set the data trigger's setter to whatever visual changes you want to indicate this.
Then add bindings to which ever other TextBox controls, which when focused, you would want the button to "look focused". The binding should be to this ShouldButtonLookFocused property, and most likely you need to use a Converter for this.
Example:
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ShouldButtonLookFocused}" Value="True">
<!-- Whatever visual thing you can think of -->
<Setter Property="Border" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
public static readonly DependencyProperty ShouldButtonLookFocusedProperty =
DependencyProperty.Register("ShouldButtonLookFocused",
typeof(bool),
typeof(WhicheverClassThisIsIn));
public bool ShouldButtonLookFocused
{
get
{
return (bool)GetValue(ShouldButtonLookFocusedProperty);
}
set
{
SetValue(ShouldButtonLookFocusedProperty, value);
}
}
<TextBox Name="SearchBox"
IsFocused="{Binding Path=ShouldButtonLookFocused,
Converter={StaticResource MyConverter}"
/>
I did not test this though, maybe you can try it and tell me how it goes.

Call an event based on property value in WPF

I have a DataGrid and a button. I want to enable the button only when an entire row is selected. Otherwise it should be disabled.
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
//Here I want to enable the button which is outside the grid.
</Trigger>
</Style.Triggers>
</Style>
How can I do it?
I am not sure whether using a style trigger is a correct idea to solve your problem.
This issue can be solved using a delegate-event concept in your view model.
Create a property in your datagrid item source whose change raises an event. The handler of the event will in turn would set the button IsEnable property to true/false.
I have created a test application that would help you understand what I want to say.
Download it from : https://www.dropbox.com/s/hxzmpslt1bs0sei/WpfApplication2.rar?dl=0
try this and tell me if it did what you desired.

Accessing Validation Errors in a UserControl

I created a WPF (.Net 4) UserControl containing some ComboBoxes and a TextBox. In XAML, some ValidationRules are bound to the TextBox. If the TextBox contains invalid data, a red frame is shown, and the tooltip is set to the error description. Works well.
Next, I placed two instances of that UserControl on a form, and added a button. In XAML, the button is connected to a RelayCommand of the ViewModel. Now I want the button to be enabled only when both of the UserControls contain valid data only.
Not a problem, I thought, let me use a strategy which works elsewhere. I added a trigger:
<Button Content="_OK" ... Command="{Binding Path=OKCommand}">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="IsEnabled" Value="false" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=cascadingComboFrom, Path=(Validation.HasError)}" Value="false" />
<Condition Binding="{Binding ElementName=cascadingComboTo, Path=(Validation.HasError)}" Value="false" />
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="true" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
But there is a catch: Validation.HasError is always false for the UserControls - the Validation failed for an element inside the UserControl, not for the UserControl proper.
I know that I can register the Click event of the button, check the validity there using the method shown in Detecting WPF Validation Errors, and prevent the execution of the RelayCommand by setting the Handled property of the RoutedEventArgs to true. But that looks more like WTF than WPF.
What do you suggest? How can I retrieve the Validation Errors of the UserControl's children? Or how can I get them in the RelayCommand's CanExecute method? Or some other tricks?
You can set a property on the command binding called ValidatesOnDataErrors.
Implementation would look something like this:
<Button Content="_OK" Command="{Binding, Path=OKCommand, ValidatesOnDataErrors=True}"/>
You can read more about it here.
The Button.IsEnabled property is already hard wired to the CanExecute method of your RelayCommand, so all you need to do is to set that return value to false when the form fields are invalid:
private bool CanExecute(object commandParameter)
{
return areFormFieldsValid;
}
Now, how you set the bool areFormFieldsValid variable to true or false is up to you... there are several ways of doing that. Personally, I prefer to use the IDataErrorInfo interface, which has a handy Error property that you can check. There are many online tutorial on how to implement this, so I won't repeat that here... however, the end result is something like this:
private bool CanExecute(object commandParameter)
{
return string.IsNullOrEmpty(yourDataObject.Error);
}

What property is set on the parent window when a child window is shown using showdialog()?

I have a custom window and would like to apply a custom style to the window when it cannot be accessed due to it having a child window (displayed via showdialog()). I assumed that the trigger property would be "IsEnabled", however this property is not set to false when showdialog() is called. I have also tried "Focusable" and looked through the list of properties in hope of finding the obvious solution. This led to me trying a data trigger which binds to "OwnedWindows.Count" but again this doesn't work! Surely this should be simple and I am missing something?
Triggers tried:
<Trigger Property="IsEnabled" Value="False">
<Trigger Property="Focusable" Value="False">
<DataTrigger Binding="{Binding Path=OwnedWindows.Count, RelativeSource={RelativeSource Self}}" Value="1" >
Note: Both IsEnabled and Focusable do the required job when I manually set the properties to False - so I know the trigger works, they just aren't being set when ShowDialog() is called.
There is no property like that. Use ComponentDispatcher.EnterThreadModal and ComponentDispatcher.LeaveThreadModal events instead. They are fired when a WPF modal dialog is shown or closed, respectively.

Binding a RelayCommand AND an additional condition

I am trying to bind a RelayCommand's CanExecute in my main window to a child window that possibly does not exist. How should I do it?
Currently I have:
<MenuItem Header="_Compact"
Command="{Binding Path=CurrentChildViewModel.CompactCommand}"
IsEnabled="{Binding CurrentChildViewModel.CanExecuteCompactCommand,
Converter={StaticResource NullToBooleanConverter}}"/>
However this does not seem to work because the converter should work on CurrentChildViewModel (and not the CanExecuteCompactCommand, but I also should include that CanExecuteCompactCommand somehow.
I want the menu item to be enabled only if CurrentChildViewModel != null and CurrentChildViewModel.CanExecuteCompactCommand() returns true.
(reason: the CurrentChildViewModel is a window's ViewModel that can be opened or not, if it is not opened, I want the menu item to be disabled. And if it is opened, I want the Compact command's CanExecute method to check if the compact command can be executed, which is something like at least two items in the listview in the ChildView (Model) are selected.)
Can anybody help please?
if your converter need the instance of CurrentChildViewModel then bind to that and not the command (remove .CanExecuteCompactCommand)
That said why on earth are you using one command to determine if another command should be able to execute? You should utilize the CanExecute of your command (CompactCommand).
Ok I think I understand your actual problem now.
If I'm correct then your xaml/bindings work as expected unless either CurrentChildViewModel or CanExecuteCompactCommand is null. (assuming you remove your converter.)
To solve this you can add FallbackBalue=false to your binding, this tells the binding to use false when it cannot find the source. And also add TargetNullValue=false this tells the binding to use false when the source is null (CompactCommand in this case)
So it would look like:
IsEnabled="{Binding CurrentChildViewModel.CanExecuteCompactCommand,
FallbackValue=false,
TargetNullValue=false}"
That said I still would discourage the usage of a command to determine if another command can execute. I would do would do something like this:
e.g.
<Style TargetType="{x:Type MenuItem}" x:Key="menuItemWithCommand">
<Style.Triggers>
<Trigger Property="Command" value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</Trigger>
</Style.Triggers>
</Style>
...
<MenuItem Header="_Compact"
Style="{StaticResource menuItemWithCommand}"
Command="{Binding Path=CurrentChildViewModel.CompactCommand}" />
...
CompactCommand= new RelayCommand(CompactCommandExecuted, CompactCommandCanExecute);
private void CompactCommandExecuted(obejct obj)
{ // Do your business
}
private bool CompactCommandCanExecute(object obj)
{
// return true if the command is allowed to be executed; otherwise, false.
}

Categories

Resources