1

I'd like to add a UserControl (UserControl2) to a panel on another form (MainForm) when the user clicks a button on the UserControl that is currently in the panel (UserControl1). UserControl2 should replace UserControl1 as the contents of the panel when this button is clicked.

I've tried figuring out how to use an event to communicate between UserControl1 and MainForm, but I just don't know where to start, as I can't find an example that is easily adaptable to my particular situation (Adding a control to a panel).

THIS question is similar, but doesn't quite fit what I'm trying to do (or at least I just don't know how to adapt it to what I'm trying to do)

I've tried this method, and get no errors, but my button doesn't do anything:

MainForm:

    private void SubscribeToEvent(MyUserControl1 Button_Click)
    {
        MyUserControl1 CurrentClass = new MyUserControl1();
        CurrentClass.Button.Click += new EventHandler(this.ButtonClickHandler);
    }

    private void ButtonClickHandler(object sender, EventArgs e)
    {
        MyUserControl2 MainPanelControls = new MyUserControl2();
        MainPanel.SuspendLayout();
        MainPanel.Controls.Clear();
        MainPanel.Controls.Add(MainPanelControls);
        MainPanel.ResumeLayout();
    }

UserControl1:

    public void Button_Click(object sender, EventArgs e)
    {
        //... Not sure what I'm missing here
    }

I've also tried this method, this time trying to implement something similar to the method described in the link near the top of my question. I know these are obviously wrong (otherwise I wouldn't need to ask the question), but I don't have enough knowledge on the subject to figure it out on my own:

UserControl1:

    public MyUserControl1()
    {
        InitializeComponent();
    }

    private MainForm mainForm = null;
    public MyUserControl1(Form callingForm)
    {
        mainForm = callingForm as MainForm;
        InitializeComponent();
    }

//...

    private void Button_Click(object sender, EventArgs e)
    {
        MyUserControl2 MainPanelControls = new MyUserControl2();
        mainForm.MainPanel.SuspendLayout();
        mainForm.MainPanel.Controls.Clear();
        mainForm.MainPanel.Controls.Add(MainPanelControls);
        mainForm.MainPanel.ResumeLayout();
    }

Now when I click Button I get an "Object reference not set to an instance of an object" error at mainForm.MainPanel.SuspendLayout(); (Or anything past that point).

I've also tried modifying Joh's answer, but end up with the same Null Reference Exception, this time on my Button_Click event at ButtonClickedToMainForm(this, e); I'm not sure if I need to create an instance of ButtonClickedToMainForm, or how to do that properly (if it's not something else, that is).

I have a feeling I've likely just placed some of this code in the wrong place, so I'm hoping someone more experienced may be able to help me sort that out.

UPDATE

This is my attempt at implementing Joh's answer, being so new to this, I'm not quite sure where I've messed up:

MainForm:

namespace MyProject
{
public delegate void ButtonClickToMainForm(object sender, EventArgs e);

public partial class MainForm : Form
{

    public MainForm()
    {
        InitializeComponent();
        UserControl1 userControl1 = new UserControl1();
        userControl1.Button1Clicked += userControl1_Button1Clicked;
    }

    private void userControl1_Button1Clicked(object sender, EventArgs e)
    {
        try
        {
            UserControl2 MainPanelControls = new UserControl2();
            MainPanel.SuspendLayout();
            MainPanel.Controls.Clear();
            MainPanel.Controls.Add(MainPanelControls);
            MainPanel.ResumeLayout();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
  }
}

UserControl1:

public partial class UserControl1: UserControl
{
    public event ButtonClickToMainForm Button1Clicked;

    public UserControl1()
    {
        InitializeComponent();
    }

    private void OnButton1Clicked(object sender, EventArgs e)
    {
        Button1Clicked?.Invoke(this, e);
    }

    private void Button1_Click(object sender, EventArgs e)
    {
        Button1Clicked(this, e);  //"Object reference not set to an instance of an object."
    }
}

I'm sure it's something simple I'm missing on UserControl1, I'm just not sure what.

Community
  • 1
  • 1
Patrick
  • 430
  • 6
  • 21
  • Please see the marked duplicate for detailed information regarding Winforms idioms for communication between components (`UserControl` and `Form` subclasses work the same here). Primarily, you should be looking at using events, to avoid coupling the implementation of your components. If you still have trouble after reviewing the related information (including but not limited to the marked duplicate), please provide a question with a good [mcve] that shows clearly what you've tried, and precise information about what that code does and how that's different from what you want. – Peter Duniho Feb 15 '16 at 20:23
  • Sorry, I think doing this might be way over my head. I read over the duplicate in the answer describing how to use an event to handle communicating between forms, and I can't understand it at all. I'm not even sure where to start in order to implement that into my project. I guess I'll just keep looking elsewhere. Thanks anyway. – Patrick Feb 15 '16 at 21:05
  • I've edited my question with a bit more info on the problem I'm having. – Patrick Feb 15 '16 at 21:51
  • @PeterDuniho is there any way of having this not marked as a duplicate? I've scoured through the linked question, and can't figure out a way to adapt anything posted in there to my particular problem. I can't see a way to accomplish what I need using a string, which seems to be the basis to all the answers you linked. Thanks. – Patrick Feb 16 '16 at 00:09
  • There is nothing about the answers in the marked duplicate that require the use of a `string` type. If you are unable to see how the answers in the duplicate apply to your own question, you would also not be able to understand a correct answer to your question. They really are the same question. – Peter Duniho Feb 16 '16 at 00:24
  • Note that even if that duplicate did not exist, there is not in fact enough detail in your question for anyone to provide a _specific_ answer; the best anyone could do would be to suggest that you implement an event in one type, such that the "separate control" (whatever that is) would raise the event, while the main form would subscribe and respond to the event, doing exactly what it does now. Indeed, your current implementation already subscribes to an event (the button's `Click` event); you'd just subscribe to the "separate control"'s event instead. – Peter Duniho Feb 16 '16 at 00:25
  • I've updated my question with more detail, and my attempt at implementing something similar to the linked question. – Patrick Feb 16 '16 at 00:29
  • @PeterDuniho I'm sorry to bother you, it's just I'm at a loss with this now. Would you mind taking one last look at my updated question to let me know if I'm on the right track please? Thank you. – Patrick Feb 16 '16 at 01:39
  • You've got three people who feel your question isn't a duplicate (see [revision history](http://stackoverflow.com/posts/35418308/revisions) for their names). I suggest you solicit _them_ to answer the question. All I'd do is provide the same information that already exists in the marked duplicate, which apparently would not help you. – Peter Duniho Feb 16 '16 at 05:25
  • Sorry, I'm still fairly new to this. I'm not trying to be a pain. – Patrick Feb 16 '16 at 05:29
  • @HansPassant would you mind chiming in on this if you had any thoughts on it? – Patrick Feb 16 '16 at 05:46
  • 1
    First: while Hans is listed in the revision history, I'm not sure that counts as having "edited" the post; so he may or may not get notified via the @ syntax with a comment here (you'd have to find a post he wrote, edited, or commented on). Second: you haven't really edited your question so much as just tacked on one thing after another. You will have more success getting someone to take the question seriously if you would _edit_ it, reworking it so that it has a single, simple code example, with a precisely worded question that takes into account all progress you've managed to this point. – Peter Duniho Feb 16 '16 at 07:42
  • I hope I've made my question clear enough now – Patrick Feb 16 '16 at 17:13

1 Answers1

1

Here is an easy example for a delegate event.When you implement it in all your usercontrols it should work for your problem.

UserControl:

//Create a delegate
    public delegate void ButtonClickToMainForm(object sender, EventArgs e);

    public partial class UserControl1 : UserControl
    {
        //Your own event based on created delegate
        public event ButtonClickToMainForm ButtonClickedToMainForm;

        public UserControl1()
        {
            InitializeComponent();
        }

       //This method will invoke your event
        private void OnButtonClickedToMainForm(object sender, EventArgs e)
        {
            ButtonClickedToMainForm?.Invoke(this, e);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            //On button1_Click this will fire the event on mainform
            OnButtonClickedToMainForm(this, e);
        }

and the mainForm :

 public Form1()
        {
            InitializeComponent();
            //Subscribe to event from your usercontrol
            userControl11.ButtonClickedToMainForm += UserControl11_ButtonClickedToMainForm;
        }

        //Button on userControl1 has been clicked
        private void UserControl11_ButtonClickedToMainForm(object sender, EventArgs e)
        {
            //do your things here...
        }

Hope this helps.

  • I think I've got all of this placed correctly in my code, but for some reason on `userControl11.ButtonClickedToMainForm += UserControl11_ButtonClickedToMainForm;` I'm getting "An object reference is required for the non-static field, method, or property" – Patrick Feb 16 '16 at 15:56
  • I tried creating an instance of `UserControl1` on my main form with `UserControl1 CurrentPanel = new UserControl1();` and that solved the error I was getting, but now when I click my button to switch the controls in the panel I get a `System.NullReferenceException` on my `button_click` event on my user control at `ButtonClickedToMainForm(this, e);` – Patrick Feb 16 '16 at 16:18
  • Also, would it be better to create the delegate in the main form, rather than the user control? since it'll be used by more than one control – Patrick Feb 16 '16 at 16:42
  • Where do you call : ButtonClickedToMainForm(this, e) be sure the parameters "this" and "e" are not null. –  Feb 17 '16 at 07:54
  • On the button1_click event. How can I be sure those parameters are not null? Sorry, it's my first time using delegates, and I'm still working on understanding how to do so properly. – Patrick Feb 17 '16 at 13:02
  • I've updated my question with my attempted implementation of your answer. When you get a chance it would be appreciated if you could have a look and see if you notice where I've missed something. Thanks! Also, I've created the delegate in the main form, rather than in `UserControl1`, I'm not sure if this is acceptable or not, but to me it made more sense to have it there, since it'll be used by more than one UserControl, and it didn't seem to create any new issues by moving it there. – Patrick Feb 17 '16 at 20:36
  • Your implementation was correct you must do something wrong elsewhere. –  Feb 17 '16 at 22:11
  • That's very strange, because everything else is working fine, and the snippets in my question are the only "new code" in my project... Any ideas where I should start looking? Because I'm at a complete loss if the code I listed on here is correct. – Patrick Feb 17 '16 at 22:25
  • I believe `this` and `e` are still null for some reason, but I'm not sure how to fix that. I tried adding `if (Button1Clicked != null)` null before `Button1Clicked(this, e);` to check if it was null when I click the button, and nothing would happen when I click on it, telling me that `this` and `e` are in fact, null. Without that `if` statement my code allows `Button1Clicked` to be called, resulting in the Object reference error. I just don't know what I'm missing to fix the issue though. – Patrick Feb 18 '16 at 05:39
  • Turns out the issue was that `ButtonClickToMainForm` had to be static. I've edited your answer and accepted it. I've also moved the delegate to MainForm so it doesn't need to be declared in every control. This is also by far the clearest example I've found on how to accomplish this, and is written in a way it can be easily modified for different events by a beginner, so thank you very much for that! – Patrick Feb 18 '16 at 16:30