WPF InputManager KeyDown not handled - wpf

First of all I already have a working solution I just need some explanation on what is happening here.
I have the following code:
var e = new KeyEventArgs(Keyboard.PrimaryDevice,
Keyboard.PrimaryDevice.ActiveSource,
Environment.TickCount,
Key.D1)
{
RoutedEvent = Keyboard.KeyDownEvent
};
InputManager.Current.ProcessInput(e);
The purpose of this code is to add the value 1 to a currently focused textbox (= Keyboard.PrimaryDevice.ActiveSource).
But if I execute this code the text which is contained in the textbox wont change. But the routed "KeyDown" event is executed.
If I change the code from above to this, it is working:
var eventArgs = new TextCompositionEventArgs(Keyboard.PrimaryDevice,
new TextComposition(InputManager.Current,
Keyboard.FocusedElement, "1"))
{
RoutedEvent = ContentElement.TextInputEvent
};
InputManager.Current.ProcessInput(eventArgs);
Now my question is, why is it not working with the first example. In my understanding if the control receives a KeyDown Event the result should be that the text is changed? The interessting part is if I want to send special key events like Enter or Backspace I HAD to use code example 1 code example 2 wont work there.
This is a little bit tricky and I thought maybe someone can enlighten me?
Thank you in advance

Related

wpf textbox won't throw textInput event

I have a window with a textbox that is not throwing textInput events when typing.
I have been looking at it with Snooper. Only the KeyDown and KeyUp events get thrown.
It IS responding to a few keys: Space, backspace, Insert, home, delete, end
It responds to copy & paste commands, as well as Select All
It is NOT responding to any character, symbol or number
And here's the kicker: This window is opened via a shared method that is called from two different places in the code. When called from one location the textbox works perfectly, when called from other location it doesn't.
I have ruled out binding, data converters, styles, control location.
I stripped the window down to just a single plain textbox with no bindings, and the problem persists.
I've tried all I can think of to track down this mysterious bug. I can't see what could be handling my event before the previewTextInput even gets throw, or why it could possibly be only occurring half the time.
Any ideas about the cause of this bug, or other tools I could try to trace the events would be greatly appreciated!
Edit: Adding some code to demonstrate. This has been stripped down to the barest code required, and the issue is still occurring.
<Window x:Class="EventViewEmail"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="880" Height="600">
<TextBox VerticalAlignment="Top"/>
</Window>
notice the lack of bindings, styles, or anything else that may be interfering the control
Public Class EventViewEmail
'all code removed from the view-behind'
End Class
this is the static class that builds the window. The two separate calls to this class build the arguments differently. I've removed the code that uses the arguments to show that they aren't affecting the issue at hand.
Public Class EventManager
Public Shared Sub Show(e As EventEdit, p As WorkplanPageViewModel)
Dim w = New EventViewEmail
If w Is Nothing Then Return
'removed datacontext for testing'
'w.DataContext = e '
'w.Tag = p'
w.Show()
End Sub
End Class
The only other thing I can add is that the code calling the Show() sub are coming from two separate solutions. Not sure what impact that could possibly have after I've removed all the arguments
Edit 2:
I have been trying to trace the events sequence to narrow down where the event is getting handled. So far I can see that between the keyDown and keyUp events, there is a sequence of events that should be happening that isn't:
PreviewInputReport / InputReport (no source)
PreviewTextInputStart / TextInputStart (textbox)
PreviewTextInput / TextInput (textbox)
PreviewInputReport / InputReport (textboxView)
the keydown event isn't being handled, so I'm not sure why the PreviewInputReport is not getting fired
Disclaimer
I'll post C# code because I'm not fluent enough in VB to write code (only to read it in a limited scope). I'll try to translate it if I find some time, or anyone of you can feel free to do it.
I have managed to reproduce the behavior you described by handling the PreviewTextInput event up the visual tree on the window (remember it's a tunneling routed event):
<Window (...)>
<StackPanel>
<TextBox x:Name="myTextBox"></TextBox>
</StackPanel>
</Window>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
PreviewTextInput += (s, e) => e.Handled = true;
}
}
So in my opinion it's a strong suspect for the cause of your problems. It also explains the comment to #AQuirky's answer that your PreviewTextInput handler is not called. You can confirm that that's the case by subscribing differently (the handler is called even if the event was marked as handled):
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
PreviewTextInput += (s, e) => e.Handled = true;
myTextBox.AddHandler(
routedEvent: PreviewTextInputEvent,
handler: new TextCompositionEventHandler((s, e) =>
{
if (e.Handled)
throw new Exception("Gotcha!");
}),
handledEventsToo: true);
}
}
If this turns out to be correct diagnosis, then there's one thing that's somewhat mysterious - who and where does handle the PreviewTextInput event? I guess that's for you to investigate...
This comment from #l33t might come in handy:
Look for global event handlers like EventManager.RegisterClassHandler(typeof(TextBox), UIElement.PreviewTextInputEvent, ...) and/or behaviors. Are you using any third-party UI library? Look in their code as well.
Update
Since it seems like PreviewTextInput is not the culprit after all, here's what I'd do next.
If I'm not mistaken, there's a whole chain of events being fired prior to TextInputEvent, and I believe handling any of those can break the chain. It also looks like InputManager is responsible for managing this event cycle (you can inspect its C# source code here).
That being said I suggest doing the following:
Using InputManager.Current subscribe to its PreProcessInput and/or PostProcessInput events (optionally also PreNotifyInput and PostNotifyInput)
Record the events chain in the working scenario (especially inspect the args' StagingItem.Input.RoutedEvent, which holds the routed event currently being processed)
Repeat the procedure for the not working scenario
Pinpoint the first difference - the first routed event being processed in the working scenario, but not being processed in the not working scenario
Investigate both last common routed event and the first one that's different - perhaps one of those is handled in your code?
In this case it turned out that the form that launched the window in the not working case was a winform. The winform was blocking the keystrokes. This was fixed by opening the window as a modal.
I had the same problem here. The difference between the two calls was: one time the WPF-Windows was called modal (works) and one time nonmodal (doesnt work), everytime from a WindowsForms Window.
As Shaboboo wrote in his anwer, this is the difference, but what, if you need to call nonmodal.
The answer is given this thread. (stupid me, I had the same problem 2 years ago, asked and got an answer). We have to use
ElementHost.EnableModelessKeyboardInterop(wpfWindow);
Thanks again to V.Leon
The problem is that while the TextInput is being fired (not thrown...exceptions are thrown) you are not seeing it because the text box itself is using it. To catch this event you need to use the PreviewTextInput. So for example
<TextBox TextInput="UIElement_OnTextInput" PreviewTextInput="UIElement_OnPreviewTextInput"></TextBox>
With event handlers...
private void UIElement_OnTextInput(object sender, TextCompositionEventArgs e)
{
Console.WriteLine($"In text input event, {e.Text}");
}
private void UIElement_OnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
Console.WriteLine($"In preview text input event, {e.Text}");
}
The first handler is never called, the second one always.
Your handler is not called because other handlers have marked the event as handled. You can add an handler with AddHandler() and pass true for the third argument:
public MainWindow()
{
var handler = new RoutedEventHandler(OnTextInput);
myTextBox.AddHandler(TextInputEvent, handler, handledEventsToo: true);
}
private static void OnTextInput(object sender, RoutedEventArgs e)
{
}

How to prevent silverlight validation popup from appearing half off the screen

I have a silverlight 4 application which has some text boxes that are as wide as the page.
When there is a validation error, a popup is displayed when the user clicks in the control.
The problem is - it only shows the popup for these long text boxes to the left of the text box. It wont go above or below and so as a consequence, most of the popup is displayed out of the page so its chopped off.
I know that I can re-template the text box and try to adjust the popup myself, but before doing that I just wanted to check to see if someone knew of a simple property or something that I can use to prevent that from happening?
Cheers
Rod.
Good question. I guess I would try to solve this via a somewhat "intelligent" AttachedProperty.
Pseudo code ahead:
<TextBox ... my:PopupUtils.KeepPopupWithinScreen="True"/>
And the (pseudo c#) code:
public static class PopupUtils
{
// remember: pseudo code, just to get the idea
static AttachedProperty KeepPopupWithinScreen = type: bool, default: false,
onChanged: HandleKeepPopupWithinScreenChanged;
private static void HandleKeepPopupWithinScreenChanged(
DependencyObject obj, bool value)
{
obj.Loaded += HandleTargetElementLoaded;
}
private staic void HandleTargetElementLoaded(object sender, ...)
{
var popup = VisualTreeHelper.GetDecendantOfType<Popup>(sender);
if ( popup != null )
{
var offsetController = new OffsetController();
offsetController.SetBinding(ObservedOffsetProperty,
new Binding("HorizontalOffset"){Source=popup});
offsetController.ControlledTarget = popup;
//now to prevent garbageCollection...
SetAttachedOffsetController(popup,offsetController);
}
}
public static AttachedProperty AttachedOffsetController = type:OffsetController;
}
I do this sometimes, so this pattern is actually working quite nicely. Maybe it feels a bit "unnatural" at first.
Just letting you know of the solution I used to this problem.
It was another StackOverflow question which I have lost the reference to so I do apologise for not referencing it properly, but the problem is caused by the Text Box and other control styles having a fixed position of to the side of the control when displaying the validation message.
I simply had to create a copy of the style and have the popup appear at the top of the controls instead of beside it.
Problem Solved.
Cheers
Rod.

Force binding in WPF

I'm writing tests which will check correctness of Binding elements specified in XAML. They work so far, the only issue is that I do not know how to correctly force databinding to happen. Surprisingly it is not enough to simply set something in DataContext, binding won't happen until you show your control/window. Please not that I'm writing 'unit'-tests and I'd like to avoid showing any windows.
Take a look at following code:
// This is main class in console application where I have all WPF references added
public class Program
{
[STAThread]
public static void Main()
{
var view = new Window();
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
view.DataContext = new int[5];
//view.Show(); view.Close(); // <-- this is the code I'm trying not to write
Console.WriteLine(view.Title);
}
}
Here I'm creating a Window and putting an array as DataContext to that window. I'm binding Window.Title to Array.Length so I expect to see number 5 printed in console. But until I Show window (commented line) I will get empty string. If I uncomment that line then I will receive desired 5 in console output.
Is there any way I can make binding happen without showing a window? It is pretty annoying to look at ~20 windows while launching tests.
P.S.: I know I can make windows more transparent and etc, but I'm looking for more elegant solution.
UPDATE Code above is simplified version of what I really have. In real code I receive a View (some UIElement with bindings) and object ViewModel. I do not know which exactly binding there were set on View, but I still want all of them to be initialized.
UPDATE 2: Answering to the questions regarding what I test and I why. I do not intend to test that classes like Binding, BindingBase, etc are working as expected, I assume they are working. I'm trying to test that in all my XAML files I have written bindings correctly. Because bindings are stringly typed things, they are not verified during compilation and by default they cause only errors in output window, which I'm missing occasionally. So if we take my example from above and if we will made a typo there in binding: {Binding Lengthhh} then my tests will notify you that there is no property with name Lengthhh available for binding. So I have around 100 XAML files and for each XAML I have a test (3-5 lines of code) and after launching my tests I know for sure that there are no binding errors in my solution.
The bindings are updated by the dispatcher with the DispatcherPriority.DataBind - so if you wait for a dummy task with SystemIdle priority you are sure that any pending databinding is done.
try
{
this.Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));
}
catch
{
// Cannot perfom this while Dispatcher in suspended mode
}
If you are trying to test correctness of your view, I suggest you test your view :-)
Why not run the UI from a unit test and write code that checks content of UI after changing data.
VS2010 does have GUI testing, or you could take a look at the code of tools such as Snoop.
Edit following comment:
If ALL you want to do is test a few simple bindings, try writing a static code test that runs as a post build event using reflection on view models and regular expressions on XAMLs. Add attributes on VM or use a config file so your test will know which view receives which View Model as DataContext. Compare property names and types in View Models with binding strings in View (automatically search XAML for these) and throw exception (thus failing build) if strings do not match.
If your bindings are more complex (converters, multibindings, ...) this may be a bit more complicated to implement.
I think you should first set the DataContext and then do the Binding, e.g.:
view.DataContext = new int[5];
BindingOperations.SetBinding(view, Window.TitleProperty, new Binding("Length"));
I'm not sure if this is real solution for your general problem, but it works in this case.
I don't believe the Window's bindings will run without calling Show or ShowDialog, because that is the only way it gets associated with the UI message loop/dispatcher.
Your best bet would be to set it to be as least visible as possible, potentially using an extension method to clean things up:
public static void PokeWindowDispatcher(this Window window)
{
window.WindowState = WindowState.Minimized;
window.ShowInTaskbar = false;
window.Visibility = Visibility.None;
using (var wait = new ManualResetEvent())
{
Action<object, RoutedEventArgs> loaded = (sender, e) => wait.Set();
window.Loaded += loaded;
try
{
window.Show();
wait.WaitOne();
}
finally
{
window.Loaded -= loaded;
window.Close();
}
}
}
I had the same problem, and from sixlettervariables gave me an idea. It's very simple.
I am using WPF in WinForms application, so I use ElementHost control to host Wpf controls on WinForms control. To enforce WinForms control initialization you can just read value of Handle (which is actually Windows HWND) and this will force control to fully initialize itself including child ElementHost and all Wpf binding work.
I didn`t try to perform the same thing for pure Wpf control. But you can easily use ElementHost to initialize your Wpf controls like this:
var el = new ElementHost();
var p = new TextBlock();
p.DataContext = new { Data = "1234" };
p.SetBinding(TextBlock.TextProperty, "Data");
el.Child = p;
var t = el.Handle;
Debug.Assert(p.Text == "1234");
PS: Found, that everything work better, if you first set DataContext and only then force a Handle to be created (just like my example). But, I think, this is already the case for you, so should not be a problem.
Have you tryed to use the IsDataBound
http://msdn.microsoft.com/en-us/library/system.windows.data.bindingoperations.isdatabound.aspx
Also check this out:
System.Windows.Interop.WindowInteropHelper helper = new System.Windows.Interop.WindowInteropHelper(view).EnsureHandle();
http://msdn.microsoft.com/en-us/library/system.windows.interop.windowinterophelper.ensurehandle.aspx
My other question is why you trying to do a UNIT test on something that has been technically tested already? By the way I am not critising, just want to understand a little better.
Not sure, but maybe something like this will work?
view.GetBindingExpression(Window.TitleProperty).UpdateTarget();

WPF: Textbox not firing onTextInput event

So basically, I have a bunch of TextBoxes that the user gets to fill out. I've got a button that I want to keep disabled until all the TextBoxes have had text entered in them. Here is a sample XAML TextBox that I'm using:
<TextBox Name="DelayedRecallScore" TextInput="CheckTextBoxFilled" Width="24" />
And here is the function that I'm trying to trigger:
//Disables the OK button until all score textboxes have content
private void CheckTextBoxFilled(object sender, RoutedEventArgs e)
{
/*
foreach (TextBox scorebox in TextBoxList)
{
if (string.IsNullOrEmpty(scorebox.Text))
{
Ok_Button.IsEnabled = false;
return;
}
}
Ok_Button.IsEnabled = true;
*/
MessageBox.Show("THIS MAKES NO SENSE");
}
The MessageBox is not showing up when TextInput should be getting triggered. As an experiment I tried triggering CheckTextBoxFilled() on PreviewTextInput, and it worked fine then, meaning that for whatever reason, the function just isn't getting called. I also have a validation function that is triggered by PreviewTextInput, which works as it should. At first I thought PreviewTextInput might somehow be interfering with TextInput, so I took PreviewTextInput off the TextBox, but that hasn't managed to fix anything. I'm completely befuddled by why this might happen, so any help would be appreciated.
Your handler for the TextInput event is not fired because the TextBox is handling the event. You could try using the TextChanged event instead, since really you just want to know when characters were added or removed from the TextBox.
InitializeComponent();
textbox.AddHandler(TextBox.TextInputEvent,
new TextCompositionEventHandler(TextBox_TextInput_1),
true);
Use "PreviewTextInput" instead, it will work.
Create a new class derived from TextBox. In the new class override the OnTextInput method. Your OnTextInput method will get called before the TextBox gets it.

Canceling SelectedIndexChanged in WinForm ListView

The winform ListView doesn't seem to have an easy way to cancel the SelectedIndexChanged event. I don't see a SelectedIndexChanging event either.
The code is my attempt. However the hightlight is gone and I was wondering if I need to color the selection also or if there's a better way to cancel. _prevSelectedIndex is the index from the last selection. I want the highlight to go back to the previous selection.
lvSearchResults.SelectedIndexChanged -= new EventHandler(lvSearchResults_SelectedIndexChanged);
lvSearchResults.SelectedIndices.Clear();
lvSearchResults.SelectedIndices.Add(_prevSelectedIndex);
lvSearchResults.Items[_prevSelectedIndex].Selected = true;
lvSearchResults.SelectedIndexChanged += new EventHandler(lvSearchResults_SelectedIndexChanged);
The best and easy way is to have a boolean property which you set to say, true when you dont want the event to fire. In the SelectedIndexChange event, you test to see if you should allow the event to continue firing else you return. Check my code out. The code below behaves the same.
//somewhere on top
private bool dontFireEvent = false;
//somewhere else
dontFireEvent = true;
treeView1.SelectedNode = treeView1.Nod.... bla bla //event won't fire
dontFireEvent = false;
//within the event code
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
if (!dontFireEvent)
ProcessLogin();
}
As long as you have multiselect turned off you shouldn't have to call
SelectedIndices.Clear() Selectedndices.Add(..)
as well as:
Items[_prevSelectedIndex].Selected = true;
The latter will accomplish the same result.
I believe you're also adding a continually growing list of event handlers. To remove an event handler you have to store an instance of the delegate then remove that instance;-= new EventHandler(...) isn't going to remove the handler.

Resources