C# Event driving between user controls on win forms - c#

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.

Related

Logging all button clicks in WinForms app

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);
}
}

How do you set a control's modifier at runtime?

private void referenceDesk_DoubleClick(object sender, EventArgs e)
{
tabControl1.TabPages.Add(new TabPage("Donkey Kong"));
}
there is no tabControl1.Modifier type command to use, and also can't use
new public TabPage("");
The Modifiers design-time property, controls member creation for the object you are modifying. It is not something you can change later. If you want to add tab pages to a tab control and you want to be able to change them later, define class members for them and assign appropriate access-modifier to them:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private List<TabPage> tabPages;
private void referenceDesk_DoubleClick(object sender, EventArgs e)
{
tabPages = new List<TabPage>();
tabPages.Add(new TabPage("First"));
tabPages.Add(new TabPage("Second"));
foreach (var tab in tabPages)
tabControl1.TabPages.Add(tab);
}
....
}
Designer code is not supposed to be user modified, as it gets re-written by Visual Studio every time you make changes to your form in the designer (as you have discovered).
One way forward it to move the control declaration and initialization to the non designer code file. However, that means your control will no longer appear in the designer.

Detect user controls inner controls focus

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());
}
}
}

C# set dataTable generated in formname.designer.cs as static

I want to know if it's ok to modify the formname.designer.cs and to set a variable that is generated from design mode as private as static:
private dtableAdapters.llist nameTable;// this to become static
public static dtableAdapters.llist nameTable;//like this
I read here C# Set Checkbox to Static that is not a good method.
Maybe I can do this in other way. Here is what I want to do:
I have a Form that contain more forms, opened in a panel. One form contains some comboboxes with values from the database. The problem is that when I add more values to the database from another form with a textbox, the combobox needs to be filled again. I thought that it could be easy if I update the combobox immediatlly after i add some values.
(combobox and the textbox -that add values in the database which are shown by combobox- are in different forms).
Do you have an other ideea of doing this? I have tried also to fill the combobox again when it's clicked but because I have more comboboxes I get some fatal errors when I click fast from one to another.
edit: as a last method: I could add a button and fill the combobox when the button is pressed, but I want to do it automatically
(winforms not web forms)
One approach is to fire an event on FormA when a value is added.
Form B can subscribe to the event and update the list.
The only tricky bit is that FormB needs a reference to FormA to hook up to the event.
Something like this...
FormA
public delegate void DataAddedEventHandler(object sender, EventArgs e);
public partial class FormA : Form
{
public event DataAddedEventHandler DataAdded;
private void AddButton_Click(object sender, EventArgs e)
{
//do The database stuff...
//fire the event
OnDataAdded();
}
private void OnDataAdded()
{
if (DataAdded != null)
{
DataAdded(this, new EventArgs());
}
}
FormB
public void HookupListener(FormA dataform)
{
//hook up the event to the handler
dataform.DataAdded += new DataAddedEventHandler(dataform_DataAdded);
}
void dataform_DataAdded(object sender, EventArgs e)
{
//refresh the combo box
}

Accessing Form's Control from Custom Control

I want to access the list box and add the item into it for my Custom control which is dynamically created on run time. I want to add the Item when I press the button place in the custom control, but it does not work. I have use the following code to work:
private void button1_Click(object sender, EventArgs e)
{
Form1 frm = new Form1();
frm.ABC = "HI";
}
the 'ABC' is the Public string on the form ie:
public string ABC
{
set { listBox1.Items.Add (value); }
}
the above string works fine when I use it form the Button on the form and it adds the value in the lsitbox but whent I use it form the custom control's button the text of the 'value' changes but it does not add the item in list box.I have also try it on tabel but does not help. I change the Modifires of the ListBox1 from Private to Public but it does not works. The above function works well in the form but cannot work from the custom control.
Thanks.
Expose an event ("ItemAdded" or whatever) in the child form that your main form can handle. Pass the data to any event subscribers through an EventArgs derived object. Now your mainform can update the UI as it please with no tight coupling between the two classes. One class should not know about the UI layout of another, it's a bad habit to get into (one that everyone seems to suggest when this question crops up).
What I think you should use is
this.ParentForm
So in your case it should be:
public string ABC
{
set { this.ParentForm.listBox1.Items.Add (value); }
}
The easiest way would be to pass the form down into your custom control as a parameter in the constructor that way you could access it from the custom control.
EX:
public class CustomControl
{
private Form1 _form;
public CustomControl(Form1 form)
{
_form = form;
}
private void button1_Click(object sender, EventArgs e)
{
_form.ABC = "HI";
}
}

Categories

Resources