views:

202

answers:

5

Hi, I have a dataGridView displaying data from DataView based on DataTable. DataTable is updated with high frequency from a background thread (usually one row a time) but with varying regularity, i.e. sometimes time between updates is 0.5 ms, other few miliseconds. As datagridview is bound to DataView, I do not send requests for refresh of datagridview. The problem I am having is that I sometimes get cells drawn somewhere where they are not supposed to be, i.e. as seen in attached picture here: http://radlak.com/dataGridViewError1.png

The row with the number 122.94 has blue cell drawn in red column(gray column is PrimaryKey of DataTable, by which DataView is sorted). This is not supposed to happen, as the only blue cells should stay in second column. Sometimes, cell from third column will be displayed somewhere else. Would anyone know what is the reason of this kind of behavior? Is there any way to eliminate it? Except of this, I do not have any issues with the speed of update - everything else seems to work very quick and ok. I would greatly appreciate any help regarding this issue. Thanks, Martin

P.S. dataGridView1 is doublebuffered.

+1  A: 

It smells like a racing condition. The grid is not done swallowing one update and you hit it with another. Do you have any synchronization between the update process and the UI thread?

What might help is if you batch your updates and only apply them every once a while - i.e. every 100ms. After all it is UI for people to read there is no point in refreshing any faster.

Edit

One way of batching it would be to have the update thread apply updates to a separate data structure (a List? An Array?) and create yet another thread which would every .1 sec copy the modifications from the data structure to the UI.

The problems with locks you tried is that you applying the locks within your code does not do any good if the same objects are not locked inside the GridView. And GridView knows nothing about your locks

mfeingold
thanks very much, that is what I was thinking about too. Thats why I applied lock command whenever the row has been updated, but that seemed to be not enough. I tried to lock the whole table, dataview and datagridview, but that did not do the job either.I think this is the part I am strugling with - synchronization. What would be the best way to do it? The key for me is to get to a point, when datagridview updates are correct without batching updates, and then batch them in order to achieve highest speed.Thanks for help
Macin
Also, the update process is based on events occuring outside. whenever new data is available, an even function is called. This makes synchronization a bit more complicated, in such a way, that I am not sure how to wait with the new event to be processed until datagridview has been updated.
Macin
And lastly, I would certainly sacrifice datagridview updates in favour of event processing. I need datagridview to just check that everything is working fine.Sorry for multiple comments but those ideas are comming to my mind in "batches" ;)
Macin
I think that this is what is missing: how to notify update process (which is waiting on monitor) about gui datagridview finished updating? Cant find it on google. Usually people are asking how to do it the other way round.
Macin
Maybe if you do updates on a timer with a reasonable rate, there will be no need to synchronize the timer thread with UI. You will still need to synch timer and updater, but both will be in your hands
mfeingold
+1  A: 

This problem has become more and more complicated as I went into details of synchronization. To fully understant what is going on I will have to provide some code snippets (attached is only the most important, the most difficult part for me):

namespace MySpace { public delegate void DelegateInitializeGrid(DataView vtbl);

public class MyMainClass
{


    Thread oThread;

    public DataTable tbl = new DataTable();
    public DataView vtbl;


    Stopwatch sw = new Stopwatch();
    Stopwatch swt = new Stopwatch();


    public DelegateInitializeGrid dDelegateInitializeGrid;

    public Form1 sampleForm = null;
    System.Windows.Forms.Timer clock;

    public void startForm()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        sampleForm = new Form1();

        clock = new System.Windows.Forms.Timer();
        clock.Interval = 40;

        clock.Tick += new EventHandler(Timer_Tick);

        Application.Run(sampleForm);
        sampleForm.Invoke(sampleForm.dDelegateInitializeGrid, vtbl);



        clock.Start();
    }





    public void Timer_Tick(object sender, EventArgs eArgs)
    {
        if (sender == clock)
        {
            Console.WriteLine("tick printed");
        }
    }

    public void stopForm()
    {
        sampleForm.Dispose();
        clock.Stop();

    }

    public override void OnInitialization()
    {
        tbl.Columns.Add("Col1", typeof(int));
        tbl.Columns.Add("Col2", typeof(double));
        tbl.Columns.Add("Col3", typeof(int));
        tbl.Columns.Add("Col4", typeof(System.Decimal));
        DataColumn[] PrimaryKeyColumns = new DataColumn[1];
        PrimaryKeyColumns[0] = tbl.Columns["Price"];
        tbl.PrimaryKey = PrimaryKeyColumns;

        tbl.Columns.Add("Col5", typeof(int));
        tbl.Columns.Add("Col6", typeof(double));
        tbl.Columns.Add("Col7", typeof(int));
        tbl.Columns.Add("Col8", typeof(int));
        tbl.Columns.Add("Col9", typeof(int));
        tbl.Columns.Add("Col10", typeof(int));

        vtbl = new DataView(tbl);
        vtbl.Sort = "Price DESC";



        // Fill DataTable with some values


        oThread = new Thread(new ThreadStart(startForm));
        oThread.Start();


        //sampleForm.Invoke(sampleForm.dDelegateInitializeGrid, vtbl);

    }

    public override void OnDataArrival()
    {
        //update DataTable
    }


    partial class Form1
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;



        /// <summary>
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should bedisposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
                //aTimer.Dispose();
            }
            base.Dispose(disposing);
        }

        #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()
        {
            System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 =
             new System.Windows.Forms.DataGridViewCellStyle();
            //System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
            this.dataGridView1 = new System.Windows.Forms.DataGridView();
            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
            this.SuspendLayout();
            //
            // dataGridView1
            //
            this.dataGridView1.AllowUserToAddRows = false;
            this.dataGridView1.AllowUserToDeleteRows = false;
            this.dataGridView1.AllowUserToResizeColumns = false;
            this.dataGridView1.AllowUserToResizeRows = false;
            this.dataGridView1.AutoSizeColumnsMode =
             System.Windows.Forms.DataGridViewAutoSizeColumnsMode.None;
            this.dataGridView1.BorderStyle = System.Windows.Forms.BorderStyle.None;
            this.dataGridView1.ColumnHeadersHeight = 40;
            this.dataGridView1.ColumnHeadersHeightSizeMode =
             System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
            dataGridViewCellStyle1.Alignment =
             System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
            dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window;
            dataGridViewCellStyle1.Font = new System.Drawing.Font("MicrosoftSans Serif", 11F, System.Drawing.FontStyle.Bold,
             System.Drawing.GraphicsUnit.Point, ((byte)(238)));
            dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText;
            dataGridViewCellStyle1.SelectionBackColor =
             System.Drawing.SystemColors.Highlight;
            dataGridViewCellStyle1.SelectionForeColor =
             System.Drawing.SystemColors.HighlightText;
            dataGridViewCellStyle1.WrapMode =
             System.Windows.Forms.DataGridViewTriState.False;
            this.dataGridView1.DefaultCellStyle = dataGridViewCellStyle1;
            this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.dataGridView1.EnableHeadersVisualStyles = false;
            this.dataGridView1.Location = new System.Drawing.Point(0, 0);
            this.dataGridView1.Name = "dataGridView1";
            this.dataGridView1.ReadOnly = true;
            this.dataGridView1.RowHeadersWidthSizeMode =
             System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.DisableResizing;
            this.dataGridView1.ScrollBars = System.Windows.Forms.ScrollBars.None;
            this.dataGridView1.Size = new System.Drawing.Size(596, 413);
            this.dataGridView1.TabIndex = 0;
            this.dataGridView1.KeyDown += new System.Windows.Forms.KeyEventHandler(this.dataGridView1_KeyDown);

            //
            // Form1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(585, 800);
            this.Controls.Add(this.dataGridView1);

            ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
            this.ResumeLayout(false);

        }

        #endregion


        private void dataGridView1_KeyDown(object sender, KeyEventArgs e)
        {
            scrollGrid();
        }

        private void scrollGrid()
        {
            int halfWay = (dataGridView1.DisplayedRowCount(false) / 2);
            if (dataGridView1.FirstDisplayedScrollingRowIndex + halfWay > dataGridView1.SelectedCells[0].RowIndex ||
             (dataGridView1.FirstDisplayedScrollingRowIndex + dataGridView1.DisplayedRowCount(false) - halfWay) <= dataGridView1.SelectedCells[0].RowIndex)
            {
                int targetRow = dataGridView1.SelectedCells[0].RowIndex;

                targetRow = Math.Max(targetRow - halfWay, 0);
                dataGridView1.FirstDisplayedScrollingRowIndex = targetRow;

            }
        }

        public void initializeGrid(DataView lst)
        {
            SetDoubleBuffered(dataGridView1);
            this.dataGridView1.ReadOnly = true;
            this.dataGridView1.DataSource = lst;
            dataGridView1.RowHeadersVisible = false;
        }


        public System.Windows.Forms.DataGridView dataGridView1;



    }


    public partial class Form1 : Form
    {
        public bool canGo = false;


        public DelegateInitializeGrid dDelegateInitializeGrid;
        public DelegateUpdateGrid dDelegateUpdateGrid;
        public DelegateUpdateTradePrice dDelegateUpdateTradePrice;

        public Form1()
        {
            InitializeComponent();
            dataGridView1.AutoGenerateColumns = true;
            dDelegateInitializeGrid = new DelegateInitializeGrid(this.initializeGrid);
            dDelegateUpdateGrid = new DelegateUpdateGrid(this.updateGrid);
            dDelegateUpdateTradePrice = new DelegateUpdateTradePrice(this.updateTradePrice);

            // set a chart title

        }

        public static void SetDoubleBuffered(Control control)
        {
            // set instance non-public property with name "DoubleBuffered" to true
            typeof(Control).InvokeMember("DoubleBuffered",
             BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
             null, control, new object[] { true });
        }
    }
}

}

The software works in such a way, first it initializes itself executing OnInitialization content, and then as new data arrive, OnDataArrival is triggered.

I dont know if I am doing it right so every comment is much appreciated. The aim is to leave main application running, and use GUI datagridview as a background helper thread to visualize what is currently in dataTable. The interesting thing is, that above code does not work. However when I move delegateInititalizeGrid to OnInitialization just after launching startForm as a separate thread (see commented line), datagridview gets updated but with those issues which I described at the beginning of this question. I have spent few days trying to resolve this issue, but finally am close to giving up. Unfortunatelly, I have to get this done and it must be right so thank you very much for any contribution. All the best Martin

Macin
A: 

Can I query:

  • you have a DataTable
  • you have a DataGridView, bound to a DataView on that table
  • on a different thread, you are updating the DataTable

?

Yes, that won't work. Forms have thread affinity, and your change events are going to be firing on the wrong thread. It is not OK to just update the DataTable on a background thread. It sounds to me like you need to think about batching up the changes you want to make, and use this.Invoke to ask the UI thread to make the changes; then it should work:

public override void OnDataArrival()
{
    // here on background thread
    this.Invoke((MethodInvoker)delegate {
        // here on UI thread
        //TODO: update DataTable
    });
}

This will involve hopping between threads (via this.Invoke), hence the desire to batch the changes.

Marc Gravell
Thanks for reply. As shown in the code above, I am using Invoke to initialize datagrid, and what I noticed was, that once that invoke method has been succesfully executed, I do not have to repeat invoked updates from `OnDataArrival`. I assume that, once the datagridview received a reference to tbl, it stays there and does not require any further notifications. Seems like update events from DataTable after inoking `dDelegateInitializeGrid` notify datagridview correctly, however as I dont know how to take control over these events, they fire on every DataTable update creating racing conditions.
Macin
I am not sure if I explained one more thing accurately: when program starts its execution, first function that is being executed is `OnInitialization` in which I initialize Windows Form.
Macin
This is becoming more and more frustrating ... According to what I found on the internet, if datagridview sits in other thread than updating process, it should not reflect the changes in datasource. However, I have noticed, that by invoking `datagridinitialization` at the beginning, all changes in source are later reflected in the datagridview. Even if I dont want to. I tried using Merge on DataTable to make a copy of it, and update grid on timer, but still same thing happens - grid updates on every data arrival. Is it a normal behavior or am I doing something wrong? Thanks
Macin
If you have updates to a data-bound source on a non-UI thread, you have made a mistake...
Marc Gravell
can I ask what mistake have I done, please?
Macin
I think that a lot of confusion in here is because UI is not a main thread, as everybody is used to. If it was the main one, I don't think there would be any problem.
Macin
I would look at something like your clone approach, but maybe using virtual mode to minimise to reset cost.
Marc Gravell
(re the threads) let me take another look....
Marc Gravell
would you like me to post current state of the code? It includes timer in separate thread and copy of table?
Macin
Sorry, but threading issues are complex and subtle; there isn't enough there to reproduce what you are seeing, which makes it virtually impossible to give a meaningful answer beyond fairly broad pointers.
Marc Gravell
Re later comment - if it can work offline / against Northwind, maybe...
Marc Gravell
Ok, I will prepare something like a simulation of my core application and will post a code as soon as I will make it reproducing mentioned errors. This should be ready by tomorrow afternoon.Thanks so much.
Macin
A: 

Just in case someone would have a bit of time to look into the code, I have attached one which includes Timer in separate thread and form in a separate thread too:

namespace MyNamespace { public delegate void DelegateInitializeGrid(DataView tbl);

public class MyMainClass : Strategy
{
    RunForm runForm;
    Thread oThread;
    Thread tThread;

    public delegate void updateGridCallback(DataTable tbl);

    private DataTable tbl = new DataTable();
    public DataTable ctbl = new DataTable();
    private DataView vtbl;
    public DataView cvtbl;
    DataRow dr;

    DataRow t;
    DataRow[] tt;

    System.Threading.Timer clock;
    bool frst = true;

    public override void OnInitialization()
    {
        tbl.Columns.Add("Col1", typeof(int));
        tbl.Columns.Add("Col2", typeof(double));
        tbl.Columns.Add("Col3", typeof(int));
        tbl.Columns.Add("Price", typeof(System.Decimal));
        DataColumn[] PrimaryKeyColumns = new DataColumn[1];
        PrimaryKeyColumns[0] = tbl.Columns["Price"];
        tbl.PrimaryKey = PrimaryKeyColumns;

        runForm = new RunForm();
        oThread = new Thread(new ThreadStart(runForm.startForm));
        oThread.Start();

        tThread = new Thread(new ThreadStart(startTimer));
        tThread.Start();
    }

    void startTimer()
    {
        clock = new System.Threading.Timer(new TimerCallback(Timer_Tick));
        clock.Change(0, 1000);
        Thread.Sleep(Timeout.Infinite);
    }

    void Timer_Tick(object state)
    {
        if (frst == true)
        {
            ctbl.Merge(tbl);
            cvtbl = new DataView(ctbl);
            cvtbl.Sort = "Price DESC";

            frst = false;

            runForm.sampleForm.Invoke(runForm.sampleForm.dDelegateInitializeGrid,
                vtbl);
        }

        Console.WriteLine("tick occured");
        ctbl.Merge(tbl);
    }

    public override void onDataArrival()
    {
        //update DataTable tbl
    }

    public override void OnStop()
    {
        runForm.stopForm();
        clock.Dispose();
        tThread.Abort();

    }

}    

public class RunForm
{

    public Form1 sampleForm = null;
    public DataTable dtbl;

    public void startForm()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        sampleForm = new Form1();
        Application.Run(sampleForm);
    }
    public void stopForm()
    {
        sampleForm.Dispose();
    }
}



partial class Form1
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;



    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should bedisposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
            //aTimer.Dispose();
        }
        base.Dispose(disposing);
    }

    #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()
    {
        // initialize form and datagridview

    }

    #endregion

    public System.Windows.Forms.DataGridView dataGridView1;

    private void initializeGrid(DataView lst)
    {
        SetDoubleBuffered(dataGridView1);
        //this.DoubleBuffered = true;
        this.dataGridView1.ReadOnly = true;
        this.dataGridView1.DataSource = lst;
    }
}

public partial class Form1 : Form
{
    public DelegateInitializeGrid dDelegateInitializeGrid;

    public Form1()
    {
        InitializeComponent();
        dataGridView1.AutoGenerateColumns = true;
        dDelegateInitializeGrid = new DelegateInitializeGrid(this.initializeGrid);
    }

    public static void SetDoubleBuffered(Control control)
    {
        // set instance non-public property with name "DoubleBuffered" to true
        typeof(Control).InvokeMember("DoubleBuffered",
            BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
            null, control, new object[] { true });
    }
}

}

Macin
A: 

Ok, I have spent some time today and created just a basic version of my actual program. Below is the code which allows to see initialy described problem with datagridview. It is a consoleApplication created in Visual Studio 2008. It should compile with no problem. If the effect I mention at the beginning of this whole post does not appear to happen, please modify the threshold between new threads. If you anyone would like to ask some questions, please send them here. I need to mention it once again - this issue is very important for me and time is very precious thus I will greatly appreciate any further help and obviously thanks very much for people who tried to help already. Martin P.S. Here is a pasted code, but as you can see, formating is not the best, therefore it is also available here: http://radlak.com/c-sharpExampleOfCrashingDataGridView.cs

using System.Text;

using System; using System.Drawing; using System.Collections.Generic; using System.ComponentModel;

using System.Data; using System.Windows.Forms; using System.Reflection; using System.Threading;

namespace ConsoleApplication1 { class Program { static void Main() { RuntimeClass rt = new RuntimeClass(); new Thread(rt.StartAll).Start(); Console.ReadKey(false); rt.OnStop(); Console.WriteLine("key pressed");

    }

}

class RuntimeClass
{
    public delegate void updateGridCallback(DataView tbl);
    Strat.RunForm runForm;
    Thread oThread;
    bool keepGoing = true;

    ValuesTable[] valuesTable = new ValuesTable[20];

    double Column2 = 100.0;
    double Column3 = 101.0;

    Random r = new Random();

    DataTable tbl = new DataTable();
    DataView vtbl;

    Ladder[] ladder = new Ladder[20];
    DataRow t;

    public RuntimeClass()
    {

    }

    public void StartAll()
    {
        OnInitialization();
    }

    public void OnInitialization()
    {

        Console.WriteLine("On Initialization");

        for (int i = 0; i < 20; i++)
        {
            valuesTable[i] = new ValuesTable(Column2 - i, r.Next(100, 1000), r.Next(100, 1000), Column3 + i);
            Console.WriteLine(i + ": " + valuesTable[i].Column2Size + " - " + valuesTable[i].Column2 + " : " + valuesTable[i].Column3 + " - " + valuesTable[i].Column3Size);
        }

        runForm = new Strat.RunForm();
        oThread = new Thread(new ThreadStart(runForm.startForm));
        oThread.Start();

        tbl.Columns.Add("Column 1", typeof(double));
        tbl.Columns.Add("Column 2", typeof(int));
        tbl.Columns.Add("ValueIndex", typeof(System.Decimal));
        DataColumn[] PrimaryKeyColumns = new DataColumn[1];
        PrimaryKeyColumns[0] = tbl.Columns["ValueIndex"];
        tbl.Columns["ValueIndex"].AllowDBNull = true;
        tbl.PrimaryKey = PrimaryKeyColumns;

        tbl.Columns.Add("Column 4", typeof(int));
        tbl.Columns.Add("Column 5", typeof(double));



        for (int i = 0; i < 10; i++)
        {
            ladder[i] = new Ladder(valuesTable[i].Column2Size, valuesTable[i].Column2, valuesTable[i].Column3Size, valuesTable[i].Column3);
        }

        for (int i = 0; i < 41; i++)
        {
            Column2 = Column3;
            DataRow tRow = tbl.NewRow();
            tRow["ValueIndex"] = (double)(valuesTable[0].Column2 - 20 + i);
            tbl.Rows.Add(tRow);
        }

        vtbl = new DataView(tbl);
        vtbl.Sort = "ValueIndex DESC";

        Thread.Sleep(2000);
        runForm.sampleForm.Invoke(runForm.sampleForm.dDelegateInitializeGrid,
            vtbl);

        while (keepGoing == true)
        {
            Thread.Sleep(r.Next(0, 35));
            new Thread(OnDataArrival).Start();
        }
    }

    public void OnStop()
    {
        keepGoing = false;
        Console.WriteLine("On Stop");
    }

    public void OnDataArrival()
    {
        lock (valuesTable)
        {
            for (int i = 0; i < 20; i++)
            {

                valuesTable[i].UpdateValues(Column2 - i, r.Next(100, 1000), r.Next(100, 1000), Column3 + i);
                Console.WriteLine(i + ": " + valuesTable[i].Column2Size + " - " + valuesTable[i].Column2 + " : " + valuesTable[i].Column3 + " - " + valuesTable[i].Column3Size);
            }


            for (int i = 0; i < 20; i++)
            {
                t = tbl.Rows.Find(valuesTable[i].Column2);
                if (t != null)
                {
                    lock (t)
                    {
                        t["Column 2"] = valuesTable[i].Column2;
                        t["Column 1"] = valuesTable[i].Column2Size;
                        t["Column 4"] = DBNull.Value;
                        t["Column 5"] = DBNull.Value;
                    }
                }
                else
                {
                    tbl.Rows.Add(valuesTable[i].Column2Size, valuesTable[i].Column2,
                        DBNull.Value, DBNull.Value);
                }
            }

            for (int i = 0; i < 20; i++)
            {
                t = tbl.Rows.Find(valuesTable[i].Column3);
                if (t != null)
                {
                    lock (t)
                    {
                        t["Column 1"] = DBNull.Value;
                        t["Column 2"] = DBNull.Value;
                        t["Column 5"] = valuesTable[i].Column3Size;
                        t["Column 4"] = valuesTable[i].Column3;
                    }
                }
                else
                {
                    tbl.Rows.Add(DBNull.Value, DBNull.Value,
                        valuesTable[i].Column3, valuesTable[i].Column3Size);
                }
            }
        }
       // runForm.sampleForm.Invoke(runForm.sampleForm.dDelegateUpdateTradeValueIndex);

        Console.WriteLine("OnDataArrival");
        Thread.CurrentThread.Abort();

        Thread.CurrentThread.Abort();
    }
}

public class ValuesTable
{
    public double Column2 { get; set; }
    public int Column2Size { get; set; }
    public double Column3 { get; set; }
    public int Column3Size { get; set; }

    public ValuesTable(double Column2, int Column2Size, int Column3Size, double Column3)
    {
        this.Column2 = Column2;
        this.Column2Size = Column2Size;
        this.Column3 = Column3;
        this.Column3Size = Column3Size;
    }

    public void UpdateValues(double Column2, int Column2Size, int Column3Size, double Column3)
    {
        this.Column2 = Column2;
        this.Column2Size = Column2Size;
        this.Column3 = Column3;
        this.Column3Size = Column3Size;
    }
}

public class Ladder
{
    public int bidVol { get; set; }
    public double bidValueIndex { get; set; }
    public int askVol { get; set; }
    public double askValueIndex { get; set; }

    public Ladder(int Column2Vol, double Column2ValueIndex, int Column3Vol, double Column3ValueIndex)
    {
        bidVol = Column2Vol;
        bidValueIndex = Column2ValueIndex;
        askVol = Column3Vol;
        askValueIndex = Column3ValueIndex;
    }

    public void ladderupdate(int Column2Vol, double Column2ValueIndex, int Column3Vol, double Column3ValueIndex)
    {
        bidVol = Column2Vol;
        bidValueIndex = Column2ValueIndex;
        askVol = Column3Vol;
        askValueIndex = Column3ValueIndex;
    }

    public int[] addto(int Column2Vol, double Column2ValueIndex, int Column3Vol, double Column3ValueIndex)
    {
        int[] ret = new int[2] { 0, 0 };
        //index 0: bid
        //index 1: ask

        if (Column2ValueIndex != 0 && Column2Vol != 0)
        {
            if (bidValueIndex == Column2ValueIndex)
            {
                if (bidVol != Column2Vol)
                {
                    bidVol = Column2Vol;
                    ret[0] = -1;
                }
            }
            else if (bidValueIndex != Column2ValueIndex)
            {
                bidValueIndex = Column2ValueIndex;
                bidVol = Column2Vol;
                ret[0] = -2;
            }
        }
        if (Column3ValueIndex != 0 && Column3Vol != 0)
        {
            if (askValueIndex == Column3ValueIndex)
            {
                if (askVol != Column3Vol)
                {
                    askVol = Column3Vol;
                    ret[1] = 1;
                }
            }
            else if (askValueIndex != Column3ValueIndex)
            {
                askValueIndex = Column3ValueIndex;
                askVol = Column3Vol;
                ret[1] = 2;
            }
        }
        return ret;
    }
}

} namespace Strat { public delegate void DelegateInitializeGrid(DataView tbl); public delegate void DelegateUpdateGrid();

public class RunForm
{

    public Form1 sampleForm = null;
    public DataTable dtbl;

    public void startForm()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        sampleForm = new Form1();
        Application.Run(sampleForm);
    }

    public void stopForm()
    {
        sampleForm.Dispose();
    }
}

partial class Form1
{
    /// <summary>
    /// Required designer variable.
    /// </summary>
    private System.ComponentModel.IContainer components = null;



    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    /// <param name="disposing">true if managed resources should bedisposed; otherwise, false.</param>
    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
            //aTimer.Dispose();
        }
        base.Dispose(disposing);
    }

    #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()
    {
        System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 =
            new System.Windows.Forms.DataGridViewCellStyle();
        //System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
        this.dataGridView1 = new System.Windows.Forms.DataGridView();
        ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
        this.SuspendLayout();
        //
        // dataGridView1
        //
        this.dataGridView1.AllowUserToAddRows = false;
        this.dataGridView1.AllowUserToDeleteRows = false;
        this.dataGridView1.AllowUserToResizeColumns = false;
        this.dataGridView1.AllowUserToResizeRows = false;
        this.dataGridView1.AutoSizeColumnsMode =
            System.Windows.Forms.DataGridViewAutoSizeColumnsMode.None;
        this.dataGridView1.BorderStyle = System.Windows.Forms.BorderStyle.None;
        this.dataGridView1.ColumnHeadersHeight = 40;
        this.dataGridView1.ColumnHeadersHeightSizeMode =
            System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.DisableResizing;
        dataGridViewCellStyle1.Alignment =
            System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
        dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window;
        dataGridViewCellStyle1.Font = new System.Drawing.Font("MicrosoftSans Serif", 11F, System.Drawing.FontStyle.Bold,
            System.Drawing.GraphicsUnit.Point, ((byte)(238)));
        dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText;
        dataGridViewCellStyle1.SelectionBackColor =
            System.Drawing.SystemColors.Highlight;
        dataGridViewCellStyle1.SelectionForeColor =
            System.Drawing.SystemColors.HighlightText;
        dataGridViewCellStyle1.WrapMode =
            System.Windows.Forms.DataGridViewTriState.False;
        this.dataGridView1.DefaultCellStyle = dataGridViewCellStyle1;
        this.dataGridView1.Dock = System.Windows.Forms.DockStyle.Fill;
        this.dataGridView1.EnableHeadersVisualStyles = false;
        this.dataGridView1.Location = new System.Drawing.Point(0, 0);
        this.dataGridView1.Name = "dataGridView1";
        this.dataGridView1.ReadOnly = true;
        this.dataGridView1.RowHeadersWidthSizeMode =
            System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode.DisableResizing;
        this.dataGridView1.ScrollBars = System.Windows.Forms.ScrollBars.None;
        this.dataGridView1.Size = new System.Drawing.Size(596, 413);
        this.dataGridView1.TabIndex = 0;
        //
        // Form1
        //
        this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
        this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
        this.ClientSize = new System.Drawing.Size(585, 800);
        this.Controls.Add(this.dataGridView1);

        ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
        this.ResumeLayout(false);

        Console.WriteLine("Form initialized");

    }

    #endregion

    private void initializeGrid(DataView lst)
    {
        SetDoubleBuffered(dataGridView1);
        this.dataGridView1.ReadOnly = true;
        this.dataGridView1.DataSource = lst;
        dataGridView1.RowHeadersVisible = false;

        dataGridView1.Columns["ValueIndex"].DefaultCellStyle.BackColor = System.Drawing.Color.DarkGray;

        dataGridView1.Columns["ValueIndex"].DefaultCellStyle.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleCenter;
        dataGridView1.Columns["ValueIndex"].DefaultCellStyle.ForeColor =
            System.Drawing.Color.White;

        dataGridView1.Columns["Column 2"].DefaultCellStyle.BackColor =
            System.Drawing.Color.Blue;
        dataGridView1.Columns["Column 2"].DefaultCellStyle.ForeColor =
            System.Drawing.Color.White;
        dataGridView1.Columns["Column 2"].DefaultCellStyle.Alignment =
            System.Windows.Forms.DataGridViewContentAlignment.MiddleRight;

        dataGridView1.Columns["Column 4"].DefaultCellStyle.BackColor =
            System.Drawing.Color.Red;
        dataGridView1.Columns["Column 4"].DefaultCellStyle.ForeColor =
            System.Drawing.Color.White;
        dataGridView1.Columns["Column 4"].DefaultCellStyle.Alignment =
            System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;

        this.dataGridView1.MultiSelect = false;

    }


    private void updateGrid()
    {
        this.dataGridView1.Refresh();
    }


    public System.Windows.Forms.DataGridView dataGridView1;



}


public partial class Form1 : Form
{
    public bool canGo = false;


    public DelegateInitializeGrid dDelegateInitializeGrid;
    public DelegateUpdateGrid dDelegateUpdateGrid;


    public Form1()
    {
        InitializeComponent();
        dataGridView1.AutoGenerateColumns = true;
        dDelegateInitializeGrid = new DelegateInitializeGrid(this.initializeGrid);
        dDelegateUpdateGrid = new DelegateUpdateGrid(this.updateGrid);
    }

    public static void SetDoubleBuffered(Control control)
    {
        // set instance non-public property with name "DoubleBuffered" to true
        typeof(Control).InvokeMember("DoubleBuffered",
            BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
            null, control, new object[] { true });
    }


}

}

Macin