I have a program, where a button should only be active if the corresponding userControl has focus.
I am using MVVM light, and got a command implementing the ICommand interface.
I have tried using the Keyboard.FocusedElement, but this returns nothing.
This is the code for the command (Note that it just returns true right now to get it working, this sis of course what i'm trying to fix):
class AddItemToNodeCommand<T> : ICommand
{
public bool CanExecute(object parameter)
{
Debug.WriteLine("fokuselement er: " + Keyboard.FocusedElement);
return true;
// throw new NotImplementedException();
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
Debug.WriteLine("Parameter er: " + parameter);
Debug.WriteLine("fokuselement er: " + Keyboard.FocusedElement);
//throw new NotImplementedException();
}
}
From the viewmodel:
public ICommand AddItemToNodeCommand { get; private set; }
AddItemToNodeCommand = new AddItemToNodeCommand<object>();
And finally some of the XAML:
<RibbonButton SmallImageSource="../Images/whatever.png" Label="Attribute" Command="{Binding AddItemToNodeCommand}" CommandParameter="Attribute"/>
I haven't posted the xaml for the userControl, but the idea is that when the userControl has focus, the CanExecute should be true.. I thought it would work with the Keyboard.FokusedElement, but I was wrong. What can I do?
Thank you in advance.
Seems that Keyboard.FocusedElement is a bit fickly.
Have a look here for a solution involving attached behavior and overriding the Keyboard.GotKeyboardFocusEvent. I tried it, and it seems to work.
Otherwise, you can bind to IsKeyboardFocused or IsKeyboardFocusWithin. Just put this in your xaml for a quick example:
<StackPanel>
<TextBox Name="test_txtbx" >Hullo</TextBox>
<TextBox Name="test_txtbx_2">Hullo 2</TextBox>
<Label Content="{Binding ElementName=test_txtbx, Path=IsKeyboardFocused}"></Label>
<Label Content="{Binding ElementName=test_txtbx_2,Path=IsKeyboardFocusWithin }"></Label>
<Button Click="TestClick">test me </Button>
</StackPanel>
The label shows if the matching textbox has focus.
(Here's an old article which says focus is funny as well .. dunno if still relevant though).
Related
WPF project + Prism 7 + (Pure MVVM pattern)
Simple, I have TextBox which need to be cleared when some button is pressed (without the violation to the MVVM pattern)
<Button Command="{Binding ClearCommand}"/>
<TextBox Text="{Binding File}">
<i:Interaction.Behaviors>
<local:ClearTextBehavior ClearTextCommand="{Binding ClearCommand, Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
</TextBox>
ViewModel
public class ViewModel {
public ICommand ClearCommand { get; set; }
}
Behavior
public class ClearTextBehavior : Behavior<TextBox>
{
public ICommand ClearTextCommand
{
get { return (ICommand)GetValue(ClearTextCommandProperty); }
set
{
SetValue(ClearTextCommandProperty, value);
RaisePropertyChanged();
}
}
public static readonly DependencyProperty ClearTextCommandProperty =
DependencyProperty.Register(nameof(ClearTextCommand), typeof(ICommand), typeof(ClearTextBehavior));
public ClearTextBehavior()
{
ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
}
private void ClearTextCommandExecuted()
{
this.AssociatedObject.Clear();
}
}
The problem is the command in the ViewModel is always null (it did not bound to the command in the Behavior), Although I made sure that it is initialized in the behavior class.
NOTE: please do NOT suggest to set the File property to empty string, because this is just an example, In my real case, I need to select all the Text, so I really need an access to the AssociatedObject of the behavior
If i understood your Question correctly, you want to know why the ICommand in the ViewModel is not set to the DelegateCommand defined in the Behaviour.
The Problem is, that the ICommand and the DelegateCommand do not have a direct connection. I assume you may misunderstood how a Binding works and what happens by using those.
First of all, the ICommand is 'comes' from a Class and is therefore a reference Type.
Second, the reference to the ICommand is saved within the DependencyProperty ClearTextCommandProperty.
Third, by using a Binding in the XAML something like this happens as C# code:
Binding binding = new Binding();
binding.Path = new PropertyPath("ClearTextCommand");
binding.Source = ClearCommand;
BindingOperations.SetBinding(TextBox.ClearTextCommandProperty, binding);
Now the important thing: I don't know exactly which assignment comes first, but both lines will override the Value reference in the ClearTextCommandProperty!
//either here
SetBinding(TextBox.ClearTextCommandProperty, binding);
//or here
ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
//Which could be written as
SetValue(ClearTextCommandProperty, new DelegateCommand(ClearTextCommandExecuted));
At no point there is an assignment like this:
ViewModel.ClearCommand = SomeICommand;
Therefore it is Null, as #Andy mentioned
Edited to match select all Text
Additionally, i suggest you drop this complex stuff and use the full potential of the Interactivity Package like this:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<Button>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<utils:SelectAllText TargetName="TextToSelect"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<TextBox x:Name="TextToSelect" Text="{Binding File}"/>
And the SelectAllText
public class SelectAllText : TargetedTriggerAction<TextBox>
{
protected override void Invoke(object parameter)
{
if (Target == null) return;
Target.SelectAll();
Target.Focus();
}
}
If you take a look at this sample here:
https://social.technet.microsoft.com/wiki/contents/articles/31915.wpf-mvvm-step-by-step-1.aspx
You will notice that I have an ICommand there and it's set up to run a method.
If it was just an ICommand with a Get and Set like you have there then it would be NULL. There's a property but it's null until it is set to something.
This a very clunky way to implement an ICommand but relies on no external libraries or anything.
If you take a look at the second article in that series, it uses mvvmlight and relaycommand so creating a command is rather less clunky.
https://social.technet.microsoft.com/wiki/contents/articles/32164.wpf-mvvm-step-by-step-2.aspx
public RelayCommand AddListCommand { get; set; }
public MainWindowViewModel()
{
AddListCommand = new RelayCommand(AddList);
}
private void AddList()
{
stringList.Add(myString));
}
If you look at that code AddListCommand is initially null.
It is set in the constructor to a new RelayCommand which means it is then not null.
This is fairly simple but the code for the command is in a different place to the property so a more elegant approach is usual. As shown here: https://msdn.microsoft.com/en-gb/magazine/dn237302.aspx
Having said all that.
Selecting all text is something to do in the view, not the viewmodel.
You shouldn't really be passing a piece of UI from the view into a viewmodel.
Rather than a command it could well be that you should be binding a bool which is set in the viewmodel and acted on in the behaviour.
Let's say I have an user control with a button
<UserControl>
<Grid>
<Button x:Name="button" Content="Show Dialog" DataContext="{Binding m_btnShowDialog}" Style="{StaticResource ButtonVM}" Command="{Binding Cmd}" HorizontalAlignment="Left" Margin="29,56,0,0" VerticalAlignment="Top" Width="75" >
</Grid>
</UserControl>
The command class implements ICommand interface.
When the focus is on the above dialog, the CanExecute sometimes get called.
That is something to be expected.
The problem is when I click the button, the Execute method get called and a new modal dialog pops up.
The focus should be on the new dialog but for some reasons, the CanExecute still get called when I interract with the new dialog.
Is that behavior normal?
And how can I override that behavior?
I don't want CanExecute method of commands attached to controls of parent dialog to be called when a child modal dialog is showing up.
This is expected. Quoting directly from WPF expert Josh Smith :
WPF will automatically ask all of the commands being used in your UI
if they can execute. This happens at various times, such as when input
focus shifts to another control, an item is selected in a list, etc.
You can also programmatically trigger this to happen by calling the
CommandManager’s InvalidateRequerySuggested static method. This all
seems magical, dreamy, and almost too good to be true.
You can get more detailed and clear explanation here
You can override behavior using CanExecuteChanged event in your command implementation.
class MyCommand : ICommand
{
public bool CanExecute(object parameter)
{
return maybeTrueOrFalse;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
// Do something awesome.
}
}
Maybe the longest question title of all time! Because this is a two part question.
(1) I do not understand how setting NotifyOnValidationError="True" can trigger updates to my CanExecute. There is a bit of magic involved here that I need to understand. Someone(thing) subscribes to the CanExecuteChanged event of my ICommand but the call stack points to External code, so I can not figure out what is going on.
(2) Maybe the most important follow up questions is: Why does it not work in MVVM Light RelayCommand! The CanExecute is only called once at initialization and then never again. Looking at the source code for RelayCommand in MVVM Light does not reveal any chocking differences compared to my own implementation. I should mention that Prism's DelegateCommand does not seem to work either.
(Bonus) Maybee I am approaching this problem the wrong way? I just basically want to enable/disable buttons based on Validation failiures.
XAML (snippet):
<TextBox Grid.Column="1" Grid.Row="0">
<Binding Path="X" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
<Binding.ValidationRules>
<ExceptionValidationRule></ExceptionValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox>
<Button Grid.Column="1" Grid.Row="3" Command="{Binding CalculateCommand}">
Calculate
</Button>
RelayCommand:
public class MyRelayCommand : ICommand
{
readonly Action<object> Execute_;
readonly Predicate<object> CanExecute_;
public MyRelayCommand(Action<object> Execute, Predicate<object> CanExecute)
{
if (Execute == null)
throw new ArgumentNullException("No action to execute for this command.");
Execute_ = Execute;
CanExecute_ = CanExecute;
}
public bool CanExecute(object parameter)
{
return (CanExecute_ == null) ? true : CanExecute_(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
Execute_(parameter);
}
}
ViewModel:
private DelegateCommand _calculateCommmand;
public DelegateCommand CalculateCommand
{
get
{
return _calculateCommmand ?? (_calculateCommmand = new DelegateCommand(
() =>
{
Sum = X + X;
},
() =>
{
try
{
Convert.ChangeType(X, TypeCode.Byte);
return true;
}
catch
{
return false;
}
}));
}
}
PS: If you wanna buy my X + X program when it is done email me at sales#xplusx.com
(2) I figured this one out myself. You can choose to include RelayCommand from two different namespaces, make sure you use
using GalaSoft.MvvmLight.CommandWpf;
I am still looking for a good answer to (1), how the plumbing works that raises CanExecutetChanged based on validation error.
I don't think it depends on the ICommand implementation. In yours, I see a public event EventHandler CanExecuteChanged, where you tell the CommandManager to handle the invocation of your command's CanExecute() method. Without the CommandManager, you would have to handle this yourself, e.g. by providing your ICommand implementation with a public void RaiseCanExecuteChanged() method that your ViewModel has to call for every command it considers to be needed to recalculated, e.g. inside the ViewModel's OnPropertyChanged. Example: https://codereview.stackexchange.com/questions/124361/mvvm-am-i-doing-it-right
So the CommandManager does the magic for you. As soon as you invoke your ViewModel's PropertyChanged event, the "external code" handles the affected commands and asks them for a fresh CanExecute() value.
(1) I think it goes like this.
When we bind a RelayCommand : ICommand to Button.Command, the binding process will also attach an eventhandler to the ICommand.CanExecuteChanged. This is default behavior.
The eventhandler passed to CanExecutedChanged will be passed along and attached to the static event CommandManager.RequerySuggested.
When a validation error occurs and the NotifyOnValidationError is set an external force, or a jedi, will raise the RequerySuggested event which will broadcast to ALL active commands.
The Button recieves the event and consequently calls CanExecute to know if it shold disable/enable the button.
I would like to know more about the third bullet point above, so I will keep the question open for a little longer to give the experts a chance to chime in.
I am trying to figure out how I should bind the click action of a button in vb.net for WPF
Here is a section of WPF code
WPF:
<TabItem Name="tab_emailSender>
<TextBox Text="{Binding Path=address}" />
<Button Command="{Binding Path=sendTestMessage}"
</TabItem>
VB
Class MainWindow
Dim thisMessage as new Message
Private Sub main() Handles Me.Loaded
tab_emailSender.DataContext = thisMessage
End Sub
End Class
Class Message
Public Property address as string
Public Sub sendTestMessage()
msgbox("it worked!")
End Sub
End Class
I am able to bind the textbox's text but I am not sure how to bind the button's click event to the sendTestMessage sub.
In order to handle the button's click event, if you want to follow MVVM, you need to create an ICommand property on your ViewModel. ICommand is the inteface, so you can do something like this (sorry for C#):
public class Message: ICommand
{
public address { get; set; }
public bool CanExecute(Object parameter)
{
return true;
}
public void Execute(Object parameter)
{
//Do the logic here.
}
}
and the xaml (as far as your DataContext is the instance of Message type):
<Button Command="{Binding}" />
Of course you can implement the interface by yourself, on another class, etc. (I've made the implementation inside the Message class because its simplier here), but you can also find useful to use DelegateCommand or RelayCommand. This classes will prevent you from re-implementing the interface involved every time you need the new command.
The following RoutedCommand example works.
However, the handling for the button which executes the command is in the codebehind of the view. The way I understand MVVM, it should be in the ViewModel.
However, When I move the method to the ViewModel (and change it to public), I get the error "ManagedCustomersView does not contain a definition of OnSave". Even if I change the RoutedCommand second parameter to typeof(ManageCustomersViewModel), I get the same error.
How can I move the command handler from the View-codebehind to the ViewModel?
ManageCustomersView.xaml:
<UserControl.CommandBindings>
<CommandBinding Command="local:Commands.SaveCustomer" Executed="OnSave"/>
</UserControl.CommandBindings>
...
<Button Style="{StaticResource formButton}"
Content="Save"
Command="local:Commands.SaveCustomer"
CommandParameter="{Binding Id}"/>
ManageCustomersView.xaml.cs:
private void OnSave(object sender
, System.Windows.Input.ExecutedRoutedEventArgs e)
{
int customerId = ((int)e.Parameter);
MessageBox.Show(String.Format
("You clicked the save button for customer with id {0}.", customerId));
}
Commands.cs:
using System.Windows.Input;
using TestDynamicForm123.View;
namespace TestDynamicForm123
{
public class Commands
{
public static RoutedCommand SaveCustomer =
new RoutedCommand("SaveCustomer", typeof(ManageCustomersView));
}
}
You'll expose a property off your ViewModel that references the command.
class MyViewModel
{
public RoutedCommand SaveCmd{ get{ return Commands.SaveCustomer; } }
}
Then in the XAML
<Button Command="{Binding SaveCmd}" />
However you might find it easier to use the RelayCommand so that you can define the actual command logic in your model as well.