Is it possible to detect whether a control has been got focus in a user control? I mean not some controls that we add in a user control at its design time rather which controls we add them after using user control on the form. A mean example is panels. My user control acts like as a panel and I want to detect when a contained(nested) control on my user control got any focus I do my thing.
Thank ya all!
The way that I would approach this is when the UserControl is created and you are not in design mode, cycle through each of the controls within the user control adding hooks to their GotFocus events and pointing the hook to a method of the UserControl (say ChildControlGotFocus) that in turn raises an event that host of the user control can use.
For example, here is a sample UserControl that implements this functionality:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
if (!this.DesignMode)
{
RegisterControls(this.Controls);
}
}
public event EventHandler ChildControlGotFocus;
private void RegisterControls(ControlCollection cControls)
{
foreach (Control oControl in cControls)
{
oControl.GotFocus += new EventHandler(oControl_GotFocus);
if (oControl.HasChildren)
{
RegisterControls(oControl.Controls);
}
}
}
void oControl_GotFocus(object sender, EventArgs e)
{
if (ChildControlGotFocus != null)
{
ChildControlGotFocus(this, new EventArgs());
}
}
}
Related
I have a main form (form1) which has a panel (panel1) -- see pic.
Form1 pic
Panel1 loads one of two different user controls based on which button is pressed (to simulate screen changes). I have a button on user control 1 which needs to act (change text) on user control 2.
The issue I have is the user controls are dynamically created with a button press on form 1 (see code below) which is causing me issues trying to link events-
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
panel1.Controls.Add(new Screens.UC1());
}
private void button1_Click(object sender, EventArgs e)
{
foreach (Control ctrl in panel1.Controls)
{
ctrl.Dispose();
}
panel1.Controls.Add(new Screens.UC1());
}
private void button2_Click(object sender, EventArgs e)
{
foreach (Control ctrl in panel1.Controls)
{
ctrl.Dispose();
}
panel1.Controls.Add(new Screens.UC2());
}
}
What is the best way to deal with linking these kinds of items with events when the instance of the objects are dynamically created. I also tried making instances of the screen and then referencing to those, but that ran into scope issues.
Code for UC1 (user control 1)
public partial class UC1 : UserControl
{
public UC1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
//Event to change text on UC2
}
}
Code for UC2 (user control 2)
public partial class UC2 : UserControl
{
public UC2()
{
InitializeComponent();
}
public void WriteText(object sender, EventArgs e)
{
label2.Text = "Text Changed...";
}
}
Any help greatly appreciated.
Why dispose and create all those controls when the operator presses a button?
Better is to create two UserControls. One with all the Controls you want to show when operator presses button 1 and one with all the Controls you want to show when operator presses button 2.
To create a user control use menu Project - Add User Control, or right click in solution explorer on your project and select add new item.
Layout your user controls with all the controls your want to show. Add event handlers etc.
Then in your form:
private readonly UserControl userControl!;
private readonly UserControl userControl2;
public MyForm()
{
InitializeComponent()
this.userControl1 = new UserControlA(...);
this.userControl2 = new UserControlB(...);
// make sure that the user controls are Disposed when this form is Disposed:
this.Components.Add(this.userControl1);
this.Components.Add(this.userControl2);
}
void OnButton1Clicked(object sender, ...)
{
// remove userControl2 from the panel
this.Panel1.Controls.Remove(this.userControl2);
// add userControl1 to the panel
this.Panel1.Controls.Add(this.userControl1);
}
This way all the overhead of creating / adding / positioning / add event handlers and all cleanup is only done once: during construction of your form. Switching the user controls will be done in a flash
I am not sure what you are trying to accomplish, but it looks like you are trying to change the state of objects from other objects that cannot have references to the objects they are trying to change.
In this case, I would create a type that functions as some kind of manager that subscribes to events of all of these controls. You can create your own events within a UC class, or just use the Windows Forms click event like you are already doing.
Since the handler of the events are defined in the manager, you can easily write logic that will work on the other user controls, as long as the manager has references to them.
Like this:
public class ClickTrafficer {
private UC target;
public void HandleClick(object sender, UCClickHandlerEventArgs ea) {
target.WriteText(ea.TextToWrite);
}
}
public Form1()
{
InitializeComponent();
var trafficer = new ClickTrafficer();
var screen1 = new Screens.UC1();
screen1.Click += trafficer.HandleClick;
panel1.Controls.Add(screen1);
}
This is a crude idea of what you could do. Missing here are the logic to set whatever the target field must be set to. You need to create logic that tells the trafficer which control sets which control's text.
Also, the ClickTrafficer I created uses a custom event with custom eventargs, you need to define those or find a way to pass the necessary information through the built in events. Creating events is really easy though so you can look that up online.
I'm looking to add a log statement for every WinForms button click that the user clicks, and ideally, an indentifier of the parent form (such as the title bar). I have seen posts logging all mouse clicks, but i'm interested in just logging button clicks. I have read the accepted answer here and adapted it:
public class ButtonLogger
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(ButtonLogger));
public static void AttachButtonLogging(Control.ControlCollection controls)
{
foreach (var button in controls.OfType<Button>())
{
button.Click += LogButtonClick;
}
}
private static void LogButtonClick(object sender, EventArgs eventArgs)
{
Button button = sender as Button;
Logger.InfoFormat("Button clicked: {0} ({1})", button.Text, button.Parent.Text);
}
}
This class is used at the end of a constructor in a form, e.g. :
ButtonLogger.AttachButtonLogging(this.Controls);
The problem I'm facing with this is that the Controls property doesn't seem to have a reference to my buttons. Presumably this is because the buttons aren't added directly to the form, but rather, another control that is in the Controls property. However, the Controls property only contains a single control, a ToolStrip.
Is there a way I can harness all of the buttons on a form, regardless of their parent container? My final goal is to add a log statement to my buttons, so if this can be accomplished some other way besides button click event methods, then I'm open to that as well
I believe you need to search for buttons recursively:
public static void AttachButtonLogging(Control.ControlCollection controls)
{
foreach (var control in controls.Cast<Control>())
{
if (control is Button)
{
Button button = (Button)control;
button.Click += LogButtonClick;
}
else
{
AttachButtonLogging(control.Controls);
}
}
}
One thing you could consider is to create a subclass of the standard Button class, and let the buttons themselves do the logging. Of course, you'd have to go around and substitute all the buttons in the application for your own implementation, but it should be possible to do that with a global search+replace.
Here's an example implementation:
public class LoggerButton : Button
{
private static readonly ILog Logger = LogManager.GetLogger(typeof(LoggerButton));
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
Logger.InfoFormat("Button clicked: {0} ({1})", this.Text, this.Parent.Text);
}
}
I have a form and two custom UserControl that I made myself. one control has some buttons that each of these button have theire Tag property set to an array of PointF. I have another UserControl that has a ObservableCollection<PointF[]> that I set its event handler to draw the lines if data is being added to it. This works fine If I put the data points on its own class...just make to sure it works.
No my problem is, having this two control in one form, how can I set the click event of buttons in the first control, to add data points to the second control?
This two controls are both in two different projects in my soloution. and the form that these to controls are being showed in, is also in a different project (it is the launching project of soloution)
Add an event to the first control.
public event EventHandler<EventArgs> Control1ButtonClicked;
private void OnClicked()
{
var handler = Control1ButtonClicked;
if (handler != null)
{
handler(this, new EventArgs());
}
}
private void Button1_Click(object sender, EventArgs e)
{
OnClicked();
}
Add a property to the second control
public ObservableCollection<PointF[]> MyPoints{ get; set;};
Then in your main application add a listener
userControl1.Control1ButtonClicked += new EventHandler<EventArgs>(userControl1_Control1ButtonClicked);
void userControl1_Control1ButtonClicked()
{
//Do Something to control 2
userControl2.MyPoints.Add() = //Whatever
}
You could add a public method on the second usercontrol that receive an array of PointF, then inside this method you could add the PointF to your collection.
EDIT: To handle the click event inside the first user control
inside the first usercontrol add the event and the delegate required
public delegate void OnClickPointDataEvent(object sender, PointF[] data);
public event OnClickPointDataEvent ClickPointData;
then form_load event subscribe to the usercontrol1 event
uc1.ClickPointData += new UserControl1.OnClickPointDataEvent(form_subscribe_event);
private void form_subscribe_event(object sender, PointF[] data)
{
uc2.SomePublicMethod(data);
}
and finally, inside the first usercontrol button click call the code that handle the event inside the form
....
if(ClickPointData != null)
ClickPointData(pointf_array);
...
I am working on my first C# and .NET project that uses WinForms, as WPF would be an overkill for our purposes. I created a so-called ButtonMenu that comprises of all menu points, which are buttons. This class is derived from the Windows class Control.
One of the buttons of the ButtonMenu is the "Culture" button that, when pressed in this single-touch application, should change the language of all the forms the application has.
Originally, the ButtonMenu was just a kind of overlay class that accessed the controls of the BaseForm and contained methods. The BaseForm was holding the buttons inside a GroupBox of its own. Later, I run into problems with this kind of design and decided to make a separate control out of it.
My question
How can I create an event (or something similar to it) that can be caught by BaseForm, where the ButtonMenu is placed? The BaseForm can currently not react on this event and cannot change the language of all its own controls, such as text fields and buttons.
Thank you for your help!
What I have tried till now is shown below. Unfortunately, I cannot reach the marked line.
public class BaseForm : Form
{
[…]
protected static ButtonMenu m_ButtonMenu = null;
protected override void OnResize(EventArgs e)
{
[…]
m_ButtonMenu = ButtonMenu.GetInstance(m_CurrentCulture, Size);
m_ButtonMenu.Visible = true;
[…]
}
public override void UpdateWidgets()
{
[…]
try
{
[…]
// Translate button menu into current language:
m_ButtonMenu.AdaptButtons(m_CurrentCulture);
}
catch (ArgumentOutOfRangeException aaore)
{
[…]
[…]
}
protected void InitializeWidgets()
{
{
string strMethod = Name + ":InitializeWidgets(): ";
m_ButtonMenu = ButtonMenu.GetInstance(m_CurrentCulture, Size);
SuspendLayout();
Controls.Add(m_ButtonMenu);
m_ButtonMenu.Top = Height - m_ButtonMenu.Height;
ResumeLayout();
[…]
m_ButtonMenu.Click += new System.EventHandler(this.ButtonMenu_CultureClick);
}
private void ButtonMenu_CultureClick(object sender, EventArgs eas)
{
int iSelection = listViewMessages.SelectedIndices[0]; // <<<<< NEVER REACHED!
[…]
}
Just define an event in your class.
Whenever you want it to fire, call it.
// field
event EventHandler somethingHappened;
// in a method:
var threadSafeCopy = somethingHappened;
if(threadSafeCopy != null)
{
threadSafeCopy(this, e);
}
Where e is an instance of EventArgs or a sub type of EventArgs.
I have a control that handles commenting. In this control, I have set a delegate event handler for sending an email.
I then have various types of controls, e.g. blog, articles etc, each of which may or may not have the commenting control added (which is done dynamically with me not knowing the id's), i.e. the commenting control is added outside this control. Each of these controls handles it's emailing differently(hence the event).
What I'm trying to determine, is how to assign the event in the parent control. At the moment, I'm having to recursively search through all the controls on the page until I find the comment control, and set it that way. Example below explains:
COMMENTING CONTROL
public delegate void Commenting_OnSendEmail();
public partial class Commenting : UserControl
{
public Commenting_OnSendEmail OnComment_SendEmail();
private void Page_Load(object sender, EventArgs e)
{
if(OnComment_SendEmail != null)
{
OnComment_SendEmail();
}
}
}
PARENT CONTROL
public partial class Blog : UserControl
{
private void Page_Load(object sender, EventArgs e)
{
Commenting comControl = (Commenting)this.FindControl<Commenting>(this);
if(comControl != null)
{
comCtrol.OnComment_SendEmail += new Commenting_OnSendMail(Blog_Comment_OnSendEmail);
}
}
}
Is there an easier way?
EDIT:
The reason I ask is that if I search from this.Page as the initial control, I am worried about time taken to search down the control tree to find it. Each different type of page would be different in how many control it would have. On some testing, it returns back quite quickly the result.
You could override the AddedControl event of your Blog class and check if the added control is instance of type Commenting. Something like:
public partial class Blog : UserControl {
protected override void AddedControl(Control control, int index) {
base.AddedControl(control, index);
Commenting commentingControl = control as Commenting;
if (commentingControl == null) return;
commentingControl.OnComment_SendEmail += new Commenting_OnSendMail(Blog_Comment_OnSendEmail);
}
}
Of course, you can put this code on a base class of all your "commentable" user controls and have an abstract method to actually handle the event.
Just one thing: the AddControl event happens AFTER the Page_Load, so be careful.
Cheers,
André