I am developing a plugin for an application, and I must register any controls I create with my host application, else they do not receive any messages and are effectively disabled. Registration is performed using a control's window handle. (Please treat this part as a given, it is only a background as to why I need this.)
In order to do this, for every winforms control I create, I use its HandleCreated event and HandleDestroyed event to recursively register/unregister the control's handle and any child controls it owns.
I can do the same thing with a ContextMenuStrip, and this is fine for the first level of items, but if any of those items have submenus, I do not know how I can get a handle to the sub-menu that has opened in order to register it.
The ToolStripMenuItem objects inherit from System.ComponentModel.Component and not from System.Windows.Forms.Control, so they do not have a .Handle property, nor HandleCreated and HandleDestroyed events.
See image for exactly which window I need the handle of (at time of creation).
Use ToolStripMenuItem.DropDown.Handle.
Related
UPDATE: So, I have a solution to my immediate problem. I haven't succeeded in making "my own" TreeView class. But, the reason I wanted to do that was because controls based on ButtonBase don't function in a Popup from a TreeView, and with the help of #MarkFeldman, I have found a solution that comes at it from a different angle.
The problem is that the MouseDown and MouseUp events bubble, and that bubbling crosses the logical tree boundary between the Popup and its owner. So, when you click on something hosted inside the Popup, the TreeViewItem and TreeView that ultimately own the Popup get to hear about it. This then triggers code inside the TreeView that checks, "Do I have focus?", and if not, helpfully sets focus back to itself -- but being a separate logical tree, the Popup has its own focus context, and so this effectively steals focus from the Button control while it is in the middle of processing a click. The Button responds to this by ignoring the click.
This erroneous handling in the TreeView only happens when MouseDown and MouseUp events reach it. What if there were a way to prevent it from seeing those events in the first place? Well, if you intercept the PreviewMouseDown and PreviewMouseUp events and mark them Handled, then the framework doesn't generate MouseDown and MouseUp events to begin with.
Looking at the Reference Source, it looks like ButtonBase's click handling is tied up in a couple of protected methods:
https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Primitives/ButtonBase.cs,414
https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Primitives/ButtonBase.cs,478
This means you can call them from your own subclasses! So, instead of making "my own" TreeView where all controls behave properly, instead I can make "my own" CheckBox that works properly in a Popup from a TreeView. Since all of the actual click handling is directly accessible, and the events it normally responds to use the same EventArgs type as the Preview events, and on top of it the default handling takes care of marking the events as Handled, the entire implementation boils down to this:
public class CheckBoxThatWorks : CheckBox
{
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e) => base.OnMouseLeftButtonDown(e);
protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e) => base.OnMouseLeftButtonUp(e);
}
Nice!
ORIGINAL QUESTION:
I need to make a clone of the TreeView WPF class -- a copy of the control that runs out of my own code. (There is a bug in the control and Microsoft doesn't seem to deem it high-enough priority to fix, and it makes it completely impossible to host buttons (including check boxes and radio buttons) within pop-ups shown from TreeViewItems. link)
I am running into serious difficulties with the amount of internal shenanigans Microsoft has undertaken in the implementation of base WPF controls. A couple of issues I've bumped into:
Controls have a property HandlesScrolling that allows them to take over basic scrolling handling from ScrollViewer. But, it's marked internal, making it seemingly impossible for me to have a control of my own that does its own handling of scrolling from keyboard input. I was going to try having my TreeView handle keyboard scrolling in OnPreviewKeyDown instead of OnKeyDown, so that it can prevent KeyDown events from being raised for the keys it intercepts. I haven't gotten far enough to know what caveats there might be about this.
The Visual States system allows you to declare what styles should be applied when different states are entered, but actually entering states seems to be tied up in the virtual method ChangeVisualState on the Control type. All controls that want to switch between visual states can override this method and inspect their state to determine which Visual State should be shown. Oh wait. They can't because the method is internal! Apparently only Microsoft gets to create controls that set their own visual states??
Are there any strategies I can use to work around these limitations, or am I just completely out of luck?
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.
I have a WinForm app, the form has TabControl, control has three tabs tabPage1,tabPage2,tabPage3.
The Tab 'tabPage3' is hosting a User defined control which internally has one or more child controls.
Now my problem lies in tabPage3,
I know it is a pure Winforms behavior, until your parent is not activated child controls Onload event won't fire.
I have a requirement to force the Onload event to fire when the focus is on tabPage1, tabPage2. Is there any way to force the Onload event to fire.
I have already visited following links but didn't find any clue. Link Link Link
This is a very unusual requirement, strongly smells like an XY problem. The Load event is heavily over-used in Winforms, a side-effect of it being the default event for a Form or UserControl. One of the behaviors inherited from VB6, the Load event was a big deal in that language. What you want can easily be accomplished by not giving Winforms a choice:
public UserControl3() {
InitializeComponent();
CreateHandle();
}
The CreateHandle() call does the forcing, OnLoad will immediately run. But do be aware that this happens very early, too early to do the kind of things that you'd really want to use OnLoad() or the Load event for. Which are rather limited, it is only truly necessary to discover the actual Location and Size of the control. Anything else belongs in the constructor. Surely including the code that you now run in OnLoad().
Strongly favor using the constructor instead.
I had a similar problem for a previous project, for my needs I managed to just iterate over every tab page in the forms constructor (or possibly OnLoad I can't remember) and then reset the index back to 0 before ever showing the end user.
Something similar to:
for(int i = 1; i < tabControl.TabCount; i++)
tabControl.SelectTab(i);
tabControl.SelectTab(0);
I am basically making a popup controller that I can pass a Control to and when popup is activated it will remove the control being "popped up" from its parents children then add it as a child of the popup controller. I would leave it there however I need events on the object being "popped up" to remain intact and registered. I tried inserting the popup controller as a child of the original parent. And that might work for routed events however I need plain CLR events to work as well. For some reason when I remove the child from the parent its events get unwired, at least some do.
People who registered for the events of the control that is now being wrapped need to still get them when they happen. If I put a breakpoint right before attempting to raise the event the handler is definitely null.
The interesting thing is that if I put it back in its original parent the events immediately begin working again.
Any ideas?
In Windows Forms when a UserControl or Form first time becomes visible the Load event is fired.
http://msdn.microsoft.com/en-us/library/system.windows.forms.usercontrol.load.aspx
Is there any such event for controls like Checkbox, TextBox, Label ?
No. You could use the HandleCreated event, it is fired when the native window for the control is created. The first event you can rely on to run after the class constructor ran. It is triggered when the parent adds the control to its Controls collection and the control becomes visible.
Beware however that it this event can fire more than once. Controls may be re-created when certain properties get reassigned, the kind that requires the native CreateWindowEx() function to be called with new style flags. So you'll at least need to carry around a bool flag that keeps track of this.
Also note that setting properties of a control after the native window is created is pretty inefficient. All Winforms controls were designed to allow properties to be set before the native window is created. Whatever code you are generating almost surely should use the class constructor instead. Either of the derived control itself. Or in the code of the parent, much like InitializeComponent() does for a form or user-control.
The same is true for the existing Load event. It tends to be over-used due to the VB6 legacy where the Load event was very important. In Winforms however it is only required for code that depends on the final location and size of a control or form. Which may be different from the design properties due to form scaling. Any other code belongs in the constructor.