In my WPF application, I have a TextBox that has a KeyBinding and KeyTrigger on it, but it seems that the KeyTrigger is not invoked.
MainWindow.xaml
<Window
x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
Title="MainWindow" Height="350" Width="525">
<TextBox>
<TextBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding LogCommand}" CommandParameter="from KeyBinding"/>
</TextBox.InputBindings>
<i:Interaction.Triggers>
<ei:KeyTrigger Key="Return">
...
</ei:KeyTrigger>
</i:Interaction.Triggers>
</TextBox>
</Window>
Is there any way to invoke both of them? I want to do this because there is something I don't want to do in ViewModel.
Just put both in the interaction.Triggers part and remove the direct input bindings:
<TextBox>
<i:Interaction.Triggers>
<ei:KeyTrigger Key="Return">
<i:InvokeCommandAction Command="{Binding LogCommand}" CommandParameter="from KeyBinding"/>
...
</ei:KeyTrigger>
</i:Interaction.Triggers>
</TextBox>
Related
This question is in the context of using MVVM pattern. I have a NavigationView in my XAML but I have no way to detect when the user clicks on the back button. I could do it in the code behind file easily but that doesnt seems like what you want to do in an MVVM context. So is there a way to detect when the user presses the back button from a NavigationView outside of the code behind cs file? I would attach a Command Object to it and use my NavigationService where I will be able to switch the Frame. So far I use the Behaviors NuGet package to detect the clicks on the MenuItems but it doesnt work for the BackButton. Here is the Xaml so far:
<Page
x:Class="ToolBoxApp.Views.AudioHomeView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ToolBoxApp.Views"
xmlns:viewmodels="using:ToolBoxApp.ViewModels"
xmlns:mainview="clr-namespace:ToolBoxApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<NavigationView x:Name="navigationViewControl"
IsBackEnabled="true">
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="ItemInvoked">
<core:EventTriggerBehavior.Actions>
<core:InvokeCommandAction Command="{Binding NavigateToView}" />
</core:EventTriggerBehavior.Actions>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
<NavigationView.MenuItems>
<NavigationViewItem Icon="MusicInfo" Content="Text to Speech"/>
<NavigationViewItem Icon="MusicInfo" Content="Youtube to Mp3"/>
</NavigationView.MenuItems>
<ScrollViewer>
<Frame SourcePageType="{Binding ScrollAudioView, Mode=TwoWay}"/>
</ScrollViewer>
</NavigationView>
</Grid>
</Page>
Why don't you simply add another EventTriggerBehavior that invokes another command for the BackRequested event?:
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="BackRequested">
<core:EventTriggerBehavior.Actions>
<core:InvokeCommandAction Command="{Binding BackCommand}" />
</core:EventTriggerBehavior.Actions>
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="ItemInvoked">
<core:EventTriggerBehavior.Actions>
<core:InvokeCommandAction Command="{Binding NavigateToView}" />
</core:EventTriggerBehavior.Actions>
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
I have a UserControl, which has CommandBindings:
<UserControl x:Class="DbCreator.UserControls.Menu"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:DbCreator"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.CommandBindings>
<CommandBinding Command="SelectAll" Executed="SelectAllCommand_Executed"/>
<CommandBinding Command="local:MyCommands.ToggleNavigator" Executed="ToggleNavigatorCommand_Executed"/>
<CommandBinding Command="local:MyCommands.ToggleGrid" Executed="ToggleGridCommand_Executed"/>
<CommandBinding Command="local:MyCommands.SelectNone" Executed="SelectNoneCommand_Executed"/>
</UserControl.CommandBindings>
<Menu Background="White">
<MenuItem Header="_Файл"/>
<MenuItem Header="_Правка">
<MenuItem Header="Выделить всё" Command="SelectAll"/>
<MenuItem Header="Убрать выделение" Command="local:MyCommands.SelectNone"/>
</MenuItem>
<MenuItem Header="_Вид">
<MenuItem x:Name="ToggleNavigator" Header="Скрыть навигатор" Command="local:MyCommands.ToggleNavigator"/>
<MenuItem x:Name="ToggleGrid" Header="Скрыть сетку" Command="local:MyCommands.ToggleGrid"/>
</MenuItem>
<MenuItem Header="_Диаграмма"/>
<MenuItem Header="_Справка"/>
</Menu>
When I click MenuItem, my Executed method is fired, but shortcuts not working. When this UserControl content was a part of MainWindow code, all shortcuts worked perfect. But I want to define menu in a separate file.
So the problem was that Menu was unfocused and didn't recieve input. Solution is to add InputBindings to MainWindow:
<Window.InputBindings>
<KeyBinding Command="SelectAll" CommandTarget="{Binding ElementName=Menu}" Key="A" Modifiers="Control"/>
<KeyBinding Command="local:MyCommands.SelectNone" CommandTarget="{Binding ElementName=Menu}" Key="A" Modifiers="Control+Shift"/>
<KeyBinding Command="local:MyCommands.ToggleNavigator" CommandTarget="{Binding ElementName=Menu}" Key="N" Modifiers="Alt"/>
<KeyBinding Command="local:MyCommands.ToggleGrid" CommandTarget="{Binding ElementName=Menu}" Key="G" Modifiers="Alt"/>
</Window.InputBindings>
I've looked into several methods of setting focus and nothing appeared to work. I'm sure someone out there has a solution to this. It's such a simple task.
I want to set the focus of the textbox which appears in the context menu when the user right-clicks on the listbox. I don't want the user to have to click the textbox each time they right-click.
MainWindow.xaml
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox>
<ListBox.ContextMenu>
<ContextMenu>
<ContextMenu.Template>
<ControlTemplate>
<Border BorderThickness="2" BorderBrush="sc#1,.1,.1,.1" CornerRadius="4"
Background="sc#1,.05,.05,.05">
<TextBox Grid.Row="0" Margin="4" MinWidth="150" Name="SearchBox" VerticalAlignment="Center">
</TextBox>
</Border>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
</Grid>
</Window>
IsFocused property of TextBox is read only. This forces the use of method in our case.
You need CallMethodAction behavior. Good tutorial to start with.
<TextBox Grid.Row="0" Margin="4" MinWidth="150" Name="SearchBox" VerticalAlignment="Center">
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger Binding="{Binding IsOpen, RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}">
<ei:CallMethodAction MethodName="FocusSearchBox" TargetObject="{Binding DataContext, ElementName=SearchBox}"/>
<ei:ChangePropertyAction PropertyName="Background" Value="Purple"/>
</ei:PropertyChangedTrigger>
</i:Interaction.Triggers>
</TextBox>
public void FocusSearchBox()
{
TextBox t = (TextBox) CtxMenu.ContextMenu.Template.FindName("SearchBox", CtxMenu.ContextMenu);
t.Focus();
}
I have a WPF Window containing a TabControl, which has Frames in it which display Pages. Like this:
<Window x:Class="MyNamespace.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<DockPanel>
<TabControl x:Name="TabControl">
<TabItem Header="StartScreen" x:Name="StartScreenTab">
<Frame Source="StartScreenPage.xaml"/>
</TabItem>
<TabItem Header="OtherTab" x:Name="OtherTab">
<Frame Source="OtherPage.xaml"/>
</TabItem>
</TabControl>
</DockPanel>
</Window>
In one of the pages, I have a KeyBinding:
<Page x:Class="MyNamespace.View.OtherPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Title="OtherPage">
<Page.InputBindings>
<KeyBinding Key="U" Modifiers="Control" Command="{Binding MyCommand}"/>
</Page.InputBindings>
<!-- Content ... -->
</Page>
MyCommand is a property of OtherPage.DataContext, it's only available from the page, not available from outside.
My problem is that the KeyBinding only works after I click on a control inside the Page. I want that KeyBinding to work whenever OtherPage is visible, equivalently when OtherTab is the active tab. How can I achieve this?
The only way that you can achieve that is to move your KeyBinding and the ICommand implementation to the MainWindow.xaml file:
<Window.InputBindings>
<KeyBinding Key="U" Modifiers="Control" Command="{Binding MyCommand}"/>
</Window.InputBindings>
If you can't move the actual implementation of the ICommand, then you must at least make it accessible from there.
I did something similar to what Sheridan recommended in his answer.
In MainWindow.xaml I declare a RoutedUICommand:
<Window x:Class="MyNamespace.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<Window.Resources>
<ResourceDictionary>
<RoutedUICommand x:Key="OtherPageCommand"/>
</ResourceDictionary>
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="{StaticResource OtherPageCommand}"
Executed="OtherPageCommandExecuted"/>
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Key="U" Modifiers="Control" Command="{StaticResource OtherPageCommand}"/>
</Window.InputBindings>
<DockPanel>
<TabControl x:Name="TabControl">
<TabItem Header="StartScreen" x:Name="StartScreenTab">
<Frame Source="StartScreenPage.xaml"/>
</TabItem>
<TabItem Header="OtherTab" x:Name="OtherTab">
<Frame Source="OtherPage.xaml"/>
</TabItem>
</TabControl>
</DockPanel>
</Window>
In MainWindow.xaml.cs I have this method:
private void OtherPageCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
var page = ((Frame)((TabItem) TabControl.SelectedItem).Content).Content as OtherPage;
if (page != null)
{
var viewModel = page.DataContext as MyViewModel;
if (viewModel != null)
{
var openWindow = viewModel.MyCommand;
var parameter = null; // put your command parameter here
if (openWindow.CanExecute(parameter))
openWindow.Execute(parameter);
}
}
}
This way I propagate the keybinding to the page but the actual command remains in the viewmodel of the page.
I bind my textboxes to ViewModel class. But, button command (it's a RelayCommand, extended from ICommand) I bind to UsersView.xaml.cs. In UsersView.xaml.cs constructor I have this:
DataContext = UserVM;
btnAdd.DataContext = this;
This is how I bind button - it works.
<Button Command="{Binding Add}" Content="Add user" />
Now, I want to add KeyGesture for that button but I can't set DataContext for InputBindings and compiler can't find this Add command in UsersVM class.
<UsersView.InputBindings>
<KeyBinding Key="F10" Command="{Binding Add}" />
</UsersView.InputBindings>
I had this on a Window and this is the code I used...
<Window
x:Class="MVVMExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myViewModels="clr-namespace:MVVMExample"
Title="MainWindow"
x:Name="MyMainWindow"
Height="350"
Width="525">
Notice that I set the x.Name of the Window. Then in my KeyBinding, I did this...
<Window.InputBindings>
<KeyBinding
Key="F10"
Command="{Binding ElementName=MyMainWindow, Path=DataContext.AddPersonCommand}" />
</Window.InputBindings>
The AddPersonCommand is my ICommand from my ViewModel.