isa türk
isa türk

Reputation: 747

C# changing UI of the main form from an UserControl

I have a form named Form1 with a body of FlowLayoutPanel

This FlowLayoutPanel is listing bunch of UserControls and whenever I click remove button inside the UserControl I want FlowLayoutPanel to be updated.

it is rendering from a list called listTiles :

  public List<BookModel> listTiles = new List<BookModel>();

How can I update the main form inside the UserControl ??

Solution

Using a event handler is a good idea for handling the problem you can find about it here : Pass click event of child control to the parent control

However just passing the parent form to the child works too. Example:

private Form main;

    UserControl(Form mainForm) 
    {
        initialize();
        main = mainForm;
    }

Upvotes: 0

Views: 1246

Answers (3)

Jimi
Jimi

Reputation: 32288

In your UserControl you have a Button that, when clicked, should remove the UserControl.

If you need to handle a List<UserControl>, referencing the currently existing UCs, you have notify the owner of this List (a Form, here, apparently) when an UC is removed (remove means disposing of it, I assume).
A notification is usually an Event that is raised by an object. Your UserControl in this case: its internal components should not talk to the outside world directly.

In this case, add a public event, which is raised when a Button inside the UC is clicked:

public partial class MyUserControl : UserControl
{
    public delegate void ControlRemoveEventHandler(object sender, EventArgs e);
    public event ControlRemoveEventHandler ControlRemoveNotify;

    public MyUserControl() => InitializeComponent();

    private void btnRemoveControl_Click(object sender, EventArgs e)
        => ControlRemoveNotify?.Invoke(this, e);
}

You can subscribe to this public event when an UserControl is created and added to the List<UserControl> (this code also adds each new UserControl to a FlowLayoutPanel, at the same time).

When the UC's Button is clicked, the UC raises the ControlRemoveNotify event. The handler of this event calls RemoveControl((MyUserControl)obj);, which removes the UC from the List and then disposes of it.
When the UC is disposed of, it is also removed from the FlowLayoutPanel:

private List<MyUserControl> userControls = new List<MyUserControl>();

private void AddUCsToList(int howMany)
{
    for (int i = 0; i < howMany; i++)
    {
        var uc = new MyUserControl();
        void RemoveControl(object s, EventArgs e)
        {
            uc.ControlRemoveNotify -= RemoveControl;
            userControls.Remove(uc);
            uc.Dispose();
        };
        uc.ControlRemoveNotify += RemoveControl;
        userControls.Add(uc);
        flowLayoutPanel1.Controls.Add(uc);
    }
}

In case you don't actually need the List<UserControl> and instead you just need to show the UserControls inside a FlowLayoutPanel, you can remove the public event and simply call this.Dispose() in the internal handler of the Button's Click event.
This will, as already described, also remove the UC from FlowLayoutPanel:

public partial class MyUserControl : UserControl
{
    public MyUserControl() => InitializeComponent();

    private void btnRemoveControl_Click(object sender, EventArgs e)
        => this.Dispose();
}

Nothing else is required:

private List<MyUserControl> userControls = new List<MyUserControl>();

private void AddUCsToList(int howMany)
{
    for (int i = 0; i < howMany; i++)
    {
        flowLayoutPanel1.Controls.Add(new MyUserControl());
    }
}

Upvotes: 0

milespossing
milespossing

Reputation: 130

There are a few ways you could do it, but I'll list one

The Event-Driven Model

The crux of it is that any particular subview should have an event. Let's say that the event is of the form

event EventHandler<BookModel> RemoveClicked;

Which would mean that the main form would need some form of event handler in the following form:

private void HandleThatEvent(object sender, BookModel model)
{
    // do the thing
    // handle the event
}

Finally, after initialization, the main form should subscribe to the event. This can be done through the designer (the event will be listed under misc.), or directly through code like so:

public void AddView(YourUserControl someView)
{
    InitializeComponent();
    someView.RemoveClicked += HandleThatEvent;
}

If you're managing a flow layout it might have some different remove methods.. you might, for example, need to pass it a reference to the control you'd like to remove. So you might have to get clever with matching up against the removed item if you don't want to rerender. It could be just as fast to rerender the whole flow control every time that list changes, though. I would test that first and see if you experience any noticeable lag in your UI if you rerender it from a list.

Upvotes: 1

Luke
Luke

Reputation: 848

Just pass a reference of your mainForm into the constructor of your User Control. After that, you can just add code into the event handler of your RemoveButton within your User Control that updates the FlowLayoutPanel on your mainForm however you need.

Public Class UserControl
{
    private Form main;

    UserControl(Form mainForm) 
    {
        initialize();
        main = mainForm;
    }

    ButtonRemoveOnClick(eventArgs e) 
    { 
        main.listTiles.Remove(""); //name or index of tile you want removed inside 
                                   //parentheses 
        main.RenderFlowLayoutTiles(); //assuming you have a method to re-render these 
                                      //tiles, otherwise, whatever code you need to 
                                      //execute to render these tiles would go here
    }
}

Upvotes: 0

Related Questions