Hi,
I'm not sure if the header is the right one, so if it's confusing, I'm sorry.
I come from Sun's Java world and don't know my way around the GUI programming in C# yet.
When I want to display a tabular data structure in Java, I use the JTable object, which is suitable for any type of tabular data, and can be provided with a 2 dimensional array of objects that can be placed in the table using the toString() method, regardless of the data source that is used for that table.
I'm trying to do something similar in c#, and I'm having trouble working with the DataGridView, which is simple only if you connect it to a datasource.
I have a TCP server program written in c# that needs to display data about connected clients. I need to display a table in the server's admin gui that specifies how many messages has been sent and received per client.
For that I need a table that represents the clients' data, and I need to update the received messages and sent messages cells.
What is the most convenient gui component to work with for this task?
The DataGridView doesn't seem to do the job for me (I've been spending many hours now, and could only show a row, but not to update it's content without removing it and adding a new row).
I'm currently trying to use the ListView, and it's easier to work with, but not to update.
Thanks for your replay. | | omerk76 Wednesday, January 17, 2007 5:35 PM | You can organize your client information in a list that implements IBindingListinterface and use data binding. I don't think I can type a whole lot before I escape my work for today but here it goes (hopefully you can pick up enough clues to get started). To simplify, I'll use a Client class that only contains two properties: Name (string) and IsHealthy (bool)
Btw I don't know if my code is optimized (probably not), any comments are welcome
1. Create the simple Client class
public class Client
{
private string _name;
private bool _isHealthy;
public string Name { get { return _name; } set { _name = value; } }
public bool IsHealthy { get { return _isHealthy; } set { _isHealthy = value; } }
public Client()
{
}
public Client(string name, bool healthy)
{
_name = name;
_isHealthy = healthy;
}
}
2. Create a ClientList class that implements IBindingList (hint: let VS.Net generates the implemenation skeltons for you - hover the mouse over "IBindingList" after "class ClientList:", click on the small blue-bordered box). Here we'll skip all methods we are not going to use (for now) and make only necessary changes
public class ClientList: IBindingList
{
private ArrayList _list;
...
#region IBindingList Members
public bool AllowEdit
{
get { return false; }
}
public bool AllowNew
{
get { return false; }
}
...
public bool SupportsChangeNotification
{
get { return false; }
}
public bool SupportsSorting
{
get { return false; }
}
....
public int Add(object value)
{
if (_list == null)
_list = new ArrayList();
_list.Add(value);
return _list.Count - 1;
}
...
#region IList Members
public object this[int index]
{
get
{
if (_list != null && index < _list.Count)
return _list[index];
else
throw new Exception("Index out of range");
}
set
{
if (_list != null && index < _list.Count)
_list[index] = value;
else
throw new Exception("Index out of range");
}
}
,,,
#region ICollection Members
public int Count
{
get { return _list == null ? 0 : _list.Count; }
}
...
#endregion
)
3. In your form, drop a datagridview and define a ClientList variable at form level. Add two columns to your datagridview: Client_Name and Client_Healthy
private ClientList myList;
...
private void Form1_Load(object sender, EventArgs e)
{
myList = new ClientList();
myList.Add( new Client("Client A", true));
myList.Add( new Client("Client B", true));
myList.Add( new Client("Client C", false));
myList.Add( new Client("Client D", false));
myList.Add( new Client("Client E", true));
dataGridView1.AutoGenerateColumns = false;
dataGridView1.Columns[ "Client_Name"].DataPropertyName = "Name";
dataGridView1.Columns[ "Client_Healthy"].DataPropertyName = "IsHealthy";
dataGridView1.DataSource = myList;
}
4. If everything goes correctly, you should have a datagridview binding to a custom object at this point. Run the application and you should see a populated grid.
5. Now, as we have the basics, we can try to nail down some other problems, the first one being dynamic update. Drop a button on your form and add onclick event handler:
private void button1_Click(object sender, EventArgs e)
{
(( Client)myList[1]).Name = "New Name";
dataGridView1.Refresh();
}
You should see the grid is automatically updated. Be aware if you are updating the list in a separate thread, you'll need to use Invoke method of dataGridView to update it.
I'll stop here as I need to run. Let me know if you would like to continue with the sample with more scenarios
| | Friendly Dog Thursday, January 18, 2007 2:52 AM | Rather than implementing IBindingList directly in ClientList use the base BindingList<T> class instead. The type stored in the list will be the Client class. Have the Client class implement the INotifyPropertyChanged interface to auto-update based upon changes to individual properties of the Client class. The grid should then allow you to add new rows. Note that you'll add new entries to the grid by adding new Client instances to the ClientList instance that you created. You do not need to rebind the list to the grid.
Rather than writing all the code I'd recommend that you do the following:
- Create the Client class as mentioned earlier.
- Create the ClientList class as mentioned earlier.
- Add a static method to the Client class that returns an instance of ClientListand contains all the currently defined clients.
- Using the designer drag and drop a BindingSource onto your form.
- Add a new ObjectDataSource through the binding source's data source property.
- The data source object will use the Client class and the static method of the class to retrieve the list of clients.
- Finally through the designer associate the binding source with the grid through the data source property.
- Optionally adjust the columns, through the designer, to show only the properties you want displayed.
You'll need to write the necessary logic to add new clients but whenever the client list updates the grid should as well. Furthermore if you implemented INotifyPropertyChanged in the Client class then any instance in the list that has a property change should also be immediately reflected in the grid.
Michael Taylor - 1/18/07 http://p3net.mvps.org
| | TaylorMichaelL Thursday, January 18, 2007 2:53 PM | The grid supports dynamically updating its contents when the underlying data changes through the use of the BindingList class and INotifyPropertyChanged. If you are going to show a fixed set of rows then it might just be easier to drop the controls in manually using a table layout container.
However if you want to use the grid then you can create a DataTable, add a column for the data name and another for the value. You then associate the data table with the grid. Now you can edit the table row values directly and the grid will automatically update itself. You could also use a data set if you wanted but it'd have only one table. You don't necessarily need a database in order to use either.
Michael Taylor - 1/17/07 http://p3net.mvps.org | | TaylorMichaelL Wednesday, January 17, 2007 6:57 PM | Examples are some of the best way to learn... Check out the Windows Forms .Net ( FAQ) which has differing lists of items, very helpful. Another FAQ which rounds up many subjects is the MSDN Winforms FAQ. Also check out is the GotDotNet Winforms Quick Start. For SQL projects check out the very robust SQL project examples as found in the Starter Kits. Also there are 101 Samples for Visual Studio 2005 which is a robust, initial development tasks to more involved tasks for winforms, web development etc which may help you in other areas. Also out some of the VB/C# examples for the basic operations . | | OmegaMan Thursday, January 18, 2007 1:10 AM | You can organize your client information in a list that implements IBindingListinterface and use data binding. I don't think I can type a whole lot before I escape my work for today but here it goes (hopefully you can pick up enough clues to get started). To simplify, I'll use a Client class that only contains two properties: Name (string) and IsHealthy (bool)
Btw I don't know if my code is optimized (probably not), any comments are welcome
1. Create the simple Client class
public class Client
{
private string _name;
private bool _isHealthy;
public string Name { get { return _name; } set { _name = value; } }
public bool IsHealthy { get { return _isHealthy; } set { _isHealthy = value; } }
public Client()
{
}
public Client(string name, bool healthy)
{
_name = name;
_isHealthy = healthy;
}
}
2. Create a ClientList class that implements IBindingList (hint: let VS.Net generates the implemenation skeltons for you - hover the mouse over "IBindingList" after "class ClientList:", click on the small blue-bordered box). Here we'll skip all methods we are not going to use (for now) and make only necessary changes
public class ClientList: IBindingList
{
private ArrayList _list;
...
#region IBindingList Members
public bool AllowEdit
{
get { return false; }
}
public bool AllowNew
{
get { return false; }
}
...
public bool SupportsChangeNotification
{
get { return false; }
}
public bool SupportsSorting
{
get { return false; }
}
....
public int Add(object value)
{
if (_list == null)
_list = new ArrayList();
_list.Add(value);
return _list.Count - 1;
}
...
#region IList Members
public object this[int index]
{
get
{
if (_list != null && index < _list.Count)
return _list[index];
else
throw new Exception("Index out of range");
}
set
{
if (_list != null && index < _list.Count)
_list[index] = value;
else
throw new Exception("Index out of range");
}
}
,,,
#region ICollection Members
public int Count
{
get { return _list == null ? 0 : _list.Count; }
}
...
#endregion
)
3. In your form, drop a datagridview and define a ClientList variable at form level. Add two columns to your datagridview: Client_Name and Client_Healthy
private ClientList myList;
...
private void Form1_Load(object sender, EventArgs e)
{
myList = new ClientList();
myList.Add( new Client("Client A", true));
myList.Add( new Client("Client B", true));
myList.Add( new Client("Client C", false));
myList.Add( new Client("Client D", false));
myList.Add( new Client("Client E", true));
dataGridView1.AutoGenerateColumns = false;
dataGridView1.Columns[ "Client_Name"].DataPropertyName = "Name";
dataGridView1.Columns[ "Client_Healthy"].DataPropertyName = "IsHealthy";
dataGridView1.DataSource = myList;
}
4. If everything goes correctly, you should have a datagridview binding to a custom object at this point. Run the application and you should see a populated grid.
5. Now, as we have the basics, we can try to nail down some other problems, the first one being dynamic update. Drop a button on your form and add onclick event handler:
private void button1_Click(object sender, EventArgs e)
{
(( Client)myList[1]).Name = "New Name";
dataGridView1.Refresh();
}
You should see the grid is automatically updated. Be aware if you are updating the list in a separate thread, you'll need to use Invoke method of dataGridView to update it.
I'll stop here as I need to run. Let me know if you would like to continue with the sample with more scenarios
| | Friendly Dog Thursday, January 18, 2007 2:52 AM |
This is the concept I was looking for.
Hwever, I still don't see any rows being added as I add client objects to the ClientList object.
Here's the code:
clientsList = new ClientList();// a new ClientList object
//connectedClientsView is the DataGridView object connectedClientsView.DataSource = clientsList;// bind the grid's DataSource to the ClientList object
connectedClientsView.Columns[ "IP"].DataPropertyName="IP";
connectedClientsView.Columns[ "User"].DataPropertyName = "User";
connectedClientsView.Columns[ "Password"].DataPropertyName = "Password";
connectedClientsView.Columns[ "Heartbeat"].DataPropertyName = "Heartbeat";
connectedClientsView.Columns[ "Timeout"].DataPropertyName = "Timeout";
connectedClientsView.Columns[ "ConnectTime"].DataPropertyName = "ConnectTime";
connectedClientsView.Columns[ "SessionID"].DataPropertyName = "SessionID";
connectedClientsView.Columns[ "MessagesSent"].DataPropertyName = "MessagesSentCount";
connectedClientsView.Columns[ "MessagesReceived"].DataPropertyName = "MessagesReceivedCount";
//end code
The Strings representing the column header names were coppied from the DataGridView Objcet.
The Strings right to the "=" sign are the exact property names of my ClientHandler class, here's the code for these:
public String IP {
get { return localEndPoint.Address.ToString(); }
}
public int Timeout {
get { return timeOut; }
set { timeOut = value; }
}
public String User {
get { return userName; }
set { userName = value; }
}
public String Password {
get { return password; }
set { password = value; }
}
public int Heartbeat {
get { return heartBeatInterval; }
set { heartBeatInterval = value; }
}
public DateTime ConnectTime {
get { return connectTime; }
}
public String SessionID {
get { return sessionId; }
set { sessionId = value; }
}
public long MessagesSentCount {
get { return sentMessagesCount; }
}
public long MessagesReceivedCount {
get { return receivedMessagesCount; }
}
//end code
If I get the point correctly, that's it. now I should see the new clients every time I add one or change it's details, but still I see no data in the grid. I changed the
AllowNew method to return true, but it still didn't help (I expected the AddNew method to throw an exception("The method or operation is not implemented."), but it didn't.
Should I implement a method or so on the dataGrid object? maybe some event handling code?
Thanks.
Omer. | | omerk76 Thursday, January 18, 2007 9:18 AM | Rather than implementing IBindingList directly in ClientList use the base BindingList<T> class instead. The type stored in the list will be the Client class. Have the Client class implement the INotifyPropertyChanged interface to auto-update based upon changes to individual properties of the Client class. The grid should then allow you to add new rows. Note that you'll add new entries to the grid by adding new Client instances to the ClientList instance that you created. You do not need to rebind the list to the grid.
Rather than writing all the code I'd recommend that you do the following:
- Create the Client class as mentioned earlier.
- Create the ClientList class as mentioned earlier.
- Add a static method to the Client class that returns an instance of ClientListand contains all the currently defined clients.
- Using the designer drag and drop a BindingSource onto your form.
- Add a new ObjectDataSource through the binding source's data source property.
- The data source object will use the Client class and the static method of the class to retrieve the list of clients.
- Finally through the designer associate the binding source with the grid through the data source property.
- Optionally adjust the columns, through the designer, to show only the properties you want displayed.
You'll need to write the necessary logic to add new clients but whenever the client list updates the grid should as well. Furthermore if you implemented INotifyPropertyChanged in the Client class then any instance in the list that has a property change should also be immediately reflected in the grid.
Michael Taylor - 1/18/07 http://p3net.mvps.org
| | TaylorMichaelL Thursday, January 18, 2007 2:53 PM | This is also a good direction.
I implemented the INotifyPropertyChanged interface in my client handler object.
However, I still see no data in the grid, but I do see it refreshes every time a client connects.
I followed your instructions but nothing happend, so I tried also this link: http://msdn2.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx , and this still didn't help.
I still don't see any clients in my grid.
Any ideas?
Thanks.
Omer.
Edit:
I've figured it out. My problem was with mixed code between the gui designer and the coding (I created some chaotic functionality there).
Now I generate the columns from the binding source and everything is updated as I wanted.
Thanks all, you've been very helpfull.
| | omerk76 Sunday, January 21, 2007 12:20 PM |
|