How to handle shortcuts and keyboard events properly? - c#

Got a problem with short cuts, any help/tips would be much appreciated!
Goal: I need to be able to handle short cut keys, with and without modifiers in my app.
So for instance i need to handle a key 'a' as well as 'CTR+a'. But I want to handle them only if no control handles these keys. For instance TextBox class takes most keys, including some commands like 'Ctrl+C' etc, so I don't want to intercept these events when TextBox will handle them.
I tried using Commands as well as attaching events to KeyUp to the Window, but, commands intercept keys before the TextBox gets a chance to view them, KeyDown bubbles to the Window level even if TextBox used the key! How can I get my window to get the keys that are NOT handled by any child control? Please see the code below that did not work for me. Also, since i have many different controls, I rather have a "proper" solution: I rather not attach handlers to each instance of a control in my window.
<Window x:Class="KeyTest.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">
<Window.CommandBindings>
<CommandBinding Command="Help"
CanExecute="HelpCanExecute"
Executed="HelpExecuted" />
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Command="Help" Key="H" />
</Window.InputBindings>
<Grid>
<WrapPanel>
<TextBox Name="myLOG" Width="300" Height="200" Background="LightBlue" />
<TextBox Name="myINPUT" Width="300" Height="200" />
<Button Content="JUST FOR FUN" />
</WrapPanel>
</Grid>
And for C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace KeyTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private void HelpCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
myLOG.Text += "HELP CAN EXECUTE\n";
e.CanExecute = true;
e.Handled = true;
}
private void HelpExecuted(object sender, ExecutedRoutedEventArgs e)
{
myLOG.Text += "HELP EXECUTED!!!\n";
e.Handled = true;
}
public void myKeyUpHandler(Object sender, KeyEventArgs args)
{
myLOG.Text += "KEY UP EVENT! " + args.Key + "\n";
}
public MainWindow()
{
InitializeComponent();
this.KeyUp += new KeyEventHandler(myKeyUpHandler);
}
}
}
When the focus is in text box, pressing "h" triggers the command, even though i want 'h' to only go to the text box. Also, when inside textbox, pressing any alpha numeric key fires the KeyUp event, even though as far as I understand, textbox should have handled=true that event!
Thanks for help!

You need to investigate using the preview event types. They happen before other controls will handle the events. Then you want to stop the events bubbling. I belive you are doing that correctly with e.Handled.
Investigate this: http://msdn.microsoft.com/en-us/library/system.windows.input.keyboard.previewkeydown.aspx
Not sure how to do it in xaml for what you want to do. The expressions blend libraries can be quite helpful for spawning commands off of events. See here: http://jacokarsten.wordpress.com/2009/03/27/applying-command-binding-to-any-control-and-any-event/

Dude I think you need to use previewKeyDown or PreviewKeyUp Event instead of a keyup event here because PreviewKeydown and PreviewKeyup Event produce tunneling effect(OPPOSITE of bubbling effect where events where fired starting from the RootParent of the control which fired the event down the to control which originally fired the event(also know as original source)). you can take advantage of this tunneling effect for handling events rather that using events which are fired via bubbling effect. Another thing is that PreviewKeyDown and PrevieKeyup events where fired before keydown events occur. This could let you intercept the event in the cleanest way.
Another thing, I think you need to check the original source of the event so that you could select the controls that could fire this events.
Here is a sample code
public void nameOfCotrol_PreviewKeyDown(object sender, RoutedEventArgs e)
{
if((e.OriginalSource as Control).Name == "NameOfControls That would be allowed to fire the event")
{
You're stuff to be done here
}
else
{
e.handled = true;
}
}
I hope this could help in a small way. Thanks

Related

WPF C# user control with 2 or more buttons

Sorry if this has been asked before and I have spent about a week trying to find a similar question to point me in the right direction. I am teaching myself C# with WPF, XAML etc and am playing around with user controls. I made a simple app with a user control to load on top of other windows or user controls. The UC in question has two buttons and I need to get to the click events for each button in main window once the control is loaded. The main window has a button that loads the control.
Through some research I was able to find a solution from user SWilko (https://stackoverflow.com/a/28949666/10659981) but I can't figure it out for each button separately (click button a and show "clicked btn a", click button b and show "clicked button b"). I did try calling by sender using name and that will not work either. I feel like I am close with the help from the answer by SWilko but stuck.
Here is the code so far:
Basic main screen loading user control
<Window x:Class="UCBTN_TEST.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:UCBTN_TEST"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="435">
<Grid>
<Button Content="Load Button" HorizontalAlignment="Left" Margin="18,23,0,0" VerticalAlignment="Top" Width="74" Click="Button_Click"/>
<Grid x:Name="GridLoad" HorizontalAlignment="Left" Height="300" Margin="120,23,0,0" VerticalAlignment="Top" Width="300" Background="#FFF1CBCB"/>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace UCBTN_TEST
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
GridLoad.Children.Clear();
GridLoad.Children.Add(new WindowControl());
}
}
}
The button user control
<UserControl x:Name="UCMain" x:Class="UCBTN_TEST.Controls.ButtonControl"
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:UCBTN_TEST.Controls"
mc:Ignorable="d" d:DesignWidth="300" Height="40.333">
<Grid Background="#FFE7EEA7">
<Button x:Name="ButtonA" Content="Button A" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Click="ButtonA_Click" Background="Red"/>
<Button x:Name="ButtonB" Content="Button B" HorizontalAlignment="Left" Margin="215,10,0,0" VerticalAlignment="Top" Width="75" Click="ButtonA_Click" Background="Green"/>
</Grid>
</UserControl>
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace UCBTN_TEST.Controls
{
/// <summary>
/// Interaction logic for ButtonControl.xaml
/// </summary>
public partial class ButtonControl : UserControl
{
public ButtonControl()
{
InitializeComponent();
}
private void ButtonA_Click(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(ClickEvent1, this));
}
public static readonly RoutedEvent ClickEvent1 = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ButtonControl));
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent1, value); }
remove { RemoveHandler(ClickEvent1, value); }
}
}
}
A second user control which would ultimately have some other controls once the buttons work correctly. But the button UC will load on top, simple button features related to WindowControl.
<UserControl
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:UCBTN_TEST"
xmlns:Controls="clr-namespace:UCBTN_TEST.Controls" x:Class="UCBTN_TEST.WindowControl"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="#FFE7CFEE">
<Controls:ButtonControl HorizontalAlignment="Left" Height="37" VerticalAlignment="Top" Width="300" Click="Click1"/>
</Grid>
</UserControl>
I understand the behind code and why this is happening. My problem is that I need to have the buttons be unique in their events. I have tried calling by sender and name and that just kills the event all together.
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using UCBTN_TEST.Controls;
namespace UCBTN_TEST
{
/// <summary>
/// Interaction logic for WindowControl.xaml
/// </summary>
public partial class WindowControl : UserControl
{
public WindowControl()
{
InitializeComponent();
}
private void Click1(object sender, RoutedEventArgs e)
{
MessageBox.Show("This triggers both");
}
}
}
I was going to add a bunch of comments but really this is kind of answering the question and there's a lot to explain.
You should look into MVVM and mostly be thinking in terms of binding commands rather than which button was clicked. There are exceptions to this. For example, if you were building an on screen keyboard. The reason this is different because it's purpose can be encapsulated. The user presses a button which has "A" in it. Whatever textbox is focussed should be sent the character "A". They press a button showing "B" and similarly "B" should be sent. That functionality can be encapsulated in the control.
As it is, you have two buttons.
You put them in a usercontrol and encapsulate them.
By doing this you created a boundary.
This then creates a complication - which was clicked?
The usercontrol is also not particularly re-use friendly. If you add two then there are two buttonA and two button B. You could potentially improve that with a custom event args on your custom routed event and a dependency property on your usercontrol. Pass some usercontrol identifier along with which button was pressed.
This would be an unusual way to work though. I've rarely seen Custom routed events used in commercial apps.
All in all I would suggest the usercontrol mainly adds complexity.
Say you wanted to have 20 sets of 2 buttons.
Or 20 sets of 5 radiobuttons for a set of multiple choice questions.
The way to do that sort of thing is to use an itemscontrol and template out the multiple controls. One template giving 2 buttons ( or a textblock question and 5 radiobuttons for answers ) per row.
A click event is already a routed event and would bubble to the window. You may as well remove your custom routed event and the handler out the usercontrol... and the usercontrol. Just handle click in the window.
Code:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var btn = e.OriginalSource as Button;
if(btn == null)
{
return;
}
MessageBox.Show($"Button clicked was {btn.Tag}");
}
Markup:
ButtonBase.Click="Button_Click"
Title="MainWindow"
>
<Grid>
<StackPanel>
<Button x:Name="ButtonA" Content="Button A" Tag="A" Background="Red"/>
<Button x:Name="ButtonB" Content="Button B" Tag="B" Background="Green"/>
</StackPanel>
</Grid>
</Window>

Wpf event not bubbling

Here is my XAML:
<Window x:Class="WpfApplication4.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="844.025" Width="678" MouseUp="somethingClicked">
<Grid MouseUp="somethingClicked">
<StackPanel MouseUp="somethingClicked" Margin="0,0,10,0">
<Button x:Name="btnClickMe" Content="Click Me!" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="101,22,0,0" MouseUp="somethingClicked"/>
<CheckBox x:Name="chkhandle" Content="CheckBox" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="241,28,0,0" RenderTransformOrigin="-0.588,1.188"/>
<ListBox x:Name="lstEvents" HorizontalAlignment="Left" Height="604" VerticalAlignment="Top" Width="416" Margin="29,66,0,0"/>
</StackPanel>
</Grid>
And here is the C# Code:
namespace WpfApplication4
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
protected int eventCounter = 0;
public MainWindow()
{
InitializeComponent();
}
private void somethingClicked(object sender, RoutedEventArgs e)
{
eventCounter++;
String message = "#" + eventCounter.ToString() + ":\r\n" +
" Sender: " + sender.ToString() + ":\r\n" +
" Source: " + e.Source + ":\r\n" +
" Original Source: " + e.OriginalSource;
lstEvents.Items.Add(message);
e.Handled = (bool) chkhandle.IsChecked;
if (e.Handled)
lstEvents.Items.Add("Completed");
}
}
}
I have the following issues with this example:
1)The MouseUp event is not fired on clicking the button.
2)The event doesn't bubble up. Clicking somewhere on the form displays:
Sender:WpfApplication4.MainWindow:
Source:WpfApplication4.MainWindow:
Original Source: System.Windows.Controls.Border.
If I understand rightly, when button is clicked, first it should be executed at Window level (which it does now), then Grid, then stack and finally text label. Is the code wrong or is my understanding of the concept faulty?
The MouseUp event is not fired on clicking the button.
Because the first fires is an event at the Button.Click, and when it works, it conflicts with the event MouseUp. Quote from here:
ButtonBase inherits from UIElement, a Button will also have access to all of the mouse button events defined for UIElement. Because the Button does something in response to button presses, it swallows the bubbling events (e.g. MouseLeftButtonDown and MouseDown). You can still detect these lower level button press events by adding handlers for the tunneling events (e.g. PreviewMouseLeftButtonDown and PreviewMouseDown).
Try to replace the Button on Label, and you'll get the desired result:
Try handling the PreviewMouseDown event instead. You can still
attach that from XAML. In your handler
Attach the event handler in code instead. Use the signature of
AddHandler
.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Grid1.MouseUp += new MouseButtonEventHandler(Grid1_MouseUp);
}
private void Grid1_MouseUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("Mouseup");
}
Microsoft wrote a very nice explanation Routed Events Overview
exactly the same thing will happen with MouseUpand PreviewMouseUp events
in your case the e.Handled = (bool) chkhandle.IsChecked; stops the routing of the event.
if you want to debug the events you can use Snoop it will illustrate very nicely which events happened on which objects and who handled them.
There is an override available to handle events, even though they were marked as handled. It requires that you add your handler through code as the following:
MainWindow.AddHander(UIElement.MouseUpEvent, new MouseButtonEventHandler(button1_MouseUp), true);
That last parameter specifies whether you want to accept events that were handled already or not. If you add that handler to your main window, you'll notice that the routed MouseUp events from your button are indeed bubbling up, (but their e.Handled indicates that they were already handled).

Ignore repetition in KeyBinding (holding the key - execute command just once)

I have keyboard shortcuts declared in my xaml using KeyBindings.
I would like to ignore repetitions due to key holding in few of them.
I have found only solutions using events and checking "IsRepetition", which doesnt really fit in my declaration of the keybindings.
Of course I could do it in the Command definition itself and measure a time difference between 2 last executes, but this gives me no way to differentiate multiple presses and 1 key holding.
What would be the best way to execute only on the first press and ignore the rest if the key is hold?
You are trying to change a behavior of the button. Better to use code for that.
The easiest way is to attach a preview event to the window like that:
<Window
...
PreviewKeyDown="HandlePreviewKeyDown">
Then in code handle it like that:
private void HandlePreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.IsRepeat)
{
e.Handled = true;
}
}
Sadly this would disable any repeat behavior, even in a textbox hosted by the form. This is an interesting question. If I find a more elegant way of doing this, I will add to the answer.
EDIT:
OK there are two ways to define Key Binding.
<Window x:Class="WpfApplication1.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">
<Window.InputBindings>
<KeyBinding x:Name="altD" Gesture="Alt+D" Command="{Binding ClickCommand}"/>
</Window.InputBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Content="_Click" Command="{Binding ClickCommand}" />
<TextBox Grid.Row="1"/>
</Grid>
</Window>
The above button will generate a click because you implicitely requested the Alt-C gesture via the underscore: _Click content. Then the window has an explicit keybinding to Alt+D.
This code behind should now work for both cases and should not interfere with regular repeat:
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
if (e.IsRepeat)
{
if (((KeyGesture)altD.Gesture).Matches(this, e))
{
e.Handled = true;
}
else if (e.Key == Key.System)
{
string sysKey = e.SystemKey.ToString();
//We only care about a single character here: _{character}
if (sysKey.Length == 1 && AccessKeyManager.IsKeyRegistered(null, sysKey))
{
e.Handled = true;
}
}
}
}
I would say if you create a very simple state machine of sorts that would take action on the KeyBinding on a KeyDown event and would ignore all other input until a KeyUp event is fired to give the KeyBinding a "one-shot" behavior.
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.keyup.aspx
Use the keyUp method instead of KeyDown.

How to properly update settings from menus

If I have a user setting ToggleThis
I want to have this setting availabe to the user in a menu, say Settings/ToggleSettings. clicking it. Each click should toggle the user setting true/false but also update the menuItem icon to display the actual setting.
I can do this using
XAML
<Window x:Class="WpfApplication1.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">
<Grid>
<Menu IsMainMenu="True">
<MenuItem Header="_Settings">
<MenuItem Header="_Toggle" Name="ToggleSettings" Click="MenuItem_Click">
<MenuItem.Icon>
<Image Source="Images/Toggle.png" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
</Grid>
</Window>
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
if (ToggleSettings.Icon == null)
{
Uri Icon = new Uri("pack://application:,,,/Images/" + "Toggle.png");
ToggleSettings.Icon = new Image
{
Source = new BitmapImage(Icon)
};
Properties.Settings.Default.toggleThis = true;
}
else
{
ToggleSettings.Icon = null;
Properties.Settings.Default.toggleThis = false;
}
}
}
}
However, I know this isn't the correct way of doing it as for example, on launch the menu probably won't be in the correct state based on previous settings. Trouble is, I don't know the right way. Can anyone give me some pointers on the right way to do this?
I'm assuming I need to use binding on both the icon and/or some value in the MenuItem but don't really know where to start.
Thank you
Actually there is no "right" way, there is only what works best, and what is most appropriate given the context.
Your example looks fine, it seems, so far at least, the only issue you have, is that you will not have the selected option in sync with what the user chose/didn't choose, the last time they used the software.
This requires only two small pieces of your code to be in two particular places.
Austin pointed out one of them already: Save your settings. This you should do right after your if/else in your method: MenuItem_Click. Just make sure the method does not exit somehow before the call to Settings.Save is made... a try/catch with a graceful way of ensuring a consistent settings state would be prudent.
The other is at the "time" you yourself mentioned: Initialization, or startup of the app. Somewhere in your app, before the initial loading is completed, you must access the setting you created (toggleThis) and use it to set the initial state of your menu item.
The best way to facilitate this, is to have a private method, which is responsible for both changing what icon is showing on the menu item, as well as storing the most recent state in the settings of the app. A method called Toggle() perhaps, which you call within your MenuItem_Click method. You need to give the menu item in question and ID though, that can be used to access the menu item in your code-behind though. As well, this code example assumes you have your icons stored in settings as well, although the icons can be coming from wherever, as long as you can reference them.
So your code could be something like this, although not exactly this:
public MainWindow()
{
InitializeComponent();
this.SetToggleIcon(Properties.Settings.Default.toggleThis);
}
private void Toggle()
{
this.StoreToggleState(!Properties.Settings.Default.toggleThis);
this.SetToggleIcon(Properties.Settings.Default.toggleThis);
}
private void SetToggleIcon(bool state)
{
this.menuItem_ToggleSettings.Icon = (Properties.Settings.Default.toggleThis) ? Properties.Settings.Default.IconTrue : Properties.Settings.Default.IconFalse;
}
private void StoreToggleState(bool state)
{
Properties.Settings.Default.toggleThis = state;
Properties.Settings.Default.Save();
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
this.Toggle();
}
You need to call Save when you deem it appropriate.
Properties.Settings.Default.Save();
It's not entirely clear how you're using it but this will ensure the updated value is at least stored.

WPF Application wide capture of key up/down events

I'm trying to capture keypress events anywhere in my WPF application, regardless of which UI element has the focus. Currently I'm having no luck. Can anyone suggest some strategies that I might not have tried? Or, ideally, provide an answer like "oh that's easy, you just do this".
It's a distributed application, which has a chat system. The effect that I'm looking for is that the user can start typing a chat message at any time, without switching to a standard chat box. I'll display their message in the application myself, using FormattedText objects. This is important because it means there are no text input elements in the application anywhere.
My XAML structure looks roughly like:
<MainWindow>
<Canvas 1>
<Canvas 2>
<Image 1 />
</Canvas 2>
<Image 2 />
</Canvas 1>
</MainWindow>
I programmatically add elements into Canvas 2, and manipulate Image 2, which is why it has that structure.
I've tried adding KeyDown, KeyUp and the Preview events to MainWindow and Canvas 1, but none of them seem to fire (I check with breakpoints). I've also, after reading another related question here, tried manually setting the focus on the main window in the Loaded() method.
I realise there are many related questions on this site, but they haven't helped me because:
there aren't any answers (will my question be answered?)
they assume a text entry widget and are interested in bubbling up events
they want a keybinding for a small number of keys - I would like to capture any key
they are interested in detecting if a control/shift/alt key is down after they've already captured the event
Thank you for taking the time to read my long winded post, and thank you for suggestions.
Update (After Rachel's comment) When I put in a TextBox and set the focus to the TextBox, a key event method at the MainWindow level will fire. So that works as advertised.
However, I would really like to not have an explicit text entry widget in the application at all. I would like the user to be able to just start typing to compose a message.
A little bit of tinkering got me this:
XAML:
<Window x:Class="KeyInput.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="100" Width="225">
<Canvas>
<Grid>
<Label Name="test" Content="Empty" />
</Grid>
</Canvas>
</Window>
CS:
using System.Windows;
using System.Windows.Input;
namespace KeyInput
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.KeyDown += new KeyEventHandler(OnButtonKeyDown);
}
private void OnButtonKeyDown(object sender, KeyEventArgs e)
{
test.Content = test.Content + e.Key.ToString();
}
}
}
This prints out stuff like "Shift" So you'd obviously have to use switches... but it has a Text Box that collects key presses.
I managed to work it out, inspired by answers from Rachel and WernerCD. As they both demonstrated, having the event capture at the MainWindow level does work. The problem was that I neglected to mention that I had a dialog before the MainWindow loaded, which seems to interfere with normal keyboard focus on the MainWindow. Putting explicit Keyboard.focus() in the Loaded() method is too soon. I fixed the problem by putting Keyboard.focus() in the MainWindow_ContentRendered() method.
All is now well (until the next issue anyway). Thank you for the help.
I usually add a PreviewKeyDown event to the MainWindow.
Perhaps your problem is you don't have any control that accepts keyboard focus on your application. Do you get the same results if you add a TextBox to the MainWindow and have focus set there?

Categories

Resources