I have a tab control that only responds to changing tabs with the mouse click.
Do I need to manually code in an event handler for tab control despite having the Surface SDK? Or is there a better control handler that I could use here?
I feel like this is entirely counter productive to the point of having the SDK. Especially because I plan on having a lot of different, unique tabs in my program and don't want to be handling each tab individually with nested ifs in a button_TouchDown function. I already have custom buttons that that have button_TouchDown setup and adding individual tab controls would be a headache and hell of a mess of code.
I tried searching but came up empty handed which makes me think that perhaps I am missing something and it should work. Is it because I have a predefined button_TouchDown function?
private void TabItem_TouchDown(object sender, TouchEventArgs e)
{
TabItem tab = sender as TabItem;
TabControl control = tab.Parent as TabControl;
control.SelectedItem = tab;
e.Handled = true;
}
XAML
<TabItem x:Name="hccontactTab" Header="Phone" TouchDown="TabItem_TouchDown">
Based on the above answer, but enhanced to account for scrolling per touch. Use a ClassHandler to handle this cleanly within your application (I use AutoFac's IStartable to auto-register it during building the container):
using System.Windows;
using System.Windows.Controls;
using Autofac;
namespace ...ClassHandlers
{
public class TabItemTouchClassHandler : IStartable
{
public void Start()
{
Register();
}
public void Register()
{
EventManager.RegisterClassHandler(typeof(TabItem), UIElement.TouchDownEvent, new RoutedEventHandler(OnTabItemTouchDown));
}
//must be static! otherwise memory leaks!
private static void OnTabItemTouchDown(object ender, routedEventArgs e)
{
var tab = sender as TabItem;
var control = tab?.Parent as TabControl;
if (control != null && !Equals(tab, control.SelectedItem))
{
control.SelectedItem = tab;
e.Handled = true;
}
}
}
}
Related
I need to be alerted before my entire form loses focus. The Deactivate event only triggers after it loses focus. LostFocus and Leave are only for controls.
I have also tried overriding WndProc but this only triggers after the message has been processed.
overriding PreProcessMessage only can be used for keyboard stuff, not form deactivation.
Dodgy Method
Even though this is a quick and hacky way of doing it, changing Input Language is unnatural to start with..
private void Form1_Deactivate(object sender, EventArgs e)
{
((Form)sender).Activate();
System.Diagnostics.Debug.WriteLine(this.ActiveControl.Name);
//Change Input Language here..
//Alt TAB to set focus to the application selected 5 milliseconds ago
SendKeys.SendWait("%{TAB");
}
Correct and orthadox method
How to monitor focus changes? and C#: Detecting which application has focus
Its using the Automation framework, Add references to UIAutomationClient and UIAutomationTypes and use Automation.AddAutomationFocusChangedEventHandler, e.g.:
public class FocusMonitor
{
public FocusMonitor()
{
AutomationFocusChangedEventHandler focusHandler = OnFocusChanged;
Automation.AddAutomationFocusChangedEventHandler(focusHandler);
}
private void OnFocusChanged(object sender, AutomationFocusChangedEventArgs e)
{
AutomationElement focusedElement = sender as AutomationElement;
if (focusedElement != null)
{
int processId = focusedElement.Current.ProcessId;
using (Process process = Process.GetProcessById(processId))
{
Debug.WriteLine(process.ProcessName);
}
}
}
}
Got it, this hack works perfectly.
private void MyForm_Deactivate(object sender, EventArgs e)
{
Thread.Sleep(200); //delay to allow external tab time to open
Form f1 = new Form(); //create a new form that will take focus, switch input, then terminate itself
f1.Shown += new EventHandler((s, e1) => { f1.Activate(); InputLanguage.CurrentInputLanguage = InputLanguage.DefaultInputLanguage; f1.Close(); });
f1.Show();
}
EDIT: upon further testing I have found this to be equally unreliable. It doesn't seem like there is a good way to do this at all.
For now I am tracking the mouse and keyboard to detect when the user is about to deactivate it. Obviously a mouse and keyboard hook is a horrible solution but its the only reliable solution so far.
I know it seems like an odd request but I have this layout. With it I'm very much enjoying and taking advantages of the FlowLayoutPanel. I disable it to disable all child controls. But there are cases I have a link in there I want enabled all the time. Is this possible?
flowLayoutPanel1.Enabled = false; // disable panel and all child controls
linkLabel1.Enabled = true; // re-enable somehow the link that was disabled inherently
I'm only asking because there maybe a way to do this, but if it's officially a bad design case then I will rethink my solution.
Previously to get around this I overlayed the control I want to enable/disable independently of the parent control. I would line it up and position the control so it appeared to be with the group. I hated this. It makes design difficult as drag and drop just puts it back into the container and also totally defeats the purpose of using a FlowLayoutPanel.
Thanks in advance.
Using the suggestion from Anthony. I've managed the following code that solves my dilemma. Using LINQ! (I always forget about using LINQ) I adjusted a bit with the extension methods and ability to just hammer in controls through a parameter array of what I do not want disabled. Very flexible to say the least.
private void button1_Click(object sender, EventArgs e)
{
// "reset"
flowLayoutPanel1.EnableAll();
}
private void button2_Click(object sender, EventArgs e)
{
// disable but the provide controls
flowLayoutPanel1.DisableBut(checkBox1);
}
public static class Extensions
{
public static void EnableAll(this FlowLayoutPanel container)
{
foreach (Control control in container.Controls.OfType<Control>())
control.Enabled = true;
}
public static void DisableBut(this FlowLayoutPanel container, params Control[] but)
{
foreach (Control control in (container.Controls.OfType<Control>().Where(x => (!but.Contains(x)))))
{
control.Enabled = false;
}
}
}
With suggested tweaks for performance, extensibility, etc.
public static class Extensions
{
public static void EnabledBut(this Control container, Boolean enabled, params Control[] but)
{
foreach (Control control in (container.Controls.OfType<Control>().Except(but)))
control.Enabled = enabled;
}
}
I have a user control with a scrollbar (scrollbar appears as a contained user control, which inherits from Panel, is too large). When using the mouse to scroll all is well, but trying to scroll with the mousewheel dont work.
My solution here is to set focus to my child-control in an eventhandler for Scroll. This works. Now the question; Will this result in a lot of unecessary calls to childControl.Focus()? Is there a more neat way of doing this?
Edit: I think I was a bit unclear with my question so Rephrasing the question:
is
private void ChildControl_OnScroll(object sender, ScrollEventArgs scrollEventArgs)
{
this.childControl.Focus();
}
a bad way of setting the focus? I.e. will the focus be set mutliple times each time I scroll? or rather, will this cause (tiny) performance issues.
Here's another approach that gives focus when the scrollbar area of panel1 inside SomeUserControl is clicked. It uses NativeWindow so you don't have to change the panel in your UserControl. This way Focus() will only be called once, when the mouse goes down in the scrollbar area:
public partial class SomeUserControl : UserControl
{
private TrapMouseDownOnScrollArea trapScroll = null;
public SomeUserControl()
{
InitializeComponent();
this.VisibleChanged += new EventHandler(SomeUserControl_VisibleChanged);
}
void SomeUserControl_VisibleChanged(object sender, EventArgs e)
{
if (this.Visible && trapScroll == null)
{
trapScroll = new TrapMouseDownOnScrollArea(this.panel1);
}
}
private class TrapMouseDownOnScrollArea : NativeWindow
{
private Control control = null;
private const int WM_NCLBUTTONDOWN = 0xA1;
public TrapMouseDownOnScrollArea(Control ctl)
{
if (ctl != null && ctl.IsHandleCreated)
{
this.control = ctl;
this.AssignHandle(ctl.Handle);
}
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_NCLBUTTONDOWN:
if (this.control != null)
{
Rectangle screenBounds = control.RectangleToScreen(new Rectangle(0, 0, control.Width, control.Height));
if (screenBounds.Contains(Cursor.Position))
{
control.Focus();
}
}
break;
}
base.WndProc(ref m);
}
}
}
This might be overkill for your scenario, but it demonstrates one way to trap lower level messages. As said before, you could also derive from Panel to achieve the same affect. You could also trap messages at the application level with IMessageFilter.
The MouseWheel event is an event that "bubbles". Windows sends it to the control that has the focus, regardless of where the mouse cursor is located. The most typical problem is that you have a control that cannot receive the focus. A Panel for example.
This changes when you put a control on the panel. Now that control can get the focus and gets the MouseWheel message. It won't have any use for it so the message passes to its parent. Which does have a use for it, the panel scrolls as expected.
You can get a focusable panel control from this answer. A generic "make it work like a browser or Office program" solution from this question
If childControl has a MouseEnter() event then use that instead:
private void childControl_MouseEnter(object sender, EventArgs e)
{
childControl.Focus();
}
Then the mouse wheel events should be direct to childControl.
I am having a treeview with some nodes. I am also having a panel. I have taken some usercontrol forms and i will load those usercontrols when corresponding node is selected from the child hood. Now what i need is have some validations like if i left the text box empty i will have some tooltips displayed to the user. Suppose if i click on first node i will have a user control loaded. With out giving any values if i hit ok i will have some tool tips as follows
Now if i select the second node from the tree still the tooltips getting displayed i would like to hide those
Any Help please
my code for rasing error tooltips is as shown below
public class TestClass
{
public void RequiredText(TextBox txtTemp, ToolTip newtoolTip)
{
if (txtTemp.Text != string.Empty)
{
txtTemp.BackColor = System.Drawing.Color.White;
newtoolTip.Hide(txtTemp);
}
else
{
txtTemp.BackColor = System.Drawing.Color.Tomato;
newtoolTip.Show("Required", txtTemp);
}
}
}
But this was done in the use control form.
I haven't yet mastered the art of reverse-engineering code from a screenshot. I'm guessing that you don't dispose the previous user control when you select a new one. Allowing the tool tip to stay visible. Use code like this:
private UserControl currentView;
public void SelectView(UserControl view) {
if (currentView == view) return;
if (currentView != null) currentView.Dispose();
if (view != null) this.Controls.Add(view);
currentView = view;
}
And call SelectView() from the TreeView's AfterSelect event handler.
Have you tried the Hide method?
http://dotnetperls.com/tooltip
Got the answer just written Usrcntrl_Leave event for every user control as
private void usrcntrlPPD_Leave(object sender, EventArgs e)
{
this.Dispose();
}
This solved my problem :)
private void timer1(object sender, EventArgs e)
{
count++;
if (count == 2)
{
toolTMensaje.SetToolTip(textBox1,"");
toolTMensaje.Hide(textBox1);
count = 0;
timer1.Stop();
}
}
i am developing keyboard control, very simple embedded on a form. using sendkey class to perform char entry. to make this functional is required to know previous selected control.
Something like the following should do the trick:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace DragDropTest
{
public partial class LostFocusTestForm : Form
{
private Control _lastControl;
public LostFocusTestForm()
{
InitializeComponent();
TrapLostFocusOnChildControls(this.Controls);
}
private void finalTextBox_Enter(object sender, EventArgs e)
{
MessageBox.Show("From " + _lastControl.Name + " to " + this.ActiveControl.Name);
}
private void AllLostFocus(object sender, EventArgs e)
{
_lastControl = (Control)sender;
}
private void TrapLostFocusOnChildControls(Control.ControlCollection controls)
{
foreach (Control control in controls)
{
control.LostFocus += new EventHandler(AllLostFocus);
Control.ControlCollection childControls = control.Controls;
if (childControls != null)
TrapLostFocusOnChildControls(childControls);
}
}
}
}
Expanding on David's answer. This is how you can use the Enter event and a variable to store the last control:
using System;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
Control lastControlEntered = null;
public Form1()
{
InitializeComponent();
foreach (Control c in Controls)
if (!(c is Button)) c.Enter += new EventHandler(c_Enter);
}
void c_Enter(object sender, EventArgs e)
{
if (sender is Control)
lastControlEntered = (Control)sender;
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = lastControlEntered == null ? "No last control" : lastControlEntered.Name;
}
}
}
To run this code, add a few textboxes and other control to a Form in Visual Studio, and add a button and a label and attach the button's click handler to button1_Click. When you press the button, the last control you were in before pressing the button is displayed in the label. Edit this code to suit your needs.
You need to store it in a variable. For Control objects, there's "Enter" event
Another strategy is to use an Extension Method to extend Control.ControlCollection, and then using some indirection (a delegate) recursively parse the Controls collection of the Form adding a special "Enter" handler that updates a static variable. By keeping track of the previous Active Control and the current Active Control, you then have what you need ... if I understand your question fully. Here's an example that requires FrameWork 3.5 or 4.0.
// in a Public static class :
using System;
using System.Collections.Generic;
using System.Windows.Forms;
private static EventHandler _event;
// extension method on Control.ControlCollection
public static void SetEnterEvent(this Control.ControlCollection theCollection, EventHandler theEvent)
{
_event = theEvent;
recurseSetEnter(theCollection);
}
// recurse all the controls and add the Enter Event :
public static void recurseSetEnter(Control.ControlCollection aCollection)
{
foreach (Control theControl in aCollection)
{
// "weed out" things like internal controls of the NumericUpDown Control
// String.IsNullOrWhiteSpace is FrameWork 4.0
// use Trim() followed by String.IsNullOrEmpty for FrameWork 3.5
if (!String.IsNullOrWhiteSpace(theControl.Name))
{
Console.WriteLine("setting enter handler for : " + theControl.Name + " : " + theControl.GetType().ToString());
theControl.Enter += _event;
}
if (theControl.Controls.Count > 0) recurseSetEnter((Control.ControlCollection)theControl.Controls);
}
}
So how do we use this : in a Form :
First let's define an actual Event handler that is going to actually execute when the Enter event is encountered on any control :
We'll keep the current active control, and the previous active control, in public static variables :
public static Control theActiveControl = null;
public static Control thePreviousControl = null;
And here's the code that does the updating :
private void control_enter(object sender, EventArgs e)
{
thePreviousControl = theActiveControl;
theActiveControl = sender as Control;
Console.WriteLine("Active Control is now : " + theActiveControl.Name);
}
Now in the Form_Load event or elsewhere we just need to wire-up the events :
// in a Form**
// define a delegate for the enter Event
private event EventHandler enter = delegate { };
// in the form load even or somewhere : assign an actual event handler to the delegate
enter += new EventHandler(control_enter);
Finally we invoke the extension method on the Controls Collection of the Form :
this.Controls.SetEnterEvent(enter);
Discussion : a WinForm maintains an 'ActiveControl collection : this will contain a pointer to the most recently activated control no matter how deeply nested it is in one or more containers : ... some containers (like Panels) do not register as active controls in this collection even though they have Leave/Enter events ... controls are going to become the ActiveControl when they are used/selected/entered-into/focused-on, etc. Unfortunately there's no "ActiveControlChanged" event.
[edit] in practice I am developing this using"filters" so I can selectively skip over certain object types, or, for example, look for some "key" (in the control name or its tag) to determine whether or not to add the handler ... yes ... it's an experiment. [edit]
[edit] note that some controls like PictureBox expose no 'Enter event, but this code does not cause an error : my long-range goal is to find a way to test, without reflection, whether a particular control does expose a given 'event before I install one : since I consider it bad practice to just let things like PictureBox "wiggle through." So far I have not found the right "test" for "container-ness" ("is ControlContainer" turned out to be the wrong track). You may note also that Panels, for example, expose an 'Enter event, but it's only fired when some Control inside the Panel is activated. [edit]
Hope this is helpful. I am sure this could probably be written more elegantly using Lambdas, but as yet I am a "larva" feeding on the leaves of Skeet in that regard :)
You can do this by
string activeControl= this.ActiveControl.Name
You have to track this yourself. Write a trivial UIElement.LostFocus handler which puts the sender into a "last control with focus" variable, and you're done.
NOTE: WPF. Not sure if you're doing that or WinForms. I've been doing so much WPF lately I have it on the brain.