两个WinForm之间的TreeView的Invoke方法应该在哪里处理?

时间:2019-01-25 18:05:22

标签: c# .net winforms treeview invoke

我有两个WinForm(SettingfrmMain)。我有一个以Setting形式的TreeView,我想以第二种形式FillTree调用它的frmMain方法。

我正在将TreeView.Invoke用于线程目的。

这是我以Setting形式填写TreeView数据的代码:

TreeNode parentNode;
public void FillTree(DataTable dtGroups, DataTable dtGroupsChilds)
{
    treeViewGroups.Nodes.Clear();
    if (dtGroups == null) return;
    foreach (DataRow rowGroup in dtGroups.Rows)
    {
        parentNode = new TreeNode
        {
            Text = rowGroup["Groupname"].ToString(),
            Tag = rowGroup["Groupid"]
        };
        treeViewGroups.Invoke(new Add(AddParent), new object[] { parentNode });

        if (dtGroupsChilds == null) continue;
        foreach (DataRow rowUser in dtGroupsChilds.Rows)
        {
            if (rowGroup["Groupid"] == rowUser["Groupid"])
            {
                TreeNode childNode = new TreeNode
                {
                    Text = rowUser["Username"].ToString(),
                    Tag = rowUser["Phone"]
                };
                treeViewGroups.Invoke(new Add(AddParent), new object[] { childNode });
                System.Threading.Thread.Sleep(1000);
            }
        }
    }
    treeViewGroups.Update();
}

public delegate void Add(TreeNode tn);

public void AddParent(TreeNode tn)
{
    treeViewGroups.Nodes.Add(tn);
}

public void AddChild(TreeNode tn)
{
    parentNode.Nodes.Add(tn);
}
上面代码中的

FillTree方法,我想以我尝试的第二种形式frmMain调用它:

Settings settingsWindow;
public frmMain()
{
    InitializeComponent();

    settingsWindow = new Settings(this);
}

private void SomeMethod()
{
    //Two DataTables (dt1 and dt2) are passed from frmMain form

    settingWindow.FillTree(dt1, dt2);
}

当我调用FillTree方法时,它显示出如下错误:

  

在创建窗口句柄之前,无法在控件上调用Invoke或BeginInvoke。

我真的想知道,在我的第二个Winform frmMain中应该在哪里处理TreeView的Invoke方法?


我正在关注以下链接(但无济于事):

1)Populating TreeView on a Background Thread

2)How to add object in treeview from another thread

3)How can i invoke a method called from backgroundworker dowork event?


为可视化问题而编辑

我已经用TreeView尝试了它不起作用,然后我用ListBox对其进行了尝试,但是问题仍然存在。

问题::我在WinForm settingsWindow中有一个方法(用于填充列表框),我想在我的第二个WinForm frmMain中调用该方法。

Settings表单屏幕截图:

enter image description here

frmMain表单屏幕截图:

enter image description here

问题GIF:

enter image description here

ListBox填充的设置表单代码:

public void PopulateGroupListData(DataTable dt)
{
    listGroups.DataSource = null;

    listGroups.DisplayMember = "GroupName";
    listGroups.ValueMember = "Groupid";
    listGroups.DataSource = dt;

    if (listGroups.Items.Count > 0)
        listGroups.SelectedItem = listGroups.Items[0];
}

以第二种PopulateGroupListData的方法调用frmMain

void onCompleteReadFromServerStream(IAsyncResult iar)
{
    /... some code

    String[] arr1 = ServerMessage[1].Split('&');
    string Groupid1 = arr1[0];
    string GroupName1 = arr1[1];
    GroupsList.Rows.Add(Groupid1, GroupName1);
    settingsWindow.PopulateGroupListData(GroupsList);

    /... some code
}

2 个答案:

答案 0 :(得分:3)

订阅HandleCreated事件,并在事件处理程序中填充树。

enter image description here

public partial class Form1 : Form
{
    Form2 settingsWindow;

    public Form1()
    {
        InitializeComponent();
    }

    private void SettingsWindow_HandleCreated(object sender, EventArgs e)
    {
        var dt1 = new SampleTable1();
        var dt2 = new SampleTable2();

        settingsWindow.FillTree(dt1, dt2);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        settingsWindow = new Form2();
        settingsWindow.HandleCreated += SettingsWindow_HandleCreated;
        settingsWindow.ShowDialog();
    }
}

此外,我们还使用FillTree方法修复了几个问题,因此可以正确构建树。

public void FillTree(DataTable dtGroups, DataTable dtGroupsChilds)
{
    treeViewGroups.Nodes.Clear();
    if (dtGroups == null) return;
    foreach (DataRow rowGroup in dtGroups.Rows)
    {
        parentNode = new TreeNode
        {
            Text = rowGroup["Groupname"].ToString(),
            Tag = rowGroup["Groupid"]
        };
        treeViewGroups.Invoke(new Add(AddParent), new object[] { parentNode });

        if (dtGroupsChilds == null) continue;
        foreach (DataRow rowUser in dtGroupsChilds.Rows)
        {
            if ((int)rowGroup["Groupid"] == (int)rowUser["Groupid"])
            {
                TreeNode childNode = new TreeNode
                {
                    Text = rowUser["Username"].ToString(),
                    Tag = rowUser["Phone"]
                };
                treeViewGroups.Invoke(new Add(AddChild), new object[] { childNode });
                //System.Threading.Thread.Sleep(1000);
            }
        }
    }
    treeViewGroups.Update();
}

回答您的后续问题::如果Form2已可见,则不会发生您报告的异常,因为此时已创建了窗口的句柄,如下所示

enter image description here

public partial class Form1 : Form
{
    Form2 settingsWindow;
    SampleTable1 dt1;
    SampleTable2 dt2;
    int groupid = 1;
    int userid = 101;

    public Form1()
    {
        InitializeComponent();

        dt1 = new SampleTable1();
        dt2 = new SampleTable2();

        dt1.AddGroup(groupid);
        dt2.AddUser(groupid, userid++);
        dt2.AddUser(groupid, userid++);

        dt1.AddGroup(++groupid);
        dt2.AddUser(groupid, userid++);
        dt2.AddUser(groupid, userid++);
        dt2.AddUser(groupid, userid++);
    }

    private void SettingsWindow_HandleCreated(object sender, EventArgs e)
    {
        settingsWindow.FillTree(dt1, dt2);
    }

    private void button1_Click(object sender, EventArgs e)
    {
        settingsWindow = new Form2(this);
        settingsWindow.HandleCreated += SettingsWindow_HandleCreated;
        settingsWindow.ShowDialog();
    }

    public void UpdateData(string groupname)
    {
        dt1.AddGroup(++groupid, groupname);
        dt2.AddUser(groupid, userid++);
        dt2.AddUser(groupid, userid++);

        settingsWindow.FillTree(dt1, dt2);
    }
}

Form2保持不变,只是为新按钮添加了一个事件处理程序:

    private void button1_Click(object sender, EventArgs e)
    {
        form1.UpdateData(textBox1.Text);
    }

回答您的第二个后续问题: UpdateData是针对用户通过Form2提交新组的特定用例而创建的。我宁愿有针对不同用例的特定代码。请注意,使用UpdateData与服务器进行协商,甚至更好地与某个抽象接口进行协商,这可能是在远程服务器中,也可能不是(在合理的设计中,它应该是不相关的/透明的。) 。),然后返回要在Form2中显示的字符串消息。但是,这样做不会增加对该示例有限范围的目的的任何了解。实际上,这会使您要报告的根本问题及其相应解决方案陷入困境。让我们不要忘记,没有它,您原始问题中报告的异常会马上传给您...:O) enter image description here

答案 1 :(得分:2)

您共享的代码无法帮助我们重现该问题。某人唯一能做的就是创建一个示例,向您展示如何在另一个线程(如果确实需要线程)中以另一种形式加载TreeView

您应该考虑以下几点:

  • 您应该首先显示该表单,然后调用该表单或其控件之一的Invoke方法。否则,您将收到'Invoke或在创建窗口句柄之前无法在控件上调用BeginInvoke。'

  • 将大量节点添加到TreeView时,首先调用BeginUpdate,然后添加所有节点,然后调用EndUpdate。这样,用更少的UI渲染就可以更快。

  • 使用异步/等待模式来设计以具有非阻塞UI。

  • 如果要从另一个线程更新UI线程,请使用Invoke

  • 不要在UI线程中阻止耗时的非UI任务。

  • 使用Invoke时,请记住代码正在UI线程中运行。因此,请勿在{{1​​}}中调用阻塞耗时的非UI任务。只需在Invoke中调用UI代码即可。

在下面的示例中,我创建了一个带有按钮的表单。单击按钮后,我加载数据,然后打开另一个内部有Invoke的窗口。然后加载具有10000个节点的树:

TreeView
相关问题