How to call a UserControl's methods from a Windows.Form - c#

The form has a button and a panel with a UserControl which has a ListBox and a TextBox.
When I click the Windows.Form button it calls UserControl's Add()
listBoxTitles.Items.Add(metroTextBoxTitles.Text);
metroTextBoxTitles.Clear();
Which simply adds whatever the UserControl's TextBox.Text has to the UserControl's ListBox.
For some reason nothing happens when i click the button.
Why. Nothing on the UserControl can be changed or used? Or does it change but doesn't update/show what's going on?

The best way to deal with communication between containers is to implement an observer class
The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.
(wikipedia)
the way i do this is creating an Observer class:
1 public delegate void dlFuncToBeImplemented(int signal);
2 public static event dlFuncToBeImplemented OnFuncToBeImplemented;
3 public static void FuncToBeImplemented(int signal)
4 {
5 OnFuncToBeImplemented(signal);
6 }
so basically: first line says that there would be a function that somebody else will implement
second line is creating an event that occur when the delegated function will call
and the third line is the creation of the function that calls the event
so in your UserControl you should add a function like this:
private void ObserverRegister()//will contain all observer function registration
{
Observer.OnFuncToBeImplemented += Observer_OnFuncToBeImplemented;
/*and more observer function registration............*/
}
void Observer_OnFuncToBeImplemented(int signal)//the function that will occur when FuncToBeImplemented(signal) will call
{
MessageBox.Show("Signal received!", "Atention!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
and in your Form you should do something like:
public static int signal = 0;
public void button1_Click(object sender, EventArgs e)
{
Observer.FuncToBeImplemented(signal);//will call the event in the user control
}
and now, you can register this function to a whole bunch of other controls and containers and they will all get the signal
I hope this would help :)

So, what was happening was, when you create a UserControl and add it to the Window.Form the Form's designer already initiates that UserControl:
private UserControl userControl1;
So in order to solve the issue i just needed to use the UserControl which the designer code created:
usercontrol1.add();
And everything is working perfectly.

Related

Calling a method in a UserControl from another UserControl

I've been researching this problem for some time now and I cannot seem to find a solution that works. I want to call a method a UserControl from a method in another UserControl.
In UserControl1 I have a method:
public void update(int lineNum, double price)
{
// do stuff...
// then call the method in another UserControl
UserControl2 uc2 = new UserControl2();
uc2.refreshList();
}
And in UserControl2, I have a method:
public void refreshList()
{
// do stuff....
}
Of course, I have tried other methods of doing this other than simply creating an object of the UserControl and calling the method that way but nothing seems to work.
Having such dependencies between user controls is not good: it limits the use and makes them hardly testable in isolation.
You have several options to achieve what you want.
Let the caller do the orchestration
You did not state where the code resides that calls update. But since the method is public, I assume that this is called from outside UserControl1. Maybe it is possible to put the userControl2.refreshList() call there just after the call to userControl1.update()
Use an event
You could define an event in UserControl1 e.g. public event EventHandler UpdatePerformed and raise it at the end of the update method. Then in your form, subscribe to this event and call refreshList from there:
userControl1.UpdatePerformed += (s, e) => userControl2.refreshList();

What is the most elegant way to call an event of another Form?

Let's say I have a Form called Form1 which will somehow (how is not relevant) calls another form Form2
(Form1)
Form2 f2= new Form2();
f2.ShowDialog();
This Form2 has a button that will do some operation everytime the user clicks on this button.
However, I want that the first time i.e. when Form2 is just shown, the code in the button (some operation) gets executed.
In other words I have to be able to call the code in Form2's button_Click which is private.
Now I can think of some ways to make this possible(making the click event public etc) , but my question is what is the most elegant (or correct) way to do this?
I would add a property to Form2 to tell the form I like to automatically executed an action.
class Form2
{
public bool AutoExecuteSomeOperation { get; set; }
}
In Form1, you would set that property and in Form2 you would check and execute appropriate code if the property was set.
I would recommend that you refactor button_Click to call another method which you can also call for automatic execution. I like to keep event handler simple and executed only for the event on the control that served to name the event handler. Thus, you know that button_Click is an handler for a Click event on a control named button. It makes the code easier to maintain.
You can decide if you want to reset the property once the code is executed or you can add some validation that the property changes are valid. For exemple, you might want to ensure that the property is called before displaying the form.
In all cases, you should avoid having any reference to a control from an external form. Only Form1 itself should know that it contains a button. Any use from outside world should be done through a public property or public event of the form. That way, if you decide that the button should be replaced by an hyperlink, a menu item, a checkbox or anything else Form1 does not need to be updated. This is very similar to what should be done for UserControl. The less internal details leak, the easier it will be to make internal changes without having to update all caller.
The easiest approach is just make it public, however its not the bastion of great design.
Decoupled messaging is probably where you want to be, event aggregator or any pub sub method messaging system. This is a more modern and scalable approach, the participants need not know about each other allowing you to make the methods private and giving you a more maintainable decoupled solution, and keep your classes self consistent.
Unity, MvvmLight both have these sorts of messaging systems, however there are lots of them.
Example of how this might work
public Form1()
{
InitializeComponent();
EventPublisher.Instance.Subscribe<NewUserCreated>
(n => listBoxUsers.Items.Add(n.User.Name));
}
...
// some other class
private void Form2()
{
var user = new User()
{
Name = textBoxUserName.Text,
Password = textBoxPassword.Text,
Email = textBoxEmail.Text
};
EventPublisher.Instance.Publish(new NewUserRequested(user));
}
Move the code from the OnClick event into its own method (e.g. "DoWork"), then call that method from the OnClick event.
Either call it when you create the form
var frm = new demoForm();
frm.DoWork();
frm.Show();
Or call it in the forms constructor.
public partial class demoForm : Form {
public demoForm() {
InitializeComponent();
DoWork();
}
private void button1_Click(object sender, EventArgs e) {
DoWork();
}
public void DoWork() {
//Code here
}
}

Access a ListView located in a UserControl from the MainForm in Winforms

I have an app that uses a side menu, and for each button (there are 3) on the left side menu, it changes the pages shown.
I tried doing it with multiple panels, but it's a nightmare to maintain in designer, and it's probably not a very good programming habit, I expect.
So I search and found what seemed to be a great idea: UserControl.
But as usual, it's not that simple (for a badly self-taught guy like me)
The general flow of the program is as follows:
a Btn_uc1_Check button that gathers informations and displays them in a uc1_ListView,
a Btn_uc2_Seek button that gathers informations on the net based on the uc1_ListView , and displays them on uc2_ListView,
a Btn_uc3_compile that compiles the info from uc2_ListView into a file,
a Clear button that clears the ListView depending on the UserControl on screen.
Now to the problem:
How on earth do I gain access to a ListView located in a UserControl to be able to read, clear, and add items from the MainFrom or from another UserControl?
I searched and honestly found nothing corresponding to what I needed?
Quite many questions.
You can gain access to any controls in UC. Just change the property "Modifiers" of the ListView in your UC to "Public".
Set that method to public. Do not use keyword "static". Each control
in your form is an instance of a class, not a static class actually. In the main form, create a button and double click on it in VS designer. A method will automatically generated, something like private void button1_Click. When the button is clicked, all of the code lines in button1_Click will run.
Create a public event handler of your user control, then pass the method in main to the handler.
So the UC class will be similar to this:
public event EventHandler button_UC_Click_handler;
public UserControl1()
{
InitializeComponent();
}
private void button_UC_Click(object sender, EventArgs e)
{
button_UC_Click_handler.Invoke(sender, e);
}
In main form:
public MainForm()
{
InitializeComponent();
userControl11.button_UC_Click_handler += UserControl11_button_UC_Click_handler;
}
private void UserControl11_button_UC_Click_handler(object sender, EventArgs e)
{
MessageBox.Show("You have clicked it!");
}
Good luck!

How to raise a generic event?

Background
The question title might be a little misleading, but I'm not sure how to quickly ask the question. I'm building a winforms app and following the MVP design pattern, with passive views. In the main view (form) for my application, there is a navigation panel that hosts buttons that when clicked will open another view (form). I'm attempting to create generics buttons:
// The type T represents the view (form) that should be opened when the button is clicked
NavigationButton<T>
The presenter for the main view creates each button individually at run time:
// Code in Main Presenter - register each button
View.RegisterNavigationButton(new NavigationButton<IViewExample1>("Example 1")); // Pass in text to show on button
View.RegisterNavigationButton(new NavigationButton<IViewExample1>("Example 2"));
// Code in View
public void RegisterNavigationButton<T>(NavigationButton<T> button) where T : class, IView
{
// Add button to flow layout panel
_flpNavigation.Controls.Add(button);
// Subscribe to click event
button.Clicked += ButtonClicked<T>;
}
Now, if this wasn't a passive view, when a navigation button was clicked, you could potentially go ahead and create the new view directly from the main view:
// This is the method subscribed to the click event as shown in the above code
private void ButtonClicked<T>(object sender, EventArgs e) where T : class, IView
{
// The ApplicationController creates the new view (form) using an IoC container (Simple Injector)
ApplicationController.ShowModelessForm<T>();
}
But, this is a passive view, so it shouldn't be creating new views (forms)...
Each of my views implements an interface, and the presenters hold a reference to its accompanying view via that interface. The view interfaces define events that the presenter can subscribe to. In other words, the presenter can call methods directly from the view as long as the method is defined in the interface, but the view must raise events to communicate to the presenter.
Question
Considering this setup, how would I go about communicating to the presenter that it should be creating a new view? Each of my views implements an interface, and the presenters hold a reference to its accompanying view via that interface. The view interfaces define events that the presenter can subscribe to. Is it possible to set up some kind of event(s) to communicate the presenter to create a view given my generic setup?
** EDIT **
My road block is that I don't know how to define the events to raise, and how to raise them. Say I have two secondary views that I want to open, defined by the following views: IView1 and IView2. Do I have to have two separate eventhandlers defined in my main view, one for each secondary view? And then, once the button is clicked, how do I raise the appropriate event?
You decide what you need for event handlers. Look at the purpose of the button and raise the appropriate event. If you have multiple buttons with the same purpose, have them raise the same event. If you have another action that has the same purpose as the button, again have it raise the same event.
The event you raise is linked to the button's purpose and is basically ignorant of any things that may happen because the event was raised.
The event could be more literal "Details Button Clicked" or a bit more abstract "Detailed Data Requested".
On the subject of how to raise the event, https://msdn.microsoft.com/en-us/library/edzehd2t(v=vs.110).aspx has an example of a fairly standard technique to raise an event. (Paraphrased below)
class Counter
{
public event EventHandler DetailsButtonClicked;
protected virtual void OnDetailsButtonClicked(EventArgs e)
{
if (DetailsButtonClicked != null)
{
DetailsButtonClicked(this, e);
}
}
// provide remaining implementation for the class
}
In the presenter, subscribe to the event and perform the action, such as opening a new view.
More info on passing messages when raising events
You can use the generic EventHandler to pass messages when you call events. I would just encourage you to evaluate the code you come up with for readability and ability to easily refactor.
For example, avoid sending a string message that means something programmatically. Instead send an enum or constant value.
public void TryIt()
{
var z = new Counter();
z.DetailsButtonClicked += Z_DetailsButtonClicked;
z.OnDetailsButtonClicked("Greetings Earthlings");
}
private void Z_DetailsButtonClicked(object sender, CustomEventArgs e)
{
Debug.Print(e.Message);
}
public class CustomEventArgs : EventArgs
{
public CustomEventArgs(string message) { this.Message = message; }
public string Message { get; set; }
}
class Counter
{
public event EventHandler<CustomEventArgs> DetailsButtonClicked;
public virtual void OnDetailsButtonClicked(string message)
{
if (DetailsButtonClicked != null)
{
DetailsButtonClicked(this, new CustomEventArgs(message));
}
}
// provide remaining implementation for the class
}

Windows Forms Binding: is there an event similar to DataBindingComplete, but fired when ALL bindings are completed?

I need to change a certain DataGridView's property (a DataSourceUpdateMode for one of its binding) only when ALL of its initial data bindings are completed.
I tried subscribing to the "DataBindingComplete" event, but it's fired too many times (one or more time for each binding associated to the control); what I need is a more global "AllDataBindingsComplete" event, fired when the control is ready to be displayed to the user.
As a temporary workaround, I'm using the MouseDown event (I've assumed that when the user is able to click the control, it means that the control is displayed... :) and the events I'm playing with - SelectionChanged - are fired after the MouseDown):
protected override void OnMouseDown(MouseEventArgs e)
{
Binding selectedItemsBinding = this.DataBindings["SelectedItems"];
if (selectedItemsBinding != null)
{
selectedItemsBinding.DataSourceUpdateMode = DataSourceUpdateMode.OnPropertyChanged;
}
base.OnMouseDown(e);
}
It works, but it smells like an ugly hack A LOT (and it's called too many times, only one time is enough for my needs).
Is there a better way?
(yes, I'm trying to adopt MVVM in a Windows Forms project, and I've added a bindable "SelectedItems" property to the DataGridView...)
What I've done at the Windows Forms form level, and may be improvised down to just the control(s) you want, is to subclass the Windows Forms baseclass into my own. Then, in its constructor, attach an extra event call to the Load() event.
So when everything else is completely loaded, only THEN will it hit my custom method (of the subclass). Since it is the bottom of the call-stack chain being attached to the event queue, I know it's last and everything else is done... Here's a snippet of the concept.
public class MyForm : Form
{
public MyForm()
{
this.Load += AfterEverythingElseLoaded;
}
private void AfterEverythingElseLoaded(object sender, EventArgs e)
{
// Do my own things here...
}
}
This concept can be applied to the Init() function too if that's more appropriate for your control... Let everything else within it get initialized(), then do you the "AfterInitialized()" function.

Categories