Advise on Real-Time update to column control

  • Thread starter Thread starter David Elliott
  • Start date Start date
D

David Elliott

I am not much of a GUI person and am looking for some insight.

I need to have a control that is multi-column and will allow for
real-time updating.

Any thoughts on controls, general steps or an example would be appreciated.

Thanks,
Dave
 
Hi Dave,

Based on my understanding, you wanted to find a control suitable for
updating and display.

I think .Net Winform DataGrid control may meet your need. DataGrid control
is the most powerful control of .Net Winform. It enables multi-columns,each
column behave the same, it also gives you the a good feature of
DataBinding, which enable you bind some data collection with the DataGrid
control.

DataGrid's databinding is a 2 way complex binding, which means:
1. you can bind data collection and display it in datagrid
2. you can modify the displayed data in DataGrid, it will automatically
update the underlying data source(data collection).

I think the DataGrid to data source automatic updating feature is what you
wanted real-time updating. Actually, when you modifed the data in the cell,
the updating will occur after the cell lost focus.

For more information about datagrid control, please refer
System.Windows.Forms.DataGrid class in MSDN.

=======================================
Please apply my suggestion above and let me know if it helps resolve your
problem.

Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
Hi Dave,

Thanks very much for your feedback.

Based on your further reply, I see that the data you want to add in is
stream and want to modify programmatically. Normally, DataGrid is a UI
control, it only displays the underlying datasource's data. Also its 2 way
databinding function gives you the way of updating and modifying the
underlying datasource through "hand" operating on the DataGrid control. The
modify operation will reflect into the underlying dataset and datatable.

But based on your requirement, if you want to modify the data
programmatically, the best way is modifying the datasource(dataset or
datatable) directly, and the modification will immediately reflect back
into DataGrid UI(Because the databinding is 2 way).

So your #1 and #3 statement is correct. Your #2 requirement may be changed
to modify the datasource like this:
DataSet ds=null;
private void Form1_Load(object sender, System.EventArgs e)
{
SqlDataAdapter adapter=new SqlDataAdapter("select * from jobs",
"server=localhost;database=pubs;uid=sa;pwd=test");
ds=new DataSet();
adapter.Fill(ds);
this.dataGrid1.DataSource=ds.Tables[0];
}

private void button1_Click(object sender, System.EventArgs e)
{
this.ds.Tables[0].Rows[0]["job_desc"]="modified data";
}

My dataset's data is retrieving from SqlServer's sample pubs database's
jobs table. Once you clicked button1, the DataGrid display will also be
updated.

======================================
Please apply my suggestion above and let me know if it helps resolve your
problem.

Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
Jeffrey,

I am using your suggestion of attaching the DataSet to a DataGrid. The problem is that the UI becomes
unresponsive when messages are coming streaming in. Here is a little history:

1) UI spawns a backround worker thread to read information from a socket.
2) Worker thread generates an event that the UI receives via the Method listed below
3) The message is parsed and added to a typed DataSet named DsMessageCollection

Any thoughts would be appreciated.

Thanks,
Dave

===========================
private void listBoxUpdate_listBoxUpdateEvent(object sender, ListBoxEventArgs args)
{
if ((args != null) &&
(args.message != null) &&
(args.message.Length > 0))
{
string[] fields;
// Seperate Fields

fields = args.message.Split(seperator);
if ((fields.Length == 7) || (fields.Length == 8))
{
DsMessageCollection.MessageRow row = dsMessages.Message.NewMessageRow();
row.ServerName = fields[0];
row.EventDateTime = fields[1];
row.ThreadId = fields[2];
row.EventId = fields[3];
row.SeverityLevel = fields[4];
row.Source = fields[5];

if (fields.Length == 7)
row.Information = fields[6];
else
{
row.TimeElapsed = fields[6];
row.Information = fields[7];
}

// Add the Row to the DataSet
dsMessages.Message.Rows.Add(row);
}

// Scroll Messages if Sitting on the Last Row
if (dsMessages.Message.Rows.Count-2 == dataGrid1.CurrentRowIndex)
dataGrid1.CurrentRowIndex = dsMessages.Message.Rows.Count-1;
}
}



Hi Dave,

Thanks very much for your feedback.

Based on your further reply, I see that the data you want to add in is
stream and want to modify programmatically. Normally, DataGrid is a UI
control, it only displays the underlying datasource's data. Also its 2 way
databinding function gives you the way of updating and modifying the
underlying datasource through "hand" operating on the DataGrid control. The
modify operation will reflect into the underlying dataset and datatable.

But based on your requirement, if you want to modify the data
programmatically, the best way is modifying the datasource(dataset or
datatable) directly, and the modification will immediately reflect back
into DataGrid UI(Because the databinding is 2 way).

So your #1 and #3 statement is correct. Your #2 requirement may be changed
to modify the datasource like this:
DataSet ds=null;
private void Form1_Load(object sender, System.EventArgs e)
{
SqlDataAdapter adapter=new SqlDataAdapter("select * from jobs",
"server=localhost;database=pubs;uid=sa;pwd=test");
ds=new DataSet();
adapter.Fill(ds);
this.dataGrid1.DataSource=ds.Tables[0];
}

private void button1_Click(object sender, System.EventArgs e)
{
this.ds.Tables[0].Rows[0]["job_desc"]="modified data";
}

My dataset's data is retrieving from SqlServer's sample pubs database's
jobs table. Once you clicked button1, the DataGrid display will also be
updated.

======================================
Please apply my suggestion above and let me know if it helps resolve your
problem.

Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
Hi Dave,

Thanks for your feedback.

You senario has too much backgroud information, I suggest you divided your
application into several parts, then test each part to find out which is
the root cause.

For example, you may first use Typed dataset binding to the datagrid, then
manually add a row to the typed dataset(without socket and threading code),
does the problem occurs? I have writen a sample project only update the
typed dataset, which works well. Code lists below:
//I used the SqlDataAdapter component in the toolbox to generated typed
dataset at design-time.
private void Form1_Load(object sender, System.EventArgs e)
{
this.sqlDataAdapter1.Fill(this.dataSet11);
this.dataGrid1.Select(this.dataSet11.jobs.Rows.Count-1);
}

private void button1_Click(object sender, System.EventArgs e)
{
datagridupdateissue.DataSet1.jobsRow jr=this.dataSet11.jobs.NewjobsRow();
jr.job_desc="just a test";
jr.max_lvl=400;
jr.min_lvl=240;
this.dataSet11.jobs.Rows.Add(jr);
this.dataGrid1.Select(this.dataSet11.jobs.Rows.Count-1);
}

Second, you may first not to use datagrid and socket, just use another
thread to update the typed dataset, then trace step by step the updating
code in the IDE, if there is any delay operation?

Third, if the above 2 steps both work well, the problem may be the socket
side. Anyway, let's wait for your further research.

==============================================
Please apply my suggestion above and let me know if it helps resolve your
problem.

Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
Hi Dave,

Thanks for your feedback.

After reviewing you code, I think the problem should be the multithreading
invoking issue with UI. That is in .Net Framework, when using multi-thread,
a control's method can only be invoked from the thread that created the
control. If a thread which did not create the control wants to invoke a
method of this control, it should use ISynchronizeInvoke.BeginInvoke or
ISynchronizeInvoke.Invoke method. This is the multithreading invoking rule
in .Net.

Because System.Windows.Forms.Control class implemented ISynchronizeInvoke
interface, we may first use a delegate to wrap the method we want to
invoke, then use Control.BeginInvoke method to invoke this delegate. I have
modified your code like this:

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace MessageViewer
{
#region UdpViewerForm

/// <summary>
/// Summary description for Form1.
/// </summary>
public class UdpViewerForm : System.Windows.Forms.Form
{
#region Data Members

private char[] seperator = {'|'};
private ArrayList filterList = new ArrayList();
private System.Windows.Forms.DataGrid dataGrid1;

private DataSet ds = new DataSet();
private System.Windows.Forms.Button button1;

/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;

#endregion

#region Constructor

public UdpViewerForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();

Initialize();
CreateRetrieveThread();
}

#endregion

#region IDispose Methods
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#endregion

#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.dataGrid1 = new System.Windows.Forms.DataGrid();
this.button1 = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).BeginInit();
this.SuspendLayout();
//
// dataGrid1
//
this.dataGrid1.AllowSorting = false;
this.dataGrid1.DataMember = "";
this.dataGrid1.HeaderForeColor = System.Drawing.SystemColors.ControlText;
this.dataGrid1.Location = new System.Drawing.Point(16, 8);
this.dataGrid1.Name = "dataGrid1";
this.dataGrid1.ReadOnly = true;
this.dataGrid1.Size = new System.Drawing.Size(634, 293);
this.dataGrid1.TabIndex = 3;
//
// button1
//
this.button1.Location = new System.Drawing.Point(240, 320);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(96, 34);
this.button1.TabIndex = 4;
this.button1.Text = "button1";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// UdpViewerForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(680, 358);
this.Controls.Add(this.button1);
this.Controls.Add(this.dataGrid1);
this.Name = "UdpViewerForm";
this.Text = "Form1";
((System.ComponentModel.ISupportInitialize)(this.dataGrid1)).EndInit();
this.ResumeLayout(false);
}
#endregion

#region Form Control Methods

private void InitDataSet()
{
ds.Tables.Add("Messages");

ds.Tables[0].Columns.Add("ServerName");
ds.Tables[0].Columns.Add("EventDateTime");
ds.Tables[0].Columns.Add("ThreadId");
ds.Tables[0].Columns.Add("EventId");
ds.Tables[0].Columns.Add("SeverityLevel");
ds.Tables[0].Columns.Add("Source");
ds.Tables[0].Columns.Add("Information");

for(int i=0;i<20;i++)
{
DataRow dr=ds.Tables[0].NewRow();
dr["ServerName"]="abc";
ds.Tables[0].Rows.Add(dr);
}
dataGrid1.DataSource = ds.Tables[0];
}

private void Initialize()
{
string empty = "";
filterList.Add(empty);

InitDataSet();
}

delegate void Retrieve_delegate(UdpViewerForm form);
Retrieve_delegate rd=null;
private void CreateRetrieveThread()
{
rd=new Retrieve_delegate(ReceiveUdp.Retrieve);
}

public void updatingmethod(string msg)
{
if (msg!=null&&msg.Length>0)
{
string[] fields;

// Seperate Fields
fields = msg.Split(seperator);

if ((fields.Length == 7) || (fields.Length == 8))
{
DataRow row = ds.Tables[0].NewRow();
row[0] = fields[0];
row[1] = fields[1];
row[2] = fields[2];
row[3] = fields[3];
row[4] = fields[4];
row[5] = fields[5];
row[6] = fields[6];

ds.Tables[0].Rows.Add(row);
}
else
{
string s = msg;
s = null;
}
}
this.dataGrid1.DataSource=ds.Tables[0];
}

#endregion

#region Main
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
Application.Run(new UdpViewerForm());
}
#endregion

private void button1_Click(object sender, System.EventArgs e)
{
rd.BeginInvoke(this, null, null);
}
}

#endregion

#region ReceiveUdp Class
/// <summary>
/// Summary description for ReceiveUdp.
/// </summary>
public class ReceiveUdp
{
#region Methods

static public string CreateMessage(int ThreadId)
{
char seperator = '|';
string ServerName = "MyServer";
DateTime EventTime = DateTime.Now;
int EventId = 0;
string SeverityLevel = "Verbose";
string Source = "Some Module +150";
string Message = "Nothing Really to report";



string _logString = (ServerName + seperator).PadRight(11, ' ')
+ "[" + EventTime.ToString ("M/d/yyyy hh:mm:ss") + "]" + seperator + " "
+ (ThreadId.ToString() + seperator).PadRight(6, ' ')
+ (EventId.ToString() + seperator).PadRight(6, ' ')
+ (SeverityLevel + seperator).PadRight(23, ' ')
+ (Source + seperator).PadRight(25, ' ');

_logString += " " + Message;
return _logString;

}

delegate void updatingmethod_delegate(string msg);
static public void Retrieve(UdpViewerForm form)
{
int ThreadId = 0;
string msg = null;

while (true)
{
Thread.Sleep(300);
msg = CreateMessage(ThreadId++);

updatingmethod_delegate ud=new
updatingmethod_delegate(form.updatingmethod);
form.BeginInvoke(ud, new object[]{msg});
}
}

#endregion
}

#endregion
}

Note: I discard the explicit thread in the code, but use asynchroize
delegate to retrieve thread from the Thread Pool.
==========================================
Please apply my suggestion above and let me know if it helps resolve your
problem.

Thank you for your patience and cooperation. If you have any questions or
concerns, please feel free to post it in the group. I am standing by to be
of assistance.

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
Hi Dave,

Does my reply make sense to you? Do you still have concern on this issue?

Please feel free to feedback. Thanks

Best regards,
Jeffrey Tan
Microsoft Online Partner Support
Get Secure! - www.microsoft.com/security
This posting is provided "as is" with no warranties and confers no rights.
 
Back
Top