c # dataGridView изменение местоположения случайных ячеек (Edit: Inter Process Synchronization?) - PullRequest
1 голос
/ 14 декабря 2009

У меня есть dataGridView, отображающий данные из DataView на основе DataTable. DataTable обновляется с высокой частотой из фонового потока (обычно по одной строке за раз), но с изменяющейся регулярностью, то есть иногда время между обновлениями составляет 0,5 мс, другие несколько миллисекунд. Поскольку datagridview привязано к DataView, я не отправляю запросы на обновление datagridview. Проблема, с которой я сталкиваюсь, заключается в том, что иногда я рисую клетки где-то там, где их не должно быть, то есть, как показано на прилагаемой картинке: http://radlak.com/dataGridViewError1.png

Строка с номером 122,94 имеет синюю ячейку, нарисованную в красном столбце (серый столбец - PrimaryKey объекта DataTable, по которому сортируется DataView). Это не должно происходить, так как во втором столбце должны оставаться только синие клетки. Иногда ячейка из третьего столбца будет отображаться где-то еще. Кто-нибудь знает, что является причиной такого поведения? Есть ли способ устранить это? Кроме этого, у меня нет проблем со скоростью обновления - все остальное работает очень быстро и нормально. Я был бы очень признателен за любую помощь по этому вопросу. Спасибо, Martin

P.S. dataGridView1 имеет двойную буферизацию.

Ответы [ 3 ]

1 голос
/ 14 декабря 2009

Пахнет как гоночное состояние. Сетка не закончила глотать одно обновление, а вы попали в другое. Есть ли у вас синхронизация между процессом обновления и потоком пользовательского интерфейса?

Что может помочь, если вы пакетируете свои обновления и применяете их только один раз, то есть каждые 100 мс. В конце концов, это пользовательский интерфейс для чтения, нет смысла обновлять его быстрее.

Редактировать

Одним из способов пакетной обработки было бы применение потока обновлений к отдельной структуре данных (Список? Массив?) И создание еще одного потока, который будет каждые 0,1 секунды копировать изменения из структуры данных в UI.

Проблема с блокировками, которые вы пробовали, заключается в том, что применение блокировок в вашем коде не принесет пользы, если одни и те же объекты не заблокированы внутри GridView. И GridView ничего не знает о ваших блокировках

0 голосов
/ 15 декабря 2009

На всякий случай, если у кого-то будет немного времени для изучения кода, я приложил тот, который включает в себя таймер в отдельном потоке и форму в отдельном потоке:

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 });
        }
    }
}
0 голосов
/ 15 декабря 2009

Могу ли я запросить:

  • у вас есть DataTable
  • у вас есть DataGridView, привязанный к DataView на этом столе
  • в другом потоке , вы обновляете DataTable

Да, это не сработает. Формы имеют сходство с потоками, и ваши изменения будут запускаться не в том потоке. Не просто обновлять DataTable в фоновом потоке. Для меня это звучит так, будто вам нужно подумать о пакетировании изменений, которые вы хотите внести, и использовать this.Invoke, чтобы попросить UI thread внести изменения; тогда должно работать:

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

Это будет включать переключение между потоками (через this.Invoke), отсюда и желание пакетировать изменения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...