key binding issues when hosting WPF in Win Form - c#

we have a WPF component (User Control) that's hosted in a Win Form through ElementHost. the WPF component is defined as the ElementHost's Child through
ElementHostControl.Child = wpfFrame
there are certain key bindings we want to pass in from the Win Form to WPF form. we are doing so through KeyBinding(). then we assign the keybinding to the
WPFFrame.InputBindings.add(curBinding);
it's kind working with most key combinations although I didn't test all of them. but Ctrl+B, Shift+P etc. seems all working fine.
But I try Shift+(Left Arrow key), it's not working. what's interesting is when I try it in standalone WPF application then it's doing the job as expected.
I made sure the data flow through the exactly same code but why Shift+P works while Shift+ <- doesn't? now I set a breakpoint in the WPF Commend executed and run it from Win Form. I use key binding to trigger another command then use Ctrl+<- to trigger the problematic command it actually "sometimes" working. if I remove breakpoint and do Ctrl+<- then it never triggers the associated command.
I suspect it's an integration issue in between WPF and Win form since Shift+<- works in WPF itself.
any input is appreciated.

it's indeed a focusing issue. setting a break point and this line:
var v = Keyboard.FocusedElement;
at runtime shows the focus was stolen by various elements. I need to hand pick couple of them and set
Focusable="False"
in XAML. issue fixed!

Related

RoutedCommand call fails using KeyGesture when using WindowsFormHost control in WPF

I have an application using cefSharp and some KeyGestures to open some forms. We were initially using the WPF ChromiumWebBrowser, but required the use of touch scrolling, which is not supported. As a result, we changed the control to the WinForms ChromiumWebBrowser inside a WindowsFormHost.
After the switch, KeyGestures bound to our RoutedCommands no longer fire.
After reviewing here, here, and here, I have tried several different solutions, but to no avail.
As per the above, when the window is deactivated, then activated the KeyGestures are routed appropriately (as discussed regarding breakpoints 'causing' the commands to work).
I have tried using the CommandManager.InvalidateRequerySuggested method on a timer or being called on loaded, after transfering focus to the window, after transfering forcus to another WPF control and after focusing the WindowsFormsHosts.
My command declaration is as follows:
RoutedCommand ShowAdmin = new RoutedCommand();
ShowAdmin.InputGestures.Add(new KeyGesture(Key.F1, ModifierKeys.Shift | ModifierKeys.Alt | ModifierKeys.Control));
CommandBindings.Add(new CommandBinding(ShowAdmin, ShowAdminForm));
I essentially have two questions:
1) Why am I not receiving the command. Is it because the WinForms control doesn't 'bubble' the keypress events?
2) What can I do to capture the keygesture without resorting to opening another window, only to close it again
Thanks in advance for your questions, comments and answers!
If you implement IKeyboardHandler you should be handle custom key press combinations, that's probably your easiest and cleanest solution.
https://github.com/cefsharp/CefSharp/blob/master/CefSharp/IKeyboardHandler.cs

Registering for UIAutomation structure change events

I am working on an application that is monitoring a given application for automation events. Currently, I am working specifically with structure change events on a WPF application that I developed.
public void MonitorStructureChangedEvents(AutomationElement element)
{
Automation.AddStructureChangedEventHandler(element, TreeScope.Subtree, OnStructureChanged);
}
where in this case, element is the root AutomationElement of the Application (its main window). The WPF application in question is just a Window with a grid view and various controls (text boxes, checkboxes, buttons, etc). It is a test app I have developed specifically for testing UIAutomation events.
I am using a Unit Test project to test these events, and I am launching the application in the ClassInitialize decorated method. I do not register for StructureChanged events until the application is launched and I have located it via WMI in my TestMethod. The application is spawned as a new process.
However, upon registering for structure changed events, I receive structure changed events for all the elements in the main window of my application, even though the WPF application is effectively idling. I have buttons in the main window that add and remove controls to test StructureChanged events, and it does work, however I am unsure why when I initially register, all of the elements fire a structure changed event.
Edit: After further testing, I notice that these events are fired as soon as I either click on the application window, or hover over a button. It then fires a structure changed event for every element in the app one time. After it is done, it no longer fires a structure changed event if I hover over a button, or click on the application (even after clicking on another application or the desktop)
Edit2: After further testing, I believe I figured out the cause of the issue, but no solution yet. When I try to TreeWalker.RawViewWalker.GetFirstChild(rootApplicationElement) I receive a null. It appears that the AutomationElement that I am acquiring has no children cached. Once I add the StructureChanged event handler on the element, the TreeWalker method works, and I get a valid element. It seems when I activate the window after this, that's when it realizes that it now has all these new child elements. Is there a way to cache all the descendants of the rootApplicationElement so that before I add the event handler, I can walk the entire subtree?
I was able to solve the problem by using the following method
CacheRequest request = new CacheRequest();
request.TreeScope = TreeScope.Element | TreeScope.Descendants;
using (request.Activate())
{
rootApplicationElement = AutomationElement.RootElement.FindAll(TreeScope.Children,
new PropertyCondition(AutomationElementIdentifiers.ProcessIdProperty, ApplicationInstance.ProcessId))[0];
}
I do not advise that people acquire a root element in this fashion, since if an application has more than one window you will get more than one result, but this was tailored to a specific testing need.

UITextView - InsertText doesn't auto-correct or auto-capitalize

I have a custom InputView on a UITextView with auto-correct and auto-capitalization turned on.
My custom input view has a bunch of UIButtons that call InsertText on the UITextView. This works fine for inserting text at the current cursor position. The problem is that auto-correction and auto-capitalization do not work when InsertText is called. If input comes in through a bluetooth keyboard, or the standard keyboard, everything works fine.
Is there some method I can call on UITextView to invalidate auto-correct/auto-capitalization? SetNeedsDisplay was my first attempt, but it had no effect. Also, my app may potentially be iOS 7 and higher, so it's fine to use newer text APIs.
*NOTE: I'm using MonoTouch (Xamarin.iOS), but Objective-C answers are welcome.
Not without rolling your own autocorrect system. The iOS autocorrect system is opaque to developers, and only works on keyboard input. Any time you explicitly set the .text property on an input or text view, the value you set is what will appear.
Ok, I found a way in which this works:
Implement your own NSTextStorage
Setup an NSLayoutManager and NSTextContainer to coordinate your NSTextStorage with UITextView
When programmatically modifying your NSTextStorage subclass, auto correct "just works"
I think this works because modifying NSTextStorage fires events that notify the UITextView to update things like autocorrect. It works only on iOS 7 and higher.

Where to implement BackgroundWorker. Winforms c#

I have created a UserControl using a DevExpress XtraTreeList. Basically in this user control I am using inventory Items. which are more than 30,000. Now when I load the complete table in my typed DataSet it takes almost 6-8 seconds.
The purpose to build the usercontrol is reuseability. So that we can use it in other parts of the application.
Now I am using this UserControl in a Winform, where this usercontrol is place in the left hand side on the form where it will be used as a Menu. The user is suppose to click its desired item by expanding the nodes and when the nodes is selected and then its further details will be fetched and will be displayed in the from. So far so good.
Now the reals issue is that in the same form along with other controls, we have a Devexpress XtraButtonEdit(build using a textbox and a button on the right handside). This buttonEdit is displaying the currently selected Item(from the treeMenu UserControl) code in its textbox, when we are clicking the buttonEdit button then we are loading another form as a model and displaying the same UserControl (XTRATreeList from DevEpress). The only issue is that when I am laoding this new form as a model. The tree again takes time to load which looks bit awkward.
To rectifiy this issue, I am trying to implement the Background worker thread, but bit confused that where should I implement that backgroundWork. In the User Control or in the form.
If I implement that Background worder in the UserControl then how can I access this BackgroundWorker thread and execute it on the main Form where I am using the UserControl on the left hand side as a Menu. Actually what I want is when user clicks the ButtonEdit control and the form loads, it should load like a flash or max in 2 sec....please help.... and execute it.
I hope that ppl will understand my issue.
If somebody finds any difficulty plz let me know....
Regards,
You don't have to "access this BackgroundWorker thread and execute it on the main Form".
A user control could contain its own Bgw. You nee a synchronous method (on the UC) to start it. Then handle the completed (and mayb progress) events inside your UC.
But the data won't load any faster, you only unfreeze your main GUI.
If the delay is caused in the code that loads the tree, then you should "thread" that part. I would recommend reading this sample chapter from C# in a Nutshell: http://www.albahari.com/threading/. They cover C# 4.0, but you should be able to apply most of the concepts to C# 3.0 if that's what you are using.

WPF TextBox doesn't take input, space and backspace works

I have textbox inside a usercontrol and I add the usercontrol to the MainWindow with the following XAML:
<Views:MyUserControl />
I have one TextBox in MyUserControl, the problem is that the TextBox doesn't take any input. Backspace och space works, but if I press the letter och numbers no text is added to the TextBox.
I have made sure that the text is not just hidden in the TextBox.
I have also tried to add a RichTextBox to MyUserControl with the same result, it doens't take any input (beside space och backspace).
I have also tried to add a TextBox to the MainWindow with the same result; it doens't take any input (beside space och backspace).
Also MyUserControl is added in a TabControl and TabItem.
Any clues?
Edit: Additional information
Forgot to write that I'm opening/creating the WPF Window from a WinForm application.
When I set my startup project in VS10 to be my WPF-project it work great with the keyboard input to the TextBox.
How come?
Im opening/creating my WPF windows with the following code:
MyWpfProject.MainWindow mw = new MyWpfProject.MainWindow();
mw.Show();
Edit: Solution
So I guess my real problem was that is was opening the WPf project from a WinForms application.
I added the following code:
MyWpfProject.MainWindow mw = new MyWpfProject.MainWindow();
ElementHost.EnableModelessKeyboardInterop(mw);
mw.Show();
"The EnableModelessKeyboardInterop() call is necessary to handle keyboard input in the WPF window if loaded from a non-WPF host like WinForms."
http://weblogs.asp.net/jdanforth/archive/2008/07/29/open-a-wpf-window-from-winforms.aspx
Answer to my own question (if someone else run into the same problem):
If you open a WPF-form from a WinForms application you have to do the following to get keyboard input:
MyWpfProject.MainWindow mw = new MyWpfProject.MainWindow();
ElementHost.EnableModelessKeyboardInterop(mw);
mw.Show();
"The EnableModelessKeyboardInterop() call is necessary to handle keyboard input in the WPF window if loaded from a non-WPF host like WinForms." http://weblogs.asp.net/jdanforth/archive/2008/07/29/open-a-wpf-window-from-winforms.aspx
maybe your user control is getting the keyboard event instead of your textbox? try to search in this way, it happens with mouse buttons.
I had the same problem but my Environment was built such that I could not apply the solution above. As it turned out that wasn't the problem. It was much more easier than expected.
I host a WPF User Control inside WinForms. My WinForms MainView (Startup form) did override the "ProcessCmdKey". Because of logical errors it returned nothing.
Nevertheless I received all key events in code behind in my XAML file but this events never updated the "Text" of my text box. Only space and backwards did so and this were the key events I didn't receive in code behind.
So in case you don't think of the simplest solution here it is for VB.NET.
STRG+F "ProcessCmdKey" and make sure that you Return the correct value of you may completely uncomment it just to verify that it isn't causing you the same trouble as it did to me.
I would like to add my own answer just on the off chance it can help anyone else with the same or similar problems. My particular use case was a .Net windows form created by an addin. The addin was running out of Autodesk Inventor which I think may be WPF (but don't take my word for it.) The addin had several windows forms some docked within the application and with some acting as popups or a separate secondary window. The solution was to set the windows forms owner to one of the docked forms. Doing this allowed text to be edited and entered into its textboxes. Very niche solution to a niche problem but hopefully it may help someone at some point in the future.
private void CreatePanels(DockableWindows DockableWindows)
{
//Create the dockable window that the panel form will live on.
DockableWindow SideWindow =
DockableWindows.Add(SidePanelNames.GUID, SidePanelNames.InternalWindowName, SidePanelNames.Title);
//Configure the windows settings.
SideWindow.ShowTitleBar = true;
SideWindow.ShowVisibilityCheckBox = true;
SideWindow.DisableCloseButton = false;
SideWindow.Visible = true;
SideWindow.AddChild(SidePanel.Handle); //Add the forms as children and then display to the user.
SidePanel.Show(); //Display the panel to the user.
}
/// <summary>
/// Creates the Display window were order and patient information is displayed to the user.
/// This method makes the adjust device form the owner of the display window. Because of this
/// dependecency it should be run after the panels have been created.
/// </summary>
private void CreateDisplayWindow()
{
DisplayForm = new DisplayForm //Create the display form.
{
Owner = SidePanel //Doing this allows text boxes to have values entered into them.
};
DisplayForm.Show(); //Show the display form to the user.
}
//Adding the side side panel as the owner of the display form was effectively the only change I needed to make.
I will add a few words of warning for anyone attempting this solution. Simply put I've since found out it's not great. It prevents the form or any of its controls from detecting certain key presses (enter and tab) which matters a lot for my usage. You'll find a much better and solution here https://forums.autodesk.com/t5/inventor-forum/dockable-window-with-wpf-controls-don-t-receive-keyboard-input/td-p/9115997

Categories

Resources