I have two WinForms (Setting
and frmMain
). I have a TreeView in Setting's form and I want to call its FillTree
method in the second form frmMain
.
I'm using the TreeView.Invoke
for the threading purposes.
Here is my code for TreeView filling data in Setting's form :
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
method from above code, I wants call it in my second form frmMain which I tried like so:
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);
}
When I call FillTree
method it show me error like this:
Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
I real wants to know, where should be handle TreeView's Invoke method in my second winform frmMain
?
I'm following these links (but no avail):
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?
I have tried it with TreeView its not working then I Tried it with ListBox but the problem is still same.
Problem: I've a method (which populate my ListBox) in WinForm settingsWindow
and I want to call that method in my second WinForm frmMain
.
Settings
form screenshot:
frmMain
form screenshot:
Problem GIF :
Settings form Code for ListBox Populating :
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];
}
Calling PopulateGroupListData
in second form frmMain
's method:
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
}
Subscribe to HandleCreated
event and fill the tree in the event handler.
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();
}
}
As a bonus, we also fixed a couple problems with FillTree
method, so the tree can be built correctly.
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();
}
Answering your follow-up question: if Form2
is already visible, the exception you reported wouldn't happen because at this point the window's handle has been created, as we show below.
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
stays the same you already have, just adding an event handler for the new button:
private void button1_Click(object sender, EventArgs e)
{
form1.UpdateData(textBox1.Text);
}
Answering your 2nd follow-up question: UpdateData
was created for the specific use case of the user submitting a new group through Form2
. I would rather have specific code for a different use case. Note that it would be fairly elementary to have UpdateData
to consult with a server, or even better, with some abstract interface, that could be in a remote server or not (good design dictates it should be irrelevant/transparent...) and then return a string message to be presented in Form2. However, doing so wouldn't add any insight into the purposes of the limited scope of this sample. It would actually mud the waters about the root problem you're reporting and its corresponding solution. Let's not forget that without it, the reported exception in your original question comes right back at you...:O)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With