I am new to WPF and am going through the examples of Professional WPF in .net 4.5. In the commands chapter, there is an example where multiple controls can send the same command. I am using a Button, CheckBox and MenuItem to trigger the New command.
The issue I am facing is that if MenuItem is pressed for the first time, the source shows correctly. However, after clicking the Button or CheckBox, then clicking MenuItem shows me the source of the last control Button or CheckBox, whichever was pressed. I couldn't find what was wrong with my code or why is this behavior shown by MenuItem in WPF.
Below is the code.
<Window x:Class="WpfApplication1.CommandSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CommandSample" Height="300" Width="300">
<Window.CommandBindings>
<CommandBinding Command="New" Executed="CommandBinding_Executed" />
</Window.CommandBindings>
<StackPanel>
<Button Command="New" MaxWidth="80" MaxHeight="30" Content="{x:Static ApplicationCommands.New}" />
<Menu MaxHeight="30" VerticalAlignment="Top">
<MenuItem Header="File">
<MenuItem Command="New"></MenuItem>
</MenuItem>
</Menu>
<CheckBox Command="New"></CheckBox>
</StackPanel>
</Window>
namespace WpfApplication1 {
public partial class CommandSample: Window {
public CommandSample() {
InitializeComponent();
}
private void CommandBinding_Executed(object sender,ExecutedRoutedEventArgs e)
{
MessageBox.Show("New Command launched by " + e.Source);
}
}
}
Yes this is correct (or at least that's how it's designed). Routed commands start routing based on the CommandTarget you specify. If one isn't specified typically the object raising the event uses itself as the starting point (so the MenuItem in this case). So the routing starts with the MenuItem in this case as you might expect. Nothing handles it there so the CommandManager goes up the parent chain. When it hits an element that is a FocusScope (like the Menu), it checks the FocusedElement of the "parent" FocusScope (e.g. the FocusScope of the parent of the Menu which in this case is the Window). If there is a FocusedElement (which there will be one once you have focused an element in the window's focus scope such as your button, checkbox, a textbox that you might put in that stackpanel, etc.) then the CommandManager starts routing the event from that element. When it does that it creates a new ExecutedRoutedEventArgs where the OriginalSource is that starting element (so the button, checkbox, textbox) and then continues routing up the tree.
So when you first ran the app, the FocusedElement of the Window (that's the root focus scope in your example) is null so there is no re-routing needed so the CommandManager just kept going up the parent chain past the Menu and that is why MenuItem appeared as the Source & OriginalSource. When you clicked on the Button you gave that keyboard focus and as part of it also became the logically focused element of its focus scope (i.e. the FocusedElement of its containing FocusScope). So when the MenuItem was subsequently clicked and the CommandManager ultimately reached the Menu, it then re-routed over to the Button (or whatever you focused in the window's focusscope) and started routing up from there. I say this is expected because with routed command you want the routing to go through the logically focused element so that for example, the Cut command of a menu item would trigger a cut of the TextBox in the Window that had focus.
Related
I have an interesting situation where I set the focus on a searchbox using: FocusManager.FocusedElement="{Binding ElementName=SearchBox}" which I found here: Set the focus on a textbox in xaml wpf
The good thing is that the search box is actually focused (I have a trigger that performs an action on focus), but I can't type into the textbox until I click on the textbox.
My textbox is very basic:
<TextBox x:Name="SearchBox"
Grid.Row="0"
style="{StaticResource SearchBox}" />
Any idea how I can allow the user to type right away as they open the control?
See the following link:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.input.focusmanager.focusedelement?redirectedfrom=MSDN&view=netframework-4.7.2#System_Windows_Input_FocusManager_FocusedElement
Especially the remarks:
The FocusedElement is the element which has logical focus for a specific focus scope. This object may or may not have keyboard focus. Keyboard focus refers to the element that receives keyboard input. For more information on focus, keyboard focus, and logical focus, see the Input Overview.
and:
https://learn.microsoft.com/en-us/dotnet/api/system.windows.input.focusmanager?redirectedfrom=MSDN&view=netframework-4.7.2
An element with logical focus does not necessarily have keyboard
focus, but an element with keyboard focus will have logical focus. It
is possible to define a focus scope within a focus scope. In this
case, both the parent focus scope and the child focus scope can have a
FocusManager.FocusedElement
In the following post you'll find an example how to handle this:
https://stackoverflow.com/a/20299923/9295125
you can also use this with <window> tag of the xaml page
<Window x:Class="WpfApplication30.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
FocusManager.FocusedElement="{Binding ElementName=SearchBox}">
<StackPanel>
<TextBox x:Name="textBox1"/>
<TextBox x:Name="SearchBox"
style="{StaticResource SearchBox}" />
</StackPanel>
</Window>
This is a MVVM WPF Question., c#.
Within a Window I have a Tab control that looks like this
<TabControl TabStripPlacement="Top" >
<TabItem Style="{StaticResource Tabitemstyle}">
<TabItem.Header>
<Label Content="Home" Style="{StaticResource Tablablestyle}"/>
</TabItem.Header>
<v:HomePageView/>
</TabItem>
<TabItem ....
<v:OtherPageView/>
The trick is that there exists a textbox within the 2nd tab item that I wish to have input focus when the user selects the 2nd tab.
I've tried a few solutions, but the closest one so far (using data trigger style, or focused element) almost works:
I can see that the cursor is intended to be within the text box, but it doesn't blink. It seems the focus is still on the tab control in the outside window, not the text box element in the view that is defined by OtherPageView.xaml. When I hit tab once, it is all ok, but this is what I am trying to relieve the users of having to do.
I would use the code behind:
Listen to visibility changed event on the TabItem content (i.e., v:HomePageView)
Find the textbox UI element (you can simply give the textbox a name
in the xaml and refer to it from code behind)
Next, set focus on the text box using the UIElement.Focus() method
Finally if the keyboard did not focus , then use the
Keyboard.Focus(...) method to focus the keyboard on the textbox.
Simplest application possible:
<Page
x:Class="TestApp.MainPage"
...>
<Grid>
<TextBox />
</Grid>
</Page>
Question: is there any elegant way to prevent the cursor (focus) from being set in the TextBox on application start up?
To expand: My real issue is that I have a PopUp that is opened when the TextBox receives focus. If I click on an element in my PopUp it should close, but since the TextBox is the first focusable element in my page it automatically receives focus and thus the PopUp immediately opens again. The core of the problem I think is represented by the example above.
Focus is managed by various properties like IsTabStop, TabIndex, IsHitTestVisible, and the FocusManager class. There is built-in functionality to focus the first focusable element once the window is activated, and this behavior is generally not customizable.
We could designate a different element to be focused instead of the textbox like, say, the page itself:
<Page IsTabStop="True">
<TextBox/>
</Page>
This works in that the page gets initial focus instead of the textbox, but now the page participates in tabbing behavior, which is slightly undesirable.
Typically the framework will set focus to the RootScrollViewer when you click out of a focused control, even though the RootScrollViewer isn't a tab stop (so it can't receive focus by tabbing). If we can focus the RootScrollViewer upon page load, the framework will detect that something has focus and won't attempt to focus the first element.
<Page Loaded="onPageLoaded">
<TextBox/>
</Page>
private ScrollViewer getRootScrollViewer()
{
DependencyObject el = this;
while (el != null && !(el is ScrollViewer))
{
el = VisualTreeHelper.GetParent(el);
}
return (ScrollViewer)el;
}
private void onPageLoaded(object sender, RoutedEventArgs e)
{
getRootScrollViewer().Focus(FocusState.Programmatic);
}
This is the most "elegant" way that I know to prevent the textbox from getting focused automatically.
In a WPF application, I want to have a User Tracking System to keep statistics on the way users are using the application. In other words, I'm looking for a way to track what commands are being executed and how they have been triggered by the user (by clicking on the toolbar button, by using keyboard shortcuts, etc). So far, I haven't found a nice way to do this while using the WPF command pattern...
Do you have ideas/suggestions on how to achieve/design something like this without overriding every control used in the application?
For discussion purposes, I created a very basic WPF application containing a toolbar with a single Save button, a TextBox and a ListBox. I also added a KeyBinding to trigger the Save command when pressing CTRL+S.
The first challenge is to determine which device (mouse or keyboard) was used to trigger the command.
The second challenge is to determine what is the control used to trigger the command (the command source). I'm not interested to know which control had keyboard focus when the command was triggered, I would like to know what control was used to trigger the command (usually it's a button, an hyperlink, a MenuItem from a ContextMenu, etc.)
MainWindow.xaml
<Window x:Class="TrackingCommands.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" x:Name="Me" Height="480" Width="600">
<Window.CommandBindings>
<CommandBinding Command="Save" Executed="OnSaveCommandExecuted" CanExecute="OnSaveCommandCanExecute" />
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Command="Save" Gesture="CTRL+S"/>
</Window.InputBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<ToolBarTray Grid.Row="0">
<ToolBar>
<Button Command="Save" Content="Save"/>
</ToolBar>
</ToolBarTray>
<TextBox Grid.Row="1" TextWrapping="Wrap" AcceptsReturn="True"/>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private void OnSaveCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
e.Handled = true;
}
private void OnSaveCommandCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
e.Handled = true;
}
}
EDIT
I realized my original question was a bit vague, I apologize. I will try to give more information and ask a more precise question.
I know it is simple enough to store a list of commands that have been executed. The challenge here is to retrieve which device was used to trigger the command initially: mouse or keyboard?
By putting the tracking logic in the "executed" handler, there is no way at this point to determine if the user triggered the command by clicking a button with the mouse, by pressing Enter on the button or if he used a keyboard shortcut. In my example, the same command can be triggered by clicking the toolbar button or by pressing CTRL+S on keyboard. How can I track these separate actions that will all trigger the same command?
Can this be achieve in the ViewModel layer? When we reach the command handler, it's already too late: we have lost this information. The only place we really know the device used is in the View itself. How to pass this information to the Command handler? Is the only way to do this is to override the Button control to intercept Click and KeyDown events in order to provide additional context to the command handler?
If you use the MVVM pattern then the Command would be bound from the View to a Command instance in the View Model. You could use create an ICommand implementation that provided an event when it was executed with some details about itself. Maybe use a command provider/factory/whatever to create each command and wire it up to a logger/tracker.
Create a Singleton or static class that has a Stack<ICommand> property and pass a reference to this class to your Windows (or preferably view models). You should of course encapsulate the Stack object using some typical AddCommand and RemoveCommand methods. Then, whenever an ICommand is called, Push it into the Stack.
However, you'll either need to define your ICommands in separate classes, or preferably use a form of the RelayCommand found online. Here's an example:
private ActionCommand deleteCommand = new ActionCommand(action => DeleteCommand(AudioTrack),
canExecute => CanDelete(AudioTrack));
public override ICommand Delete
{
get { return deleteCommand; }
}
private void DeleteCommand(AudioTrack audioTrack)
{
// Do work then add to Stack in CommandManager
CommandManager.AddCommand(deleteCommand);
}
private bool CanDelete(AudioTrack audioTrack)
{
return audioTrack != null;
}
I'm not exactly sure what your second question means, because the ICommands are set as the value to the Command property of the relevant control, so you should already know what controls they are, eg.:
<MenuItem Header="Delete track" Command="{Binding Delete}"
CommandParameter="{Binding Release.ThinDiscs.CurrentItem}">
<MenuItem.Icon>
<Image Source="pack://application:,,,/App;component/Images/Delete.png" />
</MenuItem.Icon>
</MenuItem>
I have a ContextMenu as part of a TabControl such as:
<TabControl Name="MyTabControl">
<TabControl.ContextMenu>
<ContextMenu Name="MyContextMenu" IsEnabled="False" StaysOpen="True">
<MenuItem Header="Item1"></MenuItem>
...
</ContextMenu>
</TabControl.ContextMenu>
</TabControl>
As you can see, the menu is disabled. It will be enabled later in the program, for now however, it should remain in its disabled state. The problem is, when I right-click the TabControl and the disabled menu shows, it simply stays where it was first opened, no other right-click will move it, nor will a left-click somewhere make it disappear.
The only way to get rid of it, would be either by enabling it and then right/left-clicking, or by using MyContextMenu.Visibility = Visibility.Collapsed/Hidden;
I tried setting the StaysOpen-property to False. Then the menu will open once in its disabled state. After left-clicking disappear and then not appear again even if it gets enabled.
The only way around it could be changing the StaysOpen-property along with the IsEnabled-property, but it is a bit weird that the menu opens exactly once in its disabled state and then not anymore.
I hope anybody could explain to me, why exactly a disabled menu won't close and the property StaysOpen at false makes it open exactly once, because it seems like a weird behaviour to me.
It seems that the behaviour of context menu items is quite strange - what you've described is in .Net 4.0 and if you target yor app to .Net 3.5 for instance you will notice the opposite behaviour - you can't make the menu stay opened if the single item is disabled, it just disappears immediately after it shows on right click.
However I think the preferrable way to manage the enabled state of a context menu item (and also the OnClick action it should do) is by a Command.
First you should specify a datacontext for your view, let's say it is the class ViewModel.cs. Then create a command by implementing the ICommand interface, something like this:
public class MyCommand : ICommand
And then you have a CanExecute method which does exactly what it's name says - decides if the command can be executed or not. And the enabled state of the menu item also depends on the return value of this method. So you can keep it returning false as long as you need in and the menu will behave correctly and the menu item will be disabled. Then when you want you can make it return true (by some logic in the method itself) and you'll have again a properly working context menu with enabled menu item.
If you want to disable the entire menu, use the same approach but for the menu.
And if you need to make all the items in the menu disabled (which I think is different from the entire menu), then you can use something like this:
<TabControl Name="MyTabControl" Background="Green">
<TabControl.ContextMenu>
<ContextMenu Name="MyContextMenu" StaysOpen="True" ItemsSource="{Binding Items}">
<ContextMenu.ItemTemplate>
<DataTemplate >
<MenuItem Header="{Binding Header}" IsEnabled="False" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</TabControl.ContextMenu>
</TabControl>
Here your ItemsSource (Items) is for instance List<MenuItem>, and MenuItem itselw would be your Model (thinking in MVVM) for each of your menu items, which should have property Header that contains the header you want to see in each menu item. Instead of setting the IsEnabled to false as in my dummy example, you can bind it to a property in order to have control on when it must be true and false.