C#从UserControl更改主窗体的UI

C#从UserControl更改主窗体的UI,c#,winforms,C#,Winforms,我有一个名为Form1的表单,它的主体是FlowLayoutPanel 这个FlowLayoutPanel列出了一堆用户控件,每当我在UserControl中单击remove按钮时,我都希望FlowLayoutPanel得到更新 它是从名为listTiles的列表进行渲染的: public List<BookModel> listTiles = new List<BookModel>(); 有几种方法可以做到这一点,但我会列出一种 事件驱动模型 它的关键是任何特定的

我有一个名为Form1的表单,它的主体是FlowLayoutPanel

这个FlowLayoutPanel列出了一堆用户控件,每当我在UserControl中单击remove按钮时,我都希望FlowLayoutPanel得到更新

它是从名为listTiles的列表进行渲染的:

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

有几种方法可以做到这一点,但我会列出一种

事件驱动模型 它的关键是任何特定的子视图都应该有一个事件。让我们假设事件的形式是

event EventHandler<BookModel> RemoveClicked;
最后,在初始化之后,主窗体应该订阅事件。这可以通过设计器完成(事件将在misc.下列出),或直接通过如下代码完成:

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

如果您正在管理流布局,它可能有一些不同的删除方法。。例如,您可能需要向它传递对要删除的控件的引用。因此,如果您不想重新播放,您可能必须聪明地匹配删除的项目。不过,每次列表发生更改时,重新启动整个流控件的速度也一样快。我会先测试一下,如果从列表中重新提交UI,看看它是否有明显的延迟。

只需将mainForm的引用传递到用户控件的构造函数中即可。之后,您只需将代码添加到用户控件中RemoveButton的事件处理程序中,即可根据需要更新mainForm上的FlowLayoutPanel

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

在UserControl中,您有一个按钮,单击该按钮时应删除UserControl

如果您需要处理引用当前现有UCs的
列表,则在移除UC时,您已通知该列表的所有者(这里显然是一个表单)(我认为,移除意味着处置它)。
通知通常是由对象引发的事件。在这种情况下,用户控件:其内部组件不应直接与外部世界对话

在这种情况下,添加一个
public
事件,该事件在单击UC内的按钮时引发:

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);
}
当用户控件被创建并添加到
列表中时,您可以订阅此公共事件(此代码还同时将每个新的用户控件添加到FlowLayoutPanel)

单击UC按钮时,UC将引发
ControlRemoveNotify
事件。此事件的处理程序调用
RemoveControl((MyUserControl)obj),从列表中删除UC,然后将其处理。
处理UC时,它也会从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);
    }
}
public partial class MyUserControl : UserControl
{
    public MyUserControl() => InitializeComponent();

    private void btnRemoveControl_Click(object sender, EventArgs e)
        => this.Dispose();
}
不需要其他任何东西:

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

private void AddUCsToList(int howMany)
{
    for (int i = 0; i < howMany; i++)
    {
        flowLayoutPanel1.Controls.Add(new MyUserControl());
    }
}
private List userControls=new List();
私有列表(整数多少)
{
for(int i=0;i<多少;i++)
{
flowLayoutPanel1.Controls.Add(新的MyUserControl());
}
}

请发布表单和用户控件的代码。您不需要。让它提供在单击“删除”时触发的事件。然后主窗体可以注册并相应地处理它。也许告诉OP主窗体也必须注册事件是值得的。拥有一个未注册的处理程序不会起任何作用。传入构造函数不是一个好主意,因为您失去了设计时支持。您总是可以使用找到父窗体,然后可以将其转换为所需的窗体类型或接口,并调用某些方法。但总的来说,这不是一个好主意。例如,使用事件方法是更好的方法
public partial class MyUserControl : UserControl
{
    public MyUserControl() => InitializeComponent();

    private void btnRemoveControl_Click(object sender, EventArgs e)
        => this.Dispose();
}
private List<MyUserControl> userControls = new List<MyUserControl>();

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