Add Handler method not being called when Routed Event is attached - wpf

I'm trying to create an attached behavior to add an auto-complete list to the standard TextBox control. My goal is that every time the TextChanged event is raised, my AutoCompleteBehavior class creates a popup and fills it with potential auto-complete results.
To get these results, my AutoCompleteBehavior declares the following event:
Public Shared ReadOnly AutoCompleteListRequestedEvent As RoutedEvent =
EventManager.RegisterRoutedEvent("AutoCompleteListRequested",
RoutingStrategy.Bubble,
GetType(AutoCompleteListRequestedEventHandler), GetType(AutoCompleteBehavior))
The above is meant to be an attached event, used like this:
<TextBox lib:AutoCompleteBehavior.AutoCompleteListRequested="EventHandlerHere"/>
The idea is that when TextChanged is raised, AutoCompleteBehavior.AutoCompleteListRequested is also raised, which asks the implementing program to supply a list of suggestions for the current input.
For this to work, I have to hook in to the TextBox.TextChanged event as soon as my attached event is attached to said TextBox. Per Microsoft I should be able to declare a sub Add*Handler and Remove*Handler where the "*" is the name of the attached event, and these would be called whenever the attached event is added or removed from an element.
So right below the event declaration I have:
Public Shared Sub AddAutoCompleteListRequestedHandler(TB As TextBox, handler As AutoCompleteListRequestedEventHandler)
'Code to hook into TextBox.TextChanged
End Sub
Public Shared Sub RemoveAutoCompleteListRequestedHandler(TB As TextBox, handler As AutoCompleteListRequestedEventHandler)
'Code to unhook fromTextBox.TextChanged
End Sub
My problem is AddAutoCompleteListRequestedHandler is never called. If I call TextBox.RaiseEvent to raise AutoCompleteListRequested, the event handler defined in XAML does get called (so the event is attached), but it seems my AddAutoCompleteListRequestedHandler is completely skipped.
As a final note, I found this question here which seems to be describing the same problem (my code is also in a dll just like his), but it's two years old and was never answered.

The XAML processor won't call your static methods when hooking up the event handler.
If you want to do something when the TextBox raises the TextChanged event, you would probably be better off implementing an attached behaviour and hook up to the TextChanged event in the PropertyChangedCallback or in the OnAttached() method depending on which kind of behaviour you create.
Please refer to my answer here for more information about attached behaviours.

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 get input on textchanged on winform

I want to wait to user input on winform. Without using textbox or other control. just the form it self handle the input.
I've try to add KeyPress and TextChanged Events to the form, both with no successes. What am I doing wrong?
thanks :)
Based on your comments, it sounds like you're asking why double clicking on a control to create an event handler works, but typing the exact same method signature doesn't. The reason for this is the designer generates code that winds up in the .Designer.cs file. If you look in there, you'll find (eventually -- there's a lot of code generated for forms...) a line linking the actual event the button raises to the handler method it generates; something like this:
button1.Click += button1_Clicked;
This is the magic that doesn't happen when you manually type the signature for button1_Clicked(object sender, EventArgs e) directly in the Form1.cs file.
But I might have misread your comments/question entirely, too.
If you want handle keyboard input on the Form:
Create a event handler for Form.KeyPress - event
Set property KeyPreview = true (Through designer for example)
Then before pressing keys be sure that Form have focused
Keyboard input on the Form level
About TextChanged event:
From MSDN:
This event is raised if the Text property is changed by either a
programmatic modification or user interaction.
So TextChanged event must be raised at least on the initialization of form, when .Text property be changed. And of course, everytime you change this property as: _myForm.Text = "my new form"
In your case TextChanged event not raised because(I assume) Event handler was added after InitializeComponent() method was called. Where (C#)this.Text = "MyForm" was called
TextChanged Event

WPF - Handling a RoutedEvent in the parent for a specific control instance

In my user control I have several textboxes e.g. Textbox1, Textbox2.
In the parent I only want to handle KeyDown events raised from Textbox1, not all Textbox
I have added a handler in the parent
this.AddHandler(TextBox.KeyDownEvent, new RoutedEventHandler(OnTextboxGoToPageKeyDown));
but of course this will handle all KeyDown events which I don't want.
Do I have to add some conditional logic in the RoutedEventHandler to check where the event was raised from?
or is there a better way?
What's the best way to do this?
Thanks.
The RoutedEventArgs in your OnTextboxGoToPageKeyDown has a property named Source which have a references to the Object who raised the event. You can ask for that property when you need to execute your logic.
HTH

How to subscribe to EventHandler of a private control outside of Form

maybe my design is not good, or I don't see the obvious solution, but I want to subscribe to a buttonClick EventHandler of Form1 from outside form1.
For example I have a Controller and Form1 who are both instanced in the main function.
Now I want to subscribe a function from Controller to the buttonClick event from Button1_Click in Form1. But the button1 is declarded private, so i can't do
form1->Button1->Click += gcnew EventHandler(controller->function)
Is there any way to get around this?
Ok I could write a setter or something in Form1, but is there any other solution?
I read some examples, but they are all calling events from within the same class so they don't address my specific problem.
EDIT
Maybe it helps if I say what I really want to achieve:
Ok there is the GUI aka Form1 and a Conroller Class.
The Controller should get notified, if the user triggers a specific ButtonClick event on the GUI.
Also the Controller should be able to subscribe and unsubscripe from different events during runtime. To make it even more confusing (atleast for me) the controller should raise events, which trigger some GUI behaviors, like enabling some buttons and disabling others.
So this is what I want to do, at least in theory it sounded good, but now I have problems with the implementation.
This is something you ought to refactor of course. Add an event to the Form1 class and let the button1's Click event raise the event.
Assuming this is difficult: there's a back-door through the public Controls property:
form1->Controls["Button1"]->Click += // etc...

How to capture a mouse click on an Item in a ListBox in WPF?

I want to get notified when an item in a ListBox gets clicked by the mouse, whether it is already selected or not.
I searched and found this: (http://kevin-berridge.blogspot.com/2008/06/wpf-listboxitem-double-click.html see the comments)
private void AddDoubleClickEventStyle(ListBox listBox, MouseButtonEventHandler mouseButtonEventHandler)
{
if (listBox.ItemContainerStyle == null)
listBox.ItemContainerStyle = new Style(typeof(ListBoxItem));
listBox.ItemContainerStyle.Setters.Add(new EventSetter()
{
Event = MouseDoubleClickEvent,
Handler = mouseButtonEventHandler
});
}
//Usage:
AddDoubleClickEventStyle(listView1, new MouseButtonEventHandler(listView1_MouseDoubleClick));
This works, but it does it for a DoubleClick. I can't get it working for a single click though. I tried MouseLeftButtonDownEvent - as there doesn't seem to be a MouseClick event, but it's not being called.
A bit more general side question: How can I see what events do exist and which handlers correspond to them and when they actually do something? For example, what tells me that for a MouseDoubleClickEvent I need a MouseButtonEventHandler? Maybe for a MouseLeftButtonDownEvent I need some other handler and that's why it's not working?
I also tried subclassing ListBoxItem and override OnMouseLeftButtonDown - but it doesn't get called either.
Marc
I believe that your MouseLeftButtonDown handler is not called because the ListBox uses this event internally to fire its SelectionChanged event (with the thought being that in the vast majority of cases, SelectionChanged is all you need). That said, you have a couple of options.
First, you could subscribe to the PreviewLeftButtonDown event instead. Most routed events have a routing strategy of Bubbling, which means that the control that generated the event gets it first, and if not handled, the event works its way up the visual tree giving each control a chance at handling the event. The Preview events, on the other hand, are Tunneling. This means that they start at the root of the visual tree (generally Window), and work their way down to the control that generated the event. Since your code would get the chance to handle the event prior to the ListBoxItem, this will get fired (and not be handled) so your event handler will be called. You can implement this option by replacing MouseDoubleClickEvent in your sample with PreviewMouseLeftButtonDown.
The other option is to register a class handler that will be notified whenever a ListBoxItem fires the MouseLeftButtonDown event. That is done like this:
EventManager.RegisterClassHandler(typeof(ListBoxItem),
ListBoxItem.MouseLeftButtonDownEvent,
new RoutedEventHandler(this.MouseLeftButtonDownClassHandler));
private void OnMouseLeftButtonDown(object sender, RoutedEventArgs e)
{
}
Class Handlers are called before any other event handlers, but they're called for all controls of the specified type in your entire application. So if you have two ListBoxes, then whenever any ListBoxItem is clicked in either of them, this event handler will be called.
As for your second question, the best way to know what type of event handler you need for a given event, and to see the list of events available to a given control, is to use the MSDN documentation. For example, the list of all events handled by ListBoxItem is at http://msdn.microsoft.com/en-us/library/system.windows.controls.listboxitem_events.aspx. If you click on the link for an event, it includes the type of the event handler for that event.
There is also another way - to handle PreviewMouseDown event and check if it was triggered by the list item:
In XAML:
<ListBox PreviewMouseDown="PlaceholdersListBox_OnPreviewMouseDown"/>
In codebehind:
private void PlaceholdersListBox_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var item = ItemsControl.ContainerFromElement(sender as ListBox, e.OriginalSource as DependencyObject) as ListBoxItem;
if (item != null)
{
// ListBox item clicked - do some cool things here
}
}
Was inspired by this answer, but it uses listbox by name, I propose to use sender argument to avoid unnecessary dependencies.
I think the first option in Andy's answer, of using PreviewMouseLeftButtonDown, is the way to go about this. In XAML it would look like this:
<ListBox Name="testListBox">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<EventSetter
Event="PreviewMouseLeftButtonDown"
Handler="ListBox_MouseLeftButtonDown" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
There is another way to get MouseDown event in ListBox. You can add event handler for events that are marked as handled by using handledEventsToo signature of AddHandler method:
myListBox.AddHandler(UIElement.MouseDownEvent,
new MouseButtonEventHandler(ListBox_MouseDown), true);
Third parameter above is handledEventsToo which ensures that this handler will be invoked no matter if it is already marked as Handled (which ListBoxItem does in ListBox).
See Marking Routed Events as Handled, and Class Handling for explanation.
See How to Attach to MouseDown Event on ListBox for example.
You can use Event="MouseLeftButtonUp"
Unlike "PreviewLeftButtonDown" it will get the ListBoxItem handled too.
You can use the SelectionChangedEventArgs argument of the SelectionChanged event to find what item is add or removed through AddedItems and RemovedItems, usually only have the latest clicked on, or if not, then look at the last item which is the count-1.

Resources