Функциональность отмены / повтора в DataGridView c #

Я просмотрел много тем, связанных с этой темой, но не смог найти или понять таблицу datagridview. Наконец, я сам сделал реализацию отмены / повтора, используя списки данных. Думаю, это решение подходит для небольших таблиц. Я не знаю, будет ли он эффективно работать с таблицами с большим количеством данных. Я поделюсь этим решением здесь. Может быть, я внесу свой вклад в этот сайт, где я многому научился. Также жду комментариев. Это решение глупое? или это работает? Мне нужны ваши комментарии для улучшения, или откажитесь. Спасибо.

Сначала мы добавим в нашу форму DataGridView и две кнопки с именами «Отменить» и «Вернуть».

Это коды ниже;

using System.Windows.Forms;



namespace WindowsFormsApp1

{

    public partial class Form1 : Form

    {

        DataTable dt = null;

        List<DataTable> dtList = new List<DataTable>(); // This list for recording every movements. If we want to do undo/redo, we will get data from this list to dataGridview



        public Form1()

        {
            InitializeComponent();

            dataGridView1.Rows.Add(20);// Add row

            dt = GetDataTableFromDGV(dataGridView1);// make datatable at the beginning

            dtList.Clear();

            dtList.Add(dt);// Beginning data is added to List
        }

        public DataTable GetDataTableFromDGV(DataGridView dgv)// This methot makes a DataTable from DataGridView control.
        {
            var dt = new DataTable();

            foreach (DataGridViewColumn column in dgv.Columns)

            {
             dt.Columns.Add(column.Name);
            }

            object[] cellValues = new object[dgv.Columns.Count];

            foreach (DataGridViewRow row in dgv.Rows)

            {

                for (int i = 0; i < row.Cells.Count; i++)

                {

                    cellValues[i] = row.Cells[i].Value;

                }

                dt.Rows.Add(cellValues);

            }

           return dt;

        }

        int counterUndo = 2;// This counts clicked redo button 

        int dtCount = 0;// This keeps DataTable count

        bool clickedUndo;// This checks Undo button clicked or not.

        private void btn_Undo_Click(object sender, EventArgs e)

        {
            clickedUndo = true;

            dtCount = dtList.Count;

            if (dtCount - counterUndo > -1)

            {
                int undoIndex = dtCount - counterUndo;

                datatablaToDataGrid(dataGridView1, dtList[undoIndex]);

                counterUndo++;
            }
        }

        private void btn_redo_Click(object sender, EventArgs e)

        {
            int redoIndex = dtCount - counterUndo + 2;

            if (counterUndo > 1&&redoIndex< dtCount)

            {
               datatablaToDataGrid(dataGridView1, dtList[redoIndex]);

                counterUndo--;
            }
        }

        public void datatablaToDataGrid(DataGridView dgv, DataTable datatable)// This methot gets data from DataTable to DataGridView control. 

        {
               for (int i = 0; i < datatable.Rows.Count; i++)
                {
                    for (int j = 0; j < datatable.Columns.Count; j++)

                    {
                          dgv.Rows[i].Cells[j].Value = datatable.Rows[i][j].ToString(); 
                    }
                }
        }

        private void dataGridView1_CellValidated(object sender, DataGridViewCellEventArgs e)// This event check if the cell value is change or stay same. 

        {
            DataGridView dgv = (DataGridView)sender;

            int r = e.RowIndex;

            int c = e.ColumnIndex;

            if (dgv.Rows[r].Cells[c].Value != null)

            {
                string dgvResult = dgv.Rows[r].Cells[c].Value.ToString();

                string dtResult = dt.Rows[r][c].ToString();

                if (dgvResult != dtResult)

                {

                    if (clickedUndo)

                    {

                        doWhenClickedUndo(counterUndo, dtList);

                    }

                    dt = GetDataTableFromDGV(dataGridView1);

                    dtList.Add(dt);

                    counterUndo = 2;
               }
            }
        }

        private void doWhenClickedUndo(int _counterUndo, List<DataTable> _dtList)
        {
            if (_counterUndo != 2)

            {
                int f = counterUndo - 2;

                int lastIndex = _dtList.Count - 1;

                int i = lastIndex;

                do
                {
                    _dtList.RemoveAt(i);

                    i--;

                } while (i > lastIndex - f);

               }

            clickedUndo = false;

        }

    }
}

введите описание изображения здесь

1 ответ
1

Обзор

Добро пожаловать в Code Review. Есть несколько предложений, как показано ниже.

Макет и форматирование

Не уверен, почему между фигурными скобками и определением метода много новых строк, if заявления и for заявления. Для улучшения читаемости ненужные символы новой строки могут быть удалены.

Магические числа и List<DataTable>

Я понятия не имею, почему начальное значение counterUndo установлен на 2 (в int counterUndo = 2) и почему проверка неравенства if (_counterUndo != 2) необходимо в doWhenClickedUndo метод. Ограничено ли время отмены действия в 2 раза? Как насчет случая, когда пользователь хочет отменить еще несколько шагов? Чтобы решить эту проблему, я попытался использовать Stack<DataTable> вместо List<DataTable> таким образом Push, Pop и First методы доступны (Stack полезно для поддержания исторической последовательности, например, состояния DataTable здесь). Следующий код является примером реализации с Stack класс.

public partial class Form1 : Form
{
    Stack<DataTable> dtStack = new Stack<DataTable>();
    int RecordIndex = 0;
    bool UndoRedo = false;

    public Form1()
    {
        InitializeComponent();

        //    Construct Columns
        dataGridView1.ColumnCount = 1;
        dataGridView1.Columns[0].Name = "0";

        dataGridView1.Rows.Add(20);// Add row

        dtStack.Clear();

        dtStack.Push(GetDataTableFromDGV(dataGridView1));
        UpdateBtnStatus();
    }

    public DataTable GetDataTableFromDGV(DataGridView dgv)
    {
        var dt = new DataTable();

        foreach (DataGridViewColumn column in dgv.Columns)
        {
            dt.Columns.Add(column.Name);
        }

        object[] cellValues = new object[dgv.Columns.Count];

        foreach (DataGridViewRow row in dgv.Rows)
        {
            for (int i = 0; i < row.Cells.Count; i++)
            {
                cellValues[i] = row.Cells[i].Value;
            }
            dt.Rows.Add(cellValues);
        }
        return dt;
    }

    public void datatablaToDataGrid(DataGridView dgv, DataTable datatable)
    {
        for (int i = 0; i < datatable.Rows.Count; i++)
        {
            for (int j = 0; j < datatable.Columns.Count; j++)
            {
                dgv.Rows[i].Cells[j].Value = datatable.Rows[i][j].ToString();
            }
        }
    }

    private void UpdateBtnStatus()
    {
        if (RecordIndex == this.dtStack.Count - 1)
            this.btn_Undo.Enabled = false;
        else
            this.btn_Undo.Enabled = true;

        if (RecordIndex == 0)
            this.btn_redo.Enabled = false;
        else
            this.btn_redo.Enabled = true;
    }

    private void btn_Undo_Click(object sender, EventArgs e)
    {
        UndoRedo = true;
        datatablaToDataGrid(dataGridView1, dtStack.ToList()[++RecordIndex]);
        UpdateBtnStatus();
        UndoRedo = false;
    }

    private void btn_redo_Click(object sender, EventArgs e)
    {
        UndoRedo = true;
        datatablaToDataGrid(dataGridView1, dtStack.ToList()[--RecordIndex]);
        UpdateBtnStatus();
        UndoRedo = false;
    }

    private void dataGridView1_CellValidated(object sender, DataGridViewCellEventArgs e)
    {
        UpdateBtnStatus();

        if (UndoRedo)
            return;

        DataGridView dgv = (DataGridView)sender;
        int r = e.RowIndex;
        int c = e.ColumnIndex;
        if (dgv.Rows[r].Cells[c].Value != null)
        {
            string dgvResult = dgv.Rows[r].Cells[c].Value.ToString();
            string dtResult = dtStack.ElementAt(RecordIndex).Rows[r][c].ToString();
            if (dgvResult != dtResult)
            {
                while (RecordIndex > 0)
                {
                    dtStack.Pop();
                    RecordIndex--;
                }

                dtStack.Push(GetDataTableFromDGV(dataGridView1));
            }
        }
        UpdateBtnStatus();
    }
}

  • 1

    Привет, нет предела отмены / повтора, если вы пытались. Почему counterUndo = 2?. Эта переменная используется для вызова правильного номера индекса из списка Datatable, когда пользователь нажимает кнопку «Отменить» или «Повторить». Это моя вина, что я не дал достаточно пояснений в кодах. Спасибо вам и за ваше время. Я воспользуюсь вашими кодами. Я хочу спросить, правильное ли решение использовать Datatable для отмены / повтора. Потому что после каждого действия отмены / повтора мы получаем данные из DataTable в Datagridview. Есть ли у этого недостатки? Если в таблице много данных, это замедлит работу приложения?

    – Гохан

  • Я пробовал ваши коды. Он работает, но есть проблема с повторным щелчком. Работает после двух движений. Первое движение не отменяется. Не могли бы вы отредактировать свои коды, тогда я буду отмечен как ответ.

    – Гохан

  • @Gokhan> Он работает, но есть проблема с повторным щелчком. Работает после двух движений. Не могли бы вы сообщить мне, какие шаги вы тестируете? Приведенный пример кода, который я пробовал, работал хорошо, независимо от того, какие движения.

    – JimmyHu


  • Спасибо за внимание. Я добавил картинку по ссылке ниже для ясного объяснения. связьСм. Ссылку.

    – Гохан

  • 2

    Я попробовал сейчас. Он работает хорошо. Большое спасибо, @JimmyHu. Я новичок в программировании, и теперь я узнал, что класс стека используется в приложениях отмены повтора.

    – Гохан


Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *