I have a MainView form that just contains a panel. I use a separate class (Presenter) to govern what form will be shown in that panel depending on different events. When one of these sub-forms are shown I want to be able to press a key down and have that sub-form respond; however, the keydown event does not hit in the sub-form, only the main form.
I want to know how to get the sub-form to respond to key events without going from MainView -> Presenter -> SubView.
Since your Presenter class is aware of the form it is putting into the panel, it will also need to get from that form or from associated data what control in the form is meant to have default focus. Presumably, your Presenter class has a method that shows a given form on that panel, so you do something like:
Presenter.ShowForm(MainViewPanel, Form1);
You would then need to do something like:
Form1.Controls.OrderBy(x => x.TabIndex).First().Focus();
What has the focus? If you don't have a control in the child form with the focus, it will never see the keyboard events.
Also, try setting KeyPreview to true on the child form.
https://msdn.microsoft.com/en-us/library/system.windows.forms.form.keypreview(v=vs.110).aspx
Alright I figured out what my issue was. It was a combination of a few of the posts, but I wanted to put everything down that I had to do in one place.
I have a method that looks like this:
private void DisplayView(Form view)
{
if (view == null || view == _mainView) return;
view.TopLevel = false;
_mainView.GameArea.Controls.Clear();
_mainView.GameArea.Controls.Add(view);
view.Dock = DockStyle.Fill;
view.BringToFront();
view.Show();
_mainView.GameArea.Controls[0].Focus();
}
This was the line that fixed it:
_mainView.GameArea.Controls[0].Focus();
Rather than going through the "view" variable and setting focus, I was able to set focus to the view this way.
I also had to remove the other views from the panel's collection as well:
_mainView.GameArea.Controls.Clear();
Related
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
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.
Currently I have a C# program with a windows form and then a user control template put onto the form. The user control template is really just used as a placeholder. I have a series of other controls which inherit from this user control template.
Each of those controls have navigation buttons like 'Continue' and 'Back' on them and each control knows which control needs to be loaded next. However what I need to figure out is an easier way to have variables that are global to these controls.
The only workaround I have is that I pass the form to each control when they are loaded and use variables inside of the form to read and write to. What would be the proper way to have each of these user control screens be built off of a base control which contained objects all of the controls could get to?
Sorry for the rambling nature of the post but I've been thinking about this problem all morning.
Here is some of the code:
Most of what I have written was based on hiding and showing the user controls so that content in the controls wouldn't be lost during navigation. I won't be needing to do that as eventually it will be loading the fields of data from a database.
Code for initially loading control from form click:
conTemplate1.Controls.Clear();
conInbound Inbound = new conInbound(this);
Inbound.Dock = DockStyle.Fill;
Inbound.Anchor = (AnchorStyles.Left | AnchorStyles.Top);
conTemplate1.Controls.Add(Inbound);
Code for Continue button inside of one of the controls:
if ((Parent.Controls.Count - 1) <= Parent.Controls.IndexOf(this))
{
UserControl nextControl = new conPartialClear();
nextControl.Dock = DockStyle.Fill;
Parent.Controls.Add(nextControl);
this.Hide();
Parent.Controls[Parent.Controls.IndexOf(this) + 1].Show();
}
else
{
this.Hide();
Parent.Controls[Parent.Controls.IndexOf(this) + 1].Show();
}
The best-practice for communicating from a control to a parent is to use events, and for communicating from a parent to a control is to call methods.
However, if you don't want to or can't follow this practice, here's what I would recommend.
Each UserControl has a ParentForm property that returns the Form that contains the control. If you know that the UserControl will always be attached to MyParentForm, you just cast the ParentForm and then you can access all public controls, methods, etc.
Here's what I mean:
public class conTemplate
{
public MyParentForm MyParentForm
{
get
{
return (MyParentForm)this.ParentForm;
}
}
}
This way, you can easily access any public members of MyParentForm. Your conInbound class could have code such as this.MyParentForm.GlobalSettings.etc..., and could even have access to any public controls.
I'm not totally sure I understand your problem. It sounds like you want the user control to "do something" with it's parent form. If that's the case, you may want to consider adding events to the UC and then handle them on the form itself.
Basically, for your UC's "continue", you'll have an event that's fired when it's pressed. You'll want to handle that in your form. I'm not real sure about the syntax from memory, or I'd work something out for you code-wise. But I think that's the route you'll want to take. Think of your UC like any other windows form control. If you add a button to your form, you assign it it's event method. Do the same with the UC.
I found this and thought it may be helpful. Scroll down to where it talks about UC's and events.
http://www.akadia.com/services/dotnet_user_controls.html
Hope this helps.
EDIT after new info from OP.
You could declare a global variable inside the UC of type yourForm and then set that variable to the ParentForm at run-time, if I'm understanding you correctly.
So, inside your UC Class, you could do:
private parentFormInstance;
then inside the constructor of the UC, you could set it as such:
parentFormInstance = this.ParentForm; (or whatever the property name is).
This allows you at design-time to use:
parentFormInstance.DoSomething();
without the compiler yelling at you.
Just basic advice, but if you can go back and make it easier on yourself, even if it takes some additional time re-working things, it'd be worth it. It may save you time in the long run.
I wanted to get default view of my form after some interaction with user.
In other words after some changes that has been implied by user, what command will return the form to the initial pop up appearance?
I have many controls, and calculations, so I dont want to go over the control one by one and set them null or their default value.
It would be great, as if I initate the form once more, some how.
You could try clearing the controls on the form, then calling the InitializeComponent() method.
while (Controls.Count > 0)
{
Controls[0].Dispose();
}
InitializeComponent();
EDIT:
Another Option that wouldn't cause any performance issues would be to utilize data binding. Create a data object that maps one to one with all of the fields you'd like to reset, then once it is time to reset the form simply set the data source of your form to a new instance of the data object.
You could do something like this too, assuming you just wanted to reset text on the controls. Call the following function with ResetControl(this) where "this" is your form. You'd want to check the control type so you don't have unintended consequences like wiping out label text.
private void ResetControl(Control control)
{
if (control.HasChildren)
foreach (var ctl in control.Controls)
ResetControl((Control)ctl);
if (control is TextBox)
control.ResetText();
}
Although, I still think you'll have far less bugs if you just dispose the form and create a new one (you could load an initial form that loads a user control, and dispose the user control, or use a mdi container form to load another form or something).
The best way is to write a personalized function ResetForm() and reset each component alone :
for TextBox ==> TextBox1.Text = "default text";
for ComboBox ==> ComboBox1.ResetText();
for DataGridView ==> DataGridView1.RowCount=1;
etc...
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?