Setting focus to textbox upon method-execution - c#

I have 2 different commands called; whom each perform some actions and reveal a form. I want them to set focus to the first textbox in that form when they set the Visible property to true.
I've seen all kinds of SO-articles on this subject, but I just can't seem to puzzle the pieces together. I'm working with MVVM, but all MVVM-solutions look pretty extensive for something that is just a QoL-improvement. I do have some code in my codebehind file, so I assumed I could just put it there and have a quicker/cleaner solution, but those I could mostly find for start-up focus.
I've messed around with the Focusmanager, but that doesn't seem to bring me anywhere either.
The tricky part of the whole construction is the following;
<ListBox Grid.Column="0" Grid.Row="1" Margin="5" IsEnabled="{Binding IsEnabled}" ItemsSource="{Binding DisabledConfigs}" SelectionMode="Extended" SelectedItem="{Binding SelectedConfig}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding SelectionChanged}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseDoubleClick">
<cmd:EventToCommand Command="{Binding EditConfig}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
The EditConfig-Command triggers a method that checks some properties of the double-clicked object, and either shows a form or not. When it does, the first textbox in that form should receive focus, otherwise nothing of importance happens.
I've been struggling with this stupid QoL-issue for my entire morning now, so I'm prepared to donate my left-kidney to whomever points me in a direction I can cleanly adopt ..
Edit: After a suggestion by AdminSoftDK I tried the following
// Auto-generated
private void nameBoxEdit_IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
if (nameBoxEdit.IsVisible)
{
// nameBoxEdit is (quite self-explanatory) the textbox that I want to focus on
nameBoxEdit.Focus();
}
}
I'm convinced this should be pretty close to the solution, but it's not working as is.

Huge shoutout to adminSoftDK for helping me out here
So I finally got the solution, which looks pretty weird to me, but it's working so I'm not complaining;
private void nameBoxAdd_IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
if(!nameBoxAdd.IsVisible)
{
nameBoxAdd.UpdateLayout();
// Task.Delay(500); abundant
nameBoxAdd.Focus();
// After testing some more, the Task.Delay(500) is not needed either.
// It's just the combination of UpdateLayout() and Focus()
}
}
I had quite an exstensive list of method-calls and property-checks in here to see if anything worked, an low and behold the focus was granted. I started filtering down, and for some reason the combined effort of UpdateLatyout() and the delayed task on Focus() made it work. Not either on of them (I tried having just one or the other which both put me on non-focus again), but the both of them.
Another weird thing to notice is that the IsVisibleChanged event triggers before actually changing the property. Something I ran across with the debugger..

Related

Intermittent error while using EventTriggerBehavior/InvokeCommandAction

Dеаr Colleagues,
I'm having issues with the following code:
<interactivity:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="BeforeTextChanging">
<core:InvokeCommandAction Command="{Binding ConfirmEmailCommand, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
</core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>
I want to emphasize, that the command triggers MOST of the times. The code works. The problem is, it sometimes doesn't. Command isn't called by the evens that otherwise will call it.
The behavior belongs to a Textbox inside a TemplatedControl, which exists inside a ContentDialog. I have similar code in other controls that are part of a ContentDialog and they all have such intermittent problems.
Does anyone have experience with such problems and can you elaborate on a possible fix? What order of control construction and property allocation could cause the Command to not be available for invocation during the event?

WPF Getting mouse coordinates within viewmodel

I have a WPF project (C#, MVVM Light, Visual Studio 2010).
I have a bit of a problem regarding separation of concerns (MVVM) which basically is this: I have a command in a view model. I have a context menu that I want to call that command. So far so good. The problem is that the command needs to coordinates that the mouse was clicked.
To be a little more specific, the ContextMenu only appears if you click on a particular Canvas control, and it's the coordinates within said Canvas control that I want.
The easy way to do this is to manage it all in the code behind of the XAML document (and I have been able to do it that way), but I'd rather have it within my ViewModel if I can do so. The reason is that there are calls to my data model within this command so we end up with a problem of separation.
I am aware of PassEventArgsToCommand, and I'm aware that it's a bad practise, however in this case I'm not sure I can see a way around it. So for the moment I did try that, and it looks like this:
<ContextMenu x:Key="BackgroundMenu">
<MenuItem Header="Add new node here">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.DataContext.AddNewNodeAtLocationCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</MenuItem>
</ContextMenu>
So now my command, within the view model, looks like this:
void AddNewNodeAtLocationExecute(RoutedEventArgs e)
{
return;
}
Within that method I'd like to get those mouse coordinates, but I don't know if it's possible. e.OriginalSource is 'MenuItem', which doesn't help much.
So how can I do this? Can I do this? Or should I just have this one command handled by the code behind? Said code will involve a call to the database, which is why I'm being so particular about the separation.
Thanks in advance.
Well I stumbled across this question which speaks about separation of concerns and what not.
In the end I did a merging of the two ideas I had. Firstly, the ContextMenu simply links to the code behind. At that point I get the coordinates I want. Then that code behind gets the DataContext of the view (where the command I want is) and calls the Execute method (having first checked the 'can' method).
I suppose it's as ideal as you're going to get.

CanExecute not raised when context menu opens

I know there are a handful of related questions but none of those helped me finding the issue.
Most answers suggest to implement CanExecuteChanged as shown in this answer. Well, that's not the solution to my problem. I've got an implementation of RelayCommand similar to Josh Smith's implemenation. (Similar, because our implementation adds more details but the core implementation is the same.)
While searching the Internet I also learned that if there is no focused element, the routing will stop at the ContextMenu and wouldn't reach the MenuItem. A solution that would help in that case is shown here.
However, I checked with Snoop if there really isn't any focused element and learned this is not the issue. And the fix didn't help anyway.
Besides, I simulated that issue in a test project and was able to fix it. So the fix generally works, it's just not helping me. I think there's still a chance, however, that I have to adapt the fix slightly to get it working. I tried MyControl instead of ContextMenu as AncestorType and I tried PlacementTarget.Tag instead of just PlacementTarget as Path but I wouldn't know what else to try to get it working (assuming that this is the bug).
Funny enough, it even doesn't work when I call CommandManager.InvalidateRequerySuggested() manually. I added a command that is raised on ContextMenuOpening. I thought that this would force the CanExecute to be executed but it seems I'm mistaken.
So, I'm now looking for further reasons why a CanExecute handler isn't raised when a ContextMenu is opened and how I would fix that.
Here's my XAML code (including EventTrigger for ContextMenuOpening):
<MyControl>
<MyControl.ContextMenu>
<ContextMenu>
<MenuItem Header="..."
Command="{Binding MyCommand}"
CommandParameter="{Binding}"
CommandTarget="{Binding Path=PlacementTarget,
RelativeSource={RelativeSource
AncestorType={x:Type ContextMenu}}}"/>
</ContextMenu>
</MyControl.ContextMenu>
<i:Interaction.Triggers>
<i:EventTrigger EventName="ContextMenuOpening">
<i:InvokeCommandAction Command="{Binding OnContextMenuOpening}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</MyControl>
Here's the definition of MyCommand and the (Can)Execute handlers:
internal static readonly ICommandEx MyCommand =
new RelayCommand(OnMyCommand, OnCanMyCommand);
private static void OnMyCommand(object parameter) { ... }
private static bool OnCanMyCommand(object parameter) { ... }
Here's my OnContextMenuOpening handler where I tried to force MyCommand's CanExecute to be raised:
private static void OnContextMenuOpening(object parameter)
{
CommandManager.InvalidateRequerySuggested();
}
You are incorrectly listening on OnContextMenuOpening on the ContextMenu control. It will never fire! Instead, listen on this very event on your MyControl control.

Unusual disabling behavior with Command buttons

I'm sorry for this being so wordy, but I want to make the situation perfectly clear. Please, if you are a WPF pro, take a look.
There are two CollectionViewSource bound to ItemsControls that use UserControl Views to display either StackPanels or custom Buttons. There is one for each side shown in the screenshot below. The problem I'm encountering is that when the parent collection property is set, all of the buttons in the DataTemplate view are disabled. Even the 2 buttons higher up are having the same problem even though they worked before my recent edits.
If I click on the form, or press any key, the buttons enable. As soon as the property is reset to a newly edited and sorted collection, they disable again. Rinse and repeat. This is what it looks like. The first frame is how it starts (gray using StackPanel), the 2nd is what it looks like when the RFS button is clicked, and the 3rd frame is what happens when I click anywhere or press a key.
I've been going in circles trying out things. The only thing that seems to work is a code-behind workaround that sets focus to one thing and then back. However, that would not be good for the user if they are trying to use one of the other dashboard items.
Since the WPF for all of this is very massive, I'll try to include just the relevant parts. These are the ItemsControls on the TabItemControl (UserControl).
<!-- BID (SELL) DEPTH -->
<ItemsControl Grid.Row="0" Grid.Column="0" x:Name="bidDepthList" ItemsSource="{Binding Source={StaticResource BidDepthCollection}}"
Visibility="{Binding Path=IsMultilegEnabled, Converter={StaticResource CollapsedConverter}}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewmodels:DepthLevelViewModel}">
<v:DepthLevelRowView x:Name="BidDepthLevelRowViewControl" DataContext="{Binding}" HorizontalAlignment="Center" Margin="1,0,1,3" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- ASK (BUY) DEPTH -->
<ItemsControl Grid.Row="0" Grid.Column="1" x:Name="askDepthList" ItemsSource="{Binding Source={StaticResource AskDepthCollection}}"
Visibility="{Binding Path=IsMultilegEnabled, Converter={StaticResource CollapsedConverter}}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewmodels:DepthLevelViewModel}">
<v:DepthLevelRowView x:Name="AskDepthLevelRowViewControl" DataContext="{Binding}" HorizontalAlignment="Center" Margin="1,0,1,3" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The view being used has 4 "controls" inside of a grid. Only one of them is displayed at a time depending on the state (RFS OFF/RFS ON) and which side they are on (Sell/Buy). The others are collapsed. As you can see, this is fine.
The only common factor between them is that they have their Command set, as do most of the controls at the top that are disabling/enabling correctly. The fact that the buttons enable correctly if any mouse or keyboard action is taken tells me that the CanExecute handler is working, just not immediately. The other controls started working after I made these changes, but then the big buttons started misbehaving like the depth buttons have been doing.
I've tried using CommandManager.InvalidateRequerySuggested(); after altering the collections, but that didn't help.
NOTE: This is also happening even for something as simple as this:
<Button x:Name="TestBuyButton" Command="{x:Static ptcommands:OrderCommands.Buy}" CommandTarget="{Binding RelativeSource={RelativeSource Self}}" Content="Test Buy" />
I know the booleans are set correctly because I added test CheckBoxes to display the current value with IsChecked. And, they all enable, including the extremely basic Button, as soon as any input action is taken.
Is there something else I'm missing or a different approach I can take?
EDIT: I ended up using the Dispatcher to invoke my display update routine from the event thread over to the UI thread. The boolean values get set, but WPF still didn't requery the Command. BUT.. the CommandManager.InvalidateRequerySuggested() call then worked! The only thing I don't like about that is it sounds like a broadcast invalidation. I don't want all commands to be requeried. I just want the Buy and Sell commands to requery. I've tried all sorts of weirdness trying to get just those to work, but nothing has worked so far other than the global.
EDIT 2: It appears as though the InvalidateRequerySuggested is the way to go.
From MSDN CommandManager.InvalidateRequerySuggested:
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.
This would explain why the focus changes and keypresses would cause the buttons to enable. The page also shows putting the call in a timer. Because of this, I assume it is not as resource intensive as I thought. So, I guess it's my permanent solution.
I had to make a few changes to get the buttons to enable without focus changes.
The boolean values were being set within a method called from an event thread. I had tried calling CommandManager.InvalidateRequerySuggested along with it, but nothing happened. Eventually I thought it might have something to do with the thread. This is what ended up resolving it.
In the parent form that the tab manager class (containing the event handlers and other logic) has access to, I added an invoke method:
Public Sub InvokeOnUiThread(ByRef uiAction As Action, Optional ByRef doAsync As Boolean = False)
Dim dispatchObject As Dispatcher = orderTicketView.Dispatcher
If (dispatchObject Is Nothing OrElse dispatchObject.CheckAccess()) Then
uiAction()
Else
If doAsync Then
dispatchObject.BeginInvoke(uiAction, DispatcherPriority.Normal)
Else
dispatchObject.Invoke(uiAction, DispatcherPriority.Normal)
End If
End If
End Sub
In the tab manager event handler, I changed it to call the update routine through the invoker:
formOrderTicketView.InvokeOnUiThread(New Action(AddressOf UpdateButtons))
At the bottom of the UpdateButtons method, I added a call to the CommandManager if a change has been made that would require a requery:
CommandManager.InvalidateRequerySuggested()
I do not know what type of performance hit this would have, but apparently WPF executes it in its own way when focus changes and such. Calling it directly is the advised way to force it.
From MSDN CommandManager.InvalidateRequerySuggested:
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.
Since it is working now, I am taking this as the resolution.
The command that's bound to the button...is there a CanExecute delegate with it? If so, you have to raise CanExecuteChanged when that delegate should be re-evaluated. Also, make sure the implementation of CanExecute isn't broken and incorrectly returning false;

WPF + Caliburn Micro: how to catch Window Close event?

I am new in Caliburn Micro and learn it from this helloworld example. In the example there are only 2 views (.xaml) of type Application and UserControl, and 1 view model.
I avoid to use code behind. Therefore I have only view and view model. I want to know how to catch the window close event of my helloworld application so I can handle it in view model. My target: when user is going to close the app by pressing close [x] button on top-right corner the app gives feedback to the user.
I have read about IViewAware and IScreen, but I find no specific example related to my question.
A simple sample code for view and view model are highly appreciated. Thanks in advance.
PS. I use VS2013, C#.
What you can do is in your View you can attach Caliburn Micro by using
cal:Message.Attach="[Event Closing] = [Action OnClose($eventArgs)]"
So it will look like
<Window cal:Message.Attach="[Event Closing] = [Action OnClose($eventArgs)]">
And on your ViewModel you can just define a public method that says OnClose with CancelEventArgs as the parameter and you can handle it from there.
If your ViewModel inherits Screen, Caliburn Micro has some methods that you can override like
protected override void OnDeactivate(bool close);
this is called when a screen is closed or deactivated or
public override void CanClose(Action<bool> callback)
you can check CanClose usage here
If you are using the BootstrapperBase class you can use:
protected override void OnExit(object sender, EventArgs e)
You're looking for a way to bind an Event to a Command. The typical approach here is to use the EventToCommand behavior from MVVMLight.
Example usage (from the linked article):
<StackPanel Background="Transparent">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<command:EventToCommand
Command="{Binding Main.NavigateToArticleCommand,
Mode=OneWay,
Source={StaticResource Locator}}"
CommandParameter="{Binding Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<!--...-->
</StackPanel>
For your specific scenario, you are not using MVVMLight. Since that framework is open-source, you could copy the implementation of EventToCommand into your own project, or - more simply - you can use the InvokeCommandAction, which is part of the System.Windows.Interactivity.dll library, included with Expression Blend.
Example of InvokeCommandAction:
<TextBox x:Name="TicketNumber">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<i:InvokeCommandAction Command="{Binding OpenTicketCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Lastly, this entire MVVM dogma that you "can't have any code behind" has been shot down time | and | time again (that last link is particularly relevant). MVVM is supposed to be unit-testable, and separates the "View logic" from the "Business logic." The "Close" event is admittedly a bit of a gray area between View and Business logic. But, if you can write an event handler in your code behind, which invokes your ViewModel's appropriate method or command, and if you can unit test that code, then you're as good as gold. Don't worry about removing all traces of code-behind from your project.

Categories

Resources