Привязка DataGridView к DataSource - генерирует CurrencyError IndexOutOfRangeException - PullRequest
1 голос
/ 02 июня 2011

Я ломаю голову над этим уже несколько дней, и это меня довольно сильно побило, но, честно говоря, я еще не все испытал, и у меня проблемы с DataGridView - что кажется общей темой.

public partial class frmMain : Form
{
    ServerConnection sabCom;
    private BindingSource jobSource = new BindingSource();
    private void timer1_Tick(object sender, EventArgs e)
    {
            if (bgUpdateThread.IsBusy == false)
            {
                bgUpdateThread.RunWorkerAsync(sabCom);
            }
        }
    }

    private void frmMain_Load(object sender, EventArgs e)
    {
        timer1.Interval = 3000;
        timer1.Start();
    }

    private void bgUpdateThread_DoWork(object sender, DoWorkEventArgs e)
    {
        ServerConnection s = e.Argument as ServerConnection;
        s.update();
        e.Result = s;
    }

    private void bgUpdateThread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        this.sabCom = e.Result as ServerConnection;

        if (dgvQueueFormatted == false)
        {
            dgvQueue_Init();  //Applies formatting and loads column width. Inits data sources.
        }
        else
        {
            dgvQueue_Update();

        }
    }

    private void dgvQueue_Update()
    {
        dgvQueue.Refresh();
    }

    private void dgvQueue_Init()
    {
        try
        {
            jobSource.DataSource = sabCom.queue.jobs;
            dgvQueue.DataSource = jobSource;
            try
            {
                //Apply saved column spacing to the dgvQueue
                //Uses reflection to set dgvQueue to DoubleBuffer
            }
            catch
            { }
        }
        catch
        { }
    }

    private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
    {
        //Saves information about the dgvQueue on shutdown.
    }

Класс очереди:

public class Queue  
{
    private string _status;
    public string status { get { return _status; } set { _status = value; } }
    private string _eta;
    public string eta { get { return _eta; } set { _eta = value; } }
    private List<Job> _jobs;
    public List<Job> jobs
    {
        get
        {
            return _jobs;
        }
        set
        {
            _jobs = value;
        }
    }
    private List<string> _categories;
    public List<string> categories { get { return _categories; } set { _categories = value; } }

    private XmlDocument xmld;
    private ServerConnection m_parent;
    private XmlNodeList _xmljobs;

    public Queue(ServerConnection srvConn)
    {
        //fetch the Queue xml
        m_parent = srvConn;
        xmld = new XmlDocument();
        _jobs = new List<Job>();
    }

    public void update()
    {
        updateXml();
        updateQueue();
        updateJobs();
    }

    private void updateXml()
    {
        //Loads xml file into xmld
    }

    private void updateQueue()
    {
        XmlNodeList elements = xmld.SelectNodes("queue");

        foreach (XmlNode element in elements)
        {
            _status = element.SelectSingleNode("status").InnerText;
            _eta = element.SelectSingleNode("eta").InnerText;
        }
    }

    private void updateJobs()
    {
        _xmljobs = xmld.SelectNodes("queue/job");
        jobs.Clear();

        foreach (XmlNode xmljob in _xmljobs)
        {
            Job t_job;

            _status = xmljob.SelectSingleNode("status").InnerText;
            _eta = xmljob.SelectSingleNode("eta").InnerText;

            //Create temp job to match against list.
            t_job = new Job(_status, _eta);
            jobs.Add(t_job);
        }
    }

Класс задания: в действительности он содержит около 30 значений различных типов, но все они в одном формате:

public class Job
{
    private int _status;
    public int status { get { return _status; } set { _status = value; } }
    private string _eta;
    public string eta { get { return _eta; } set { _eta = value; } }


    public Job(string status, string eta)
    {
        _status = status;
        _eta = eta;
    }
}

При взаимодействии с DataGridView я получаю сообщение об ошибке:

В DataGridView произошло следующее исключение:

System.IndexOutOfRangeException: у индекса нет значения.в System.Windows.Forms.CurrencyManager.get_Item (индекс Int32) в System.Windows.Forms.DataGridViewDataConnection.GetError (Int32 boundColumnIndex, Int32 columnIndex, Int32 rowIndex)

И при входе в отладчик это срабатывает при запускеApplication.Run (new frmMain (). Что, черт возьми, я делаю неправильно? Программа все еще функционирует и обновляется нормально, но я даже не могу обработать событие, чтобы подавить сообщение об ошибке по умолчанию!

Редактировать - Ответить!Вместо очистки списка и его повторного создания, просто обновление значений внутри него работает лучше: на данный момент у меня есть этот код:

            t_job = _jobs.FirstOrDefault(c => c.nzo_id == t_nzo_id);
            if (t_job == null) //Job not in list, insert
            {
                t_job = new Job(t_status, i_index, t_eta, i_timeLeft, t_age, i_mbleft, i_mb, t_filename, i_priority, t_category, i_percentage, t_nzo_id, this);
                jobs.Add(t_job);
            }
            else //update object in current list
            {
                jobs[t_job.Index].status = t_status;
                jobs[t_job.Index].priority = i_priority;
                jobs[t_job.Index].category = t_category;
                jobs[t_job.Index].percentage = i_percentage;
                jobs[t_job.Index].timeleft = i_timeLeft;
                jobs[t_job.Index].mbleft = i_mbleft;
            } 

Что мешает!

1 Ответ

1 голос
/ 04 июня 2011

Кажется, проблема в том, что с интервалом обновления, скажем, 1 секунда, когда пользователь прокручивает или пытается получить доступ к полям данных, данные постоянно удаляются и читаются.Это приводит к тому, что привязка для вызова try вызывает значение, которое, по ее мнению, все еще может быть, но это не так, и именно поэтому вы получаете индекс вне диапазона исключений

Первое, что я бы предложил сделать, этов вашем методе updateJobs, а также во всех других списках, которые обновляются как список очередей, вместо того, чтобы каждый раз очищать список, сначала проверяется, существует ли задание из xml в текущем списке заданий, и если да, то вы можете изменитьтекущие значения, если значение изменилось, иначе ничего не делают.

Чтобы проверить, существует ли задание, это так же просто, как:

t_job = _jobs.FirstOrDefault(c => c.Filename == t_filename);

Это вернет ноль, если задание не существуетЯ бы предположил, что имя файла может быть не уникальным, поэтому может потребоваться изменить его так, чтобы оно действительно было уникальным, то есть

t_job = _jobs.FirstOrDefault(c => c.Filename == t_filename && c.nzo_id == t_nzo_id);

Одна вещь, которую вам теперь придется обслуживать, заключается в том, что старые задания не будут автоматическиудаляется, поэтому при каждом добавлении нового задания истории сначала проверьте, существует ли оно в очереди, а затем удалите его, прежде чем добавлять всписок истории.

поэтому измените ваши свойства на:

public int Index 
        { 
            get { return _index; } 
            set 
            { 
                if (_index != value)
                    _index = value; 
            } 
        }

вместо:

public int Index { get { return _index; } set { _index = value; } }

Другое дело, что попытка перехвата исключений обходится дорого, поэтомувместо:

try { i_percentage = double.Parse(t_percentage); } catch { }

потому что вы знаете, что если есть значение, оно будет двойным, вы можете изменить его на:

if (!string.IsNullOrEmpty(t_percentage))
    i_percentage = double.Parse(t_percentage);

сейчас, если вы неЧтобы знать, будет ли значение в t_percentage двойным, вы можете использовать try-parse:

if (!string.IsNullOrEmpty(t_percentage))
    double.TryParse(t_percentage,out i_percentage);

Таким образом вы избежите накладных расходов, вызванных исключением.Это может быть микрооптимизацией и не всегда необходимо, если на самом деле это не вызывает проблемы, но, учитывая, что у вас может быть сотни заданий, каждое из которых имеет 10 или около того свойств, обновляющихся каждую секунду, на самом деле все может стать заметно медленнее, если даже 2 из10 свойств создает исключение.

Еще одна вещь, после того, как ваш фоновый работник завершил, в методе: dgvQueue_Update() вы вызываете ResetBindings(true);, это заставляет обновлять всю вашу сетку данных, а не только отдельные элементы.

попробуйте изменить его на:

for (int i = 0; i < jobSource.List.Count; i++)
    jobSource.ResetItem(i);

Разница в следующем:

this.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, itemIndex));

по сравнению с тем, когда вы говорите ResetBindings:

this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
...