Binding a RelayCommand AND an additional condition - c#

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.
}

Related

Notify user that command could not be executed

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.

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.

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

Binding a MenuItem's Command via MVVM

I have a system set up where a ContextMenu hierarchy is populated dynamically using a MVVM architecture. All of my bindings function properly except for Command. My view is a ContextMenu that specifies an ItemContainerStyle.
The ContextMenu's ItemContainerStyle is set to this:
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Command, Mode=OneWay}"/>
<Setter Property="IsCheckable" Value="{Binding IsCheckable}"/>
<Setter Property="IsChecked" Value="{Binding IsChecked, Mode=TwoWay}"/>
<Setter Property="Header" Value="{Binding Label}"/>
<Setter Property="ItemsSource" Value="{Binding Children}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsVisible}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContextMenu.ItemContainerStyle>
So far, there is no ItemTemplate, as seemingly I have been able to accomplish all the desired functionality in the style.
The ViewModel must be constructed with an instance of the model it wraps, so it seems the DataContext of the ContextMenu cannot be explicitly set to the ViewModel (the compiler complains that it does not have a parameterless constructor. The complaint mentions that a type converter can also be used, though I am unsure as to what that actually means (could solve the issue).
The relevant pieces of my ViewModel are as follows, starting with the two following read-only public facing members that are available to be bound to:
public CommandBinding CommandBinding { get; private set; }
public RoutedCommand Command { get { return CommandBinding.Command as RoutedCommand; } }
CommandBinding and its command are instantiated in the constructor:
CommandBinding = new CommandBinding(new RoutedCommand(), CommandBinding_Executed, CommandBinding_CanExecute);
The methods referred to in that construction simply operate on members of the model, and are implemented as follows:
void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (ContextItem.Command != null) ContextItem.Command(ContextItem);
}
void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = ContextItem.IsEnabled;
if (ContextItem.ExecuteConditions != null) e.CanExecute = ContextItem.ExecuteConditions.GetInvocationList().Cast<ExecuteCondition>().All(s => s() == true);
}
It seems that when the binding to Command actually works, all of the items appear dimmed, as if CanExecute were returning false. However, when I set a breakpoint in CanExecute, execution never breaks at that point (though perhaps this is due to layout threading?). Even if I explicitly set e.CanExecute to true and comment out the other lines in CommandBinding_CanExecute, the items still appear dimmed. In XAML, I have tried binding to both the Command and CommandBinding members with and without Path=, all to the same effect. When I set the binding mode to OneWayToSource, the debugger appropriately complains that the property is read-only and cannot be operated on (I want the ViewModel to provide the command, so this is intended).
I have read the other examples and solutions to related issues. For those that follow the MVVM pattern, I cannot determine how my implementation differs.
For any solution, I must insist that I can still require the ViewModel to be constructed with the model as a parameter, and I would prefer the view to remain all XAML, with no C# code behind.
Seems the CommandBinding was the issue. I ended up creating my own implementation of ICommand that allows me to specify Execute and CanExecute delegates on construction... which worked perfectly.
This fixed the problem and the implementation was simple, but it is still unclear to me as to why my use of CommandBindings was not working.

MVVM Execute Command on ViewModel from other ViewModel

I'm struggling for about 14 days now with a simple task: In database, I have definitions for hardware categories. For example :
HDD
Internal
External
Flash
This list is in database defined like this:
[ID - ParrentID - Name] : 1 - 0 - HDD, 2 - 1 - Internal, 3 - 1 - External, 4 - 1 - Flash.
Through Entity Framework I get these rows into my application. From this flat data I then create structured object which is my DataModel. This model is defined as follows :
public class Category
{
private int _id = -1;
private string _name = "";
private List<Category> _subCategories = null;
// property getters and setters, constructors, and bool HasSubCategories
}
Now, from these I create ViewModel called SubCategoryViewModel to which is binded my TreeView. So, I can view my categories in treeview and with my defined and maintained hierarchy. This works just fine. In SubCategoryViewModel is defined a Command through Attached Behavior for MouseDoubleClick which is also binded to TreeView. So, when user doubleclicks on Item, in SubViewCategoryModel defined method will execute particular code. List of SubCategoryViewModel is nested in HWDocumentViewModel which is a main ViewModel for my window.
What I need now is obvious : When user doubleclicks on item in TreeView, I need to load items from database and show them in ListView. My opinion is, that in HWDocumentViewModel I need to define an collection of Items and load them accordingly to selected category in ListView. But, I don't know how to execute a method on HWDocumentViewModel from SubCategoryViewModel. Because : TreeView is binded to list of SubCategoryViewModel items, so when DoubleClick occurs, the method on SubCategoryViewModel is executed. I'm searching for a way, how to execute a method on main ViewModel (HWDocumentViewModel).
I tried this approach :
I created a property : public static SubCategoryViewModel SelectedCategory on HWDocumentViewModel. When doubleclick occurs, I set this property from SubCategoryViewModel as this. So, in this property is object, which executed doubleclick event delegate. Great, now I have in HWDocumentView model an object, which user selected.
So, I need to load items to ListView. But, will I load them from method in SubCategoryViewModel ? I don't think so. Instead I should load them from Main View Model by creating a ViewModel for them and bind it to ListView, right ? But, how can I from SubCategoryViewModel call a method in HWDocumentViewModel ? Should I write a static method
on a HWDocumentViewModel which will be accessible from SubCategoryViewModel ?
Or is there a way, how to call Command defined on HWDocumentViewModel from SubCategoryViewModel ?
Or generally, did I take a right approach to create a Warehouse-like application in WPF ?
Thanks a lot.
EDIT: XAML for my TreeView looks like this :
<TreeView x:Name="tvCategories" Background="White" ItemsSource="{Binding Categories}">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="behaviors:MouseDoubleClick.Command" Value="{Binding MouseDoubleClickCommand}" />
<Setter Property="behaviors:MouseDoubleClick.CommandParameter" Value="{Binding}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type localvm:SubCategoryViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding CategoryName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I'm not sure I see the problem. You have a tree of subcategories and when one is selected, the appropriate SubCategoryViewModel sets itself as SelectedCategory on the main HWDocumentViewModel. That seems like a reasonable approach.
So why do you need to call a command? Why can't you just load the new list in HWDocumentViewModel in response to a change of its SelectedCategory property (ie in the setter)?
If you really must use a command to invoke the load, then simply keep a reference to your main HWDocumentViewModel in each SubCategoryViewModel, and invoke the command with a simple:
_mainViewModel.LoadCategoryCommand.Execute();
With MVVM and trying to communicate between View and ViewModel or between ViewModels a publisher/Subscriber setup works well or a messaging paradigm like what's found in MVVMLight or Prism. I posted an answer on MVVM Light's messaging setup here
In the message you can send an object that holds any data you would like to send back and forth between the view models.
I highly recomend using a framework when working with mvvm it makes like much easier. MVVM Framework Comparison is a link to an answer that goes through a comparison of some of the major frameworks.

Categories

Resources