Custom OSK: Listen to TextBox Focus - c#

I have written a custom OnScreen Keyboard as an UserControl to have a better control over what the user can type (Alphanumeric/Numpad/Navigation Keys - stuff like that) and to have a better control over the screen layout at design time.
The OSK works by manipulating the text- and selection-properties/functions of a textbox-control.
My main Problem is how to find the right TextBox to inject text into.
My first, naive approach was to register every TextBox I want to use with the OSK Control manually and use the GotFocus/LostFocus of those registered TextBoxes to determine the active control:
public void RegisterInput(TextBox text) {
if (!_listeners.ContainsKey(text)) {
_listeners.Add(text, modes);
text.GotFocus += Input_OnGotFocus;
text.LostFocus += Input_OnLostFocus;
}
}
private void Input_OnLostFocus(object sender, RoutedEventArgs routedEventArgs) {
if (_focused == sender) {
_focused = null;
IsEnabled = false;
UpdateKeyboardMode(); // << Updates Keyboard layout (Alphanumerical vs Numpad) based on focused control
}
}
private void Input_OnGotFocus(object sender, RoutedEventArgs routedEventArgs) {
_focused = (TextBox) sender;
IsEnabled = true;
UpdateKeyboardMode();
Bindings.Update();
}
I work with Focus here, because I need to determine which kind of keyboard (full-size alphanumerical vs. short numpad) to display for each TextBox. The _focused TextBox is then used to directly inject the pressed keys into it. In the constructor of my Page which also contains the OSK-control I would call RegisterInput() with a reference of each and every TextBox I defined on the page. This works just fine — if I have those references.
But now I am working with UserControls. That also removes the TextBoxes out of reach for direct referencing, but I could write some kind of VisualTree-Scan after InitializeComponent() to find all references and call RegisterInput() on each reference I found. If I only need to do this once, it isn't a problem (altough it is still ugly).
One step further - ListBoxes with dynamicly changing contents and DataTemplates. Now I'd need to rescan the whole VisualTree explicitly everytime something changes. But how to detect those changes?
The question is: Can I get an event as soon as $any element in my VisualTree gets/looses focus, without knowing all those elements beforehand (thus replacing RegisterInput() completely)? Or can I listen to changes to the VisualTree to rescan all controls and then call RegisterInput() manually for every TextBox I found?
The goal is to get a handler called everytime a GetFocus/LostFocus event on any TextBox/Control in the UI is raised so that I can update the keyboard to either display a full-sized alphanumerical keyboard (for default textboxes) or a shortened numpad (e.g. for textboxes bound to numerical backing fields).
Alternatively: Is there any other way to inject text and call UpdateKeyboardMode() to update the keyboard layout as soon as the selected textbox changes?
Other options I thought about include:
Build a custom control which derives from a TextBox and let it register itself to the OSK. I'll probably resort to this method, if I don't find any better way. But this will destroy support for 3rd party libraries in which my control is not present and thus does not use the "special magical textbox with osk support".
Don't use events at all. Get the currently focused TextBox with the FocusManager as soon as the user presses a key on my OSK and inject text into the focused instance. Problem with this approach is, that it completely destroys the capability to adapt the OSK to different input types (alphanumerical vs only Numpad), because I cannot determine the keyboard type I need before pressing a key.
Rescan the VisualTree with a timer. Won't do that, thats simply too much of a hack.
Use the OnScreen-Keyboard supplied by Win10 IoT. Two problems: It has no designtime support and is displayed above elements, even if the focused element is directly underneath the keyboard (acceptable if neccessary), but I don't know of a way to change the keyboard "layout" between a full-sized alphanumeric keyboard and a shortened Numpad which only contains numbers and some keys. Also it does not allow to use custom keys (e.g. arrow keys for navigation, custom return key handling).

After a discussion in the chat forum, the actual problem isn't to create a Custom OSK control and use that to interact with the TextBoxs but instead, it's "being bound to use custom control" wrapping a textbox everywhere a OSK needs to be shown.
The Solution would be to listen to the OS-OSK events and when they are triggered, pop up the Custom OSK this ways you won't have to wrap a Textbox in a user control and use that throughout your project.
Link to the Documentation: - respond to the presence of the touch keyboard

Related

Find if TextBox.Text got changed by the user or by the code (Windows Phone 8)

I have a Windows Phone 8 project that converts values (i.e.: Celsius to Fahrenheit). There are two TextBox UI elements, one of which is read-only. The user can change the first TextBox to input the value to be converted. He can also press a button to "swap" the two TextBoxes so that he can do the reverse conversion. When the user presses the button, the value from the second TextBox goes into the first TextBox (and vice versa). But it's not the user who changed the value, it's the code who did.
I asked around (on IRC) and researched the subject, but I am a beginner and couldn't understand most of what I have found.
I heard that a simple solution would be to use Data Bindings. I researched the subject, and from what I read, Data Bindings can't solve my problem (correct me if I'm wrong).
I also tried to create a subclass of TextBox, hoping that I could hook in some custom event to it and go further in that direction. But I did not understand how to link the custom TextBox to the UI (in XAML). The way I created the subclass is to just create a new class and add TextBox as the parent. I know there is a template in VS to create a new User Control, and I tried it, but I couldn't understand what I was doing (or what I was supposed to do).
So I have two questions: Am I looking at the problem from the right angle? If yes, how do I create a custom TextBox and link it to the UI? If not, how could I solve my problem?
If your question is how to distinguish if the text got changed by the user or by the code then its simple.
Assuming that when the user types something you'd like to perform method A but when the code changes the text you'd like to perform method B:
In both cases you will need to override the TextBox.TextChanged() event handler.
You will also need a flag variable to tell you if the swap button was pressed or not.
The event handler should be something like this:
{
if (swap_pushed)
{
Method_B();
swap_pushed = false;
}
else
{
Method_A();
}
}
And finally your event handler for swap Button.Click() should be like this:
{
swap_pushed = true;
}

Correct UserControl Usage?

I just started breaking up my GUI application into UserControls. I have a TabControl with a bunch of TagePages. Obviously my MainForm.cs file was filled up with tons of events and controls etc and it got very messy quick.
So a previous question gained me the insight of how to create a UserControl. I intend on creating a UserControl for each TabPage and I was wondering how I can interact with Components on the main form or other UserControls.
Here is an example of a TabPage that I have made using a UserControl, which needs to Enable or Disable a button depending which TabPage is currently selected. Is this proper usage or is there a better way?
public partial class TabDetails : UserControl
{
private RequestForm fRequestForm;
public TabDetails()
{
InitializeComponent();
}
public void CustomInitialization(RequestForm pRequestForm)
{
fRequestForm = pRequestForm;
pRequestForm.TabControl_Main.SelectedIndexChanged += SelectedTabIndexChanged;
}
private void SelectedTabIndexChanged(object pSender, EventArgs pEvents)
{
fRequestForm.Button_SubmitRequest.Enabled = fRequestForm.TabControl_Main.SelectedTab != fRequestForm.Tab_Details;
}
}
In the MainForm.cs constructor I call:
this.tab_Details1.CustomInitialization(this);
This doesn't look like a good use of a user control. The user control should not decide how things in the form should behave when something is changed in the user control. A user control should be unaware of its container and should operate in any container.
The user control should notify the form that something has changed without telling what's the internal implementation and the form should decide what to do.
Example:
A user control named "NameUserControl" consists of TitleComboBox, FirstNameTextBox and LastNameTextBox. The user control wants to notify when one of the values has changed.
Wrong Way:
Create events:
TitleComboBox - SelectedIndexChanged.
FirstNameTextBox, LastNameTextBox - TextChanged.
The problems here:
You expose the internal controls behavior. What will happen if you want to change the TitleComboBox to TextBox? You'll have to change the event name and implementation.
You expose the fact that you use exactly 3 different controls. What will happen if you want to use the same text box for first and last name? You'll have to delete one event and change the name of the other.
Good Way:
Create only a single event: NameChanged and expose 1 property of FullName or three different properties for the values.
Either way the form subscribe to the event and decide what to do next.
Another thing to think about: the more you add more functionality to your user control, you either make it less reusable or you make its code more complex. For example, if you add validation inside the user control, you'll find one day that you need it without validation, so you'll add a property "bool ValidateData" or it will be so complicated that you'll need to build another control. One way to solve that is to build very small user controls, but combine them in one or more bigger user controls that fit all your current needs.

C# Lock WinForm Controls

In a program I have written users can add controls to the form and move them around and set some properties in a pseudo design mode. I want to be able to lock all these controls into one location when they press a button to switch to "data mode". How can I do this? I wanted to do be able to loop through all the controls and use the Lock Property but I noticed it didn't show up in intellisense.
Thanks!
The Locked property is not a real property -- it is one which is added in by the Windows Forms designer (like the Generate Member and Modifiers "properties"). You would therefore need to simulate it yourself, either at the form level or (if required) at the control level (say with a dictionary of which controls are locked), and manually check it in the code you've written for moving controls around.
I am assuming by "pseudo-design mode" you do mean that your application is in a run-time state, and the end-user is experiencing a "virtual design mode" : please correct me if I am wrong.
But, I am assuming you are referring to the design-time 'Locked property of controls, and that you wish to "emulate" this at run-time ... correct ?
I'm also assuming you are attaching mouse up/down/move handlers to the controls you do allow to move around, probably by looping through all, or a subset of, the controls on the form (or a collection you are maintaining of controls allowed to be moved).
If my assumptions are correct, I would go for removing the event handlers that enable moving when you need to disable control movement, then restoring those event handlers when you need to allow controls to be moved again.
One main reason being that it is, imho, "best practice" to control event-handling rigorously (leaving event handlers "in-place" can interfere with object disposal ... although that may, in no way, apply to your scenario here).
One more idea : you have an "invisible" Panel docked 'fill to the Form : on this panel are all controls that can be moved : this may allow you to more easily "narrow your focus" on which controls you "spend" this extra code on. The drawbacks in using this approach are usually :
if you use hostingForm.ActiveControl to determine which control got the mousedown (and, thus, can then be moved) : you'll find some controls, like labels, and pictureboxes, do not become the activecontrol of the form when clicked, but most do.
you have a "z-order" thing to think about since a control not in your panel encapsulating the controls you wish to allow to move sent behind the pseudo-transparent panel will be hidden.
For these reasons, imho, I think disabling and re-enabling event handler attachments is best, most simple, and since it can be done when the controls are "down-cast" to their control "identity" :
private void enableControlsMove()
{
foreach (Control theControl in panel1.Controls)
{
Console.WriteLine(theControl.Name);
theControl.MouseDown += new MouseEventHandler(theControl_MouseDown);
theControl.MouseUp += new MouseEventHandler(theControl_MouseUp);
theControl.MouseMove += new MouseEventHandler(theControl_MouseMove);
}
}
private void disableControlsMove()
{
foreach (Control theControl in panel1.Controls)
{
Console.WriteLine(theControl.Name);
theControl.MouseDown -= theControl_MouseDown;
theControl.MouseUp -= theControl_MouseUp;
theControl.MouseMove -= theControl_MouseMove;
}
}
I use it this way.
best, Bill
Locking controls prevents them from
being dragged to a new size or
location on the design surface.
However, you can still change the size
or location of controls by means of
the Properties window or in code.
MSDN
I guess it's a visible-to-designer-only property. I think you'd have to implement your own freeze mechanism - a little flag to toggle between Design and Use modes.
Update: It seems that custom designer classes can add properties to controls based on whether they are in Design Mode or not.
More details available here if you intend to take the VS architectural hammer path. In any case, worth 10 mins of reading time.
Custom Design-time Control Features in Visual Studio .NET - Dino Esposito

How do I select the previous control to a container control?

I have a control which contains a NumericUpDown. The updown is only shown when the container has focus, so the container has to be selectable (or else it could never receive focus). I want the control to behave as a single entity with regards to tab order; that is, when the user tabs to the control, it shows the updown and the updown is focused; when the user tabs away from the updown, it is as if they had tabbed away from the control.
It's easy enough to achieve the first part: in the container's OnEnter, I focus the updown. If the user tabs away without shift, it also works fine, since the next control in the tab order is the correct one. However, the previous control in the tab order to the updown is the container, since it had to be selectable; so when the user shift-tabs away from the updown, the container is selected, and therefore the updown gets selected again.
How do I select the previous control to the container control, when the user shift-tabs away from the updown?
UPDATE:
My problem isn't detecting when I need to do this - it's finding the control to send focus to.
UPDATE:
SelectNextControl only seems to work within the container's parent's controls; if the container is the only control on its parent, it doesn't change focus, even if there are other controls elsewhere in the hierarchy that ought to receive focus via tab.
if you know the direction of the tab you could use SendKeys.Send("+{TAB}"); and SendKeys.Send("{TAB}");
or you could use Control.SelectNextControl()
void UserControl1_Leave(object sender, EventArgs e)
{
this.numericUpDown1.Visible = false;
Control c = Parent.Controls[this.Name];
int i = Parent.Controls.IndexOf(c);
Parent.Controls[i - 1].Focus();
}
I've added this leave event to a custom control and its working for me. Basically when the user shift tabs away this event sets the focus to the previous control in the parent form's control collection. Don't know if its what your looking for exactly but hopefully it will send you in the right direction.
It's a hack, but you can use the OnEnter event coupled with a boolean variable. If the variable is set to true then you were already in your container and go to the previous control (which could be a property of your container control so you know where you are going).
If the variable is false, your just getting to your custom control and focus on the up/down.
On the exit of the container, set the variable back to false.
I'm sure there's something simpler out there, but offhand this is the quickest thing I can think of.
Actually this seems to be the default behavior for me?

Disabling a control from receiving a event in C#

I have a dialog with loads of control in it. Each and evey control will be populated during the loading sequence and it might take a while for it to get completely filled. Mean while, I don't wanna allow the user to click on any of the controls. In other words, I wanna disable the control from receiving the events and I don't wanna disable the controls (as it might look odd).Also, I don't wanna subscribe and unsubscribe for the events regular intervals. Is there any way to stop the controls from listening to the events for a brief time ??
Sudarsan Srinivasan
The whole point of disabling controls is to communicate to the user that the control cannot be used at a particular time. This is a convention that users have learned and are used to, so I would advice to follow that. Not doing that may confuse the users.
The easiest way is to disable the container in which the controls are located in, rather than disabling each and every control. A better way (or at least the way that I prefer) is to have a method that will control the Visible and Enabled properties of controls based on which state the UI is in.
The easiest way is to move the control population out of the load event (if possible). Then in Load do something like:
private bool _LoadComplete;
void OnFormLoad(Object sender, EventArgs e)
{
_LoadComplete = true;
InitializeControls();
_LoadComplete = false;
}
void InitializeControls()
{
// Populate Controls
}
void OnSomeControlEvent()
{
if (_LoadComplete)
{
// Handle the event
}
}
Edit A Couple other Ideas:
Set the Application.Cursor = WaitCursor (typically will disallow clicking, but not a 100% guarantee)
Create a "Spinner" control to let the user know that the screen is busy. When loading bring it to the front so it sits on top and covers all other controls. Once you're done loading set it to visible = false and show your other controls
Unfortunately the only way i know of is to have a class variable (called something like _loading) and in each control handler do something like:
If (! _loading )
{
...
}
And in your loading code set _loading = true; once you have finished loading.
If you just want to disable user input, then you can set the form's Enabled property to false.
This has the effect of blocking user input to any of the form's controls, without changing the appearance of the controls; it's the technique used internally by the ShowDialog method.

Categories