Хобрук: Ваш путь к мастерству в программировании

Как лучше всего замаскировать событие пользовательского интерфейса?

Я работаю с DataGridView и использую событие CellValueChanged.

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

Вот почему я заключаю свои операции по изменению значений ячеек в следующий код:

void changeCellOperation()
{
    dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged);

    ...
    cell.Value = myNewCellValue
    ...

    dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged);
}

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

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

Итак, я закончил так:

int valueChangedEventMask = 0;

void changeCellOperation()
{
    valueChangedEventMask++;

    ...
    cell.Value = myNewCellValue
    ...

    valueChangedEventMask--;
}

void dgv_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
    if (valueChangedEventMask > 0)
        return

    ...
}

Это прекрасно работает. Также, когда вызовы вложены, в том числе внутри самого события.

Но событие CellValueChanged теперь запускается слишком много раз без причины.

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

Итак, мой вопрос:

Каков лучший совет, чтобы правильно замаскировать события пользовательского интерфейса и избежать ненужных срабатываний событий?



Ответы:


1

CellValueChanged - это не событие пользовательского интерфейса, а событие изменения свойства. Это означает, что вы не можете использовать его, чтобы отличить ввод пользователя от программного изменения. Вы всегда можете использовать технику подписчика / отказа от подписки или флага +/- или BeginEdit / EndEdit, но, возможно, вам придется найти другой (лучший) подход. Например, в случае флажка вы можете использовать событие Click вместо Changed, потому что (сюрприз!) Оно сообщит вам, когда пользователь щелкнет его, и в противном случае безопасно измените значение Checked программно.

В случае DataGridView проще всего было бы использовать Changed с некоторым флагом (который будет установлен, когда начинается редактирование, и сбрасывается, когда заканчивается - см. CellBeginEdit/CellEndEdit).

18.02.2013

2

Вы можете использовать CellEndEdit вместо CellValueChange. Я не знаю, что делает ваш метод dgv_CellValueChanged, просто будьте осторожны, чтобы CellEndEdit запускался каждый раз, когда вы выходите из режима редактирования ячейки, даже если его значение не было изменено. Это означает, что вам нужно отслеживать текущие значения ваших ячеек, если вы не хотите, чтобы метод выполнялся, когда значение не изменяется.

Я бы избегал событий, связанных с мышью, таких как CellClick, потому что ваши пользователи могут использовать только клавиатуру.

В любом случае я обычно избегаю подобных проблем, отделяя логику от пользовательского интерфейса, т.е. пишу отдельный класс, привязанный к форме. Взгляните на MVVM (вы можете реализовать свою собственную версию в WinForms, если вы хотите) или старый добрый MVC.

18.02.2013
  • Спасибо. Это полностью ручной DataGridView. С ним не связаны ни источник данных, ни механизм привязки данных. Я попробую то, что вы предлагаете, с CellEndEdit. 18.02.2013
  • Я имею в виду, что если у меня нет механизма привязки данных, я создаю его сам. Нужно написать больше кода, но я думаю, что это того стоит, особенно если вы разрабатываете свой собственный фреймворк, который можно использовать во многих различных проектах. 19.02.2013
  • Согласованный. Мои требования заключаются в том, что для некоторых ячеек требуются определенные цвета в зависимости от их значений, некоторые ячейки переключаются из / в режим только для чтения, другие ячейки являются вычисляемыми значениями, плюс сама сетка транспонируется, поэтому столбец является строкой в ​​моей модели данных ... Логика довольно сложно, и я вынужден проектировать и заполнять сетку вручную. Должен признаться, это приятнее, чем кажется, и это достигается очень чисто. Я даже нахожу некоторые возобновляемые фрагменты кода. Я не думаю, что сторонний компонент потребует меньше работы. Повторное открытие DataGridView таким образом заставило меня почувствовать себя кодирующим в Excel :) 19.02.2013

  • 3

    В итоге я смешал оба решения в очень простом. Я использую счетчик и подключаю / отключаю только те события, которые хочу замаскировать.

    EventMask valueChangedEventMask;
    
    // In the class constructor
    valueChangedEventMask = new EventMask(
        () => { dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged); },
        () => { dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged); }
    );
    
    // The value change operation I want to hide from the event
    void changeCellOperation()
    {
        valueChangedEventMask.Push();
    
        ...
        cell.Value = myNewCellValue
        ...
    
        valueChangedEventMask.Pop();
    }
    
    // The class
    public class EventMask
    {
        Action hook;
        Action unHook;
    
        int count = 0;
    
        public EventMask(Action hook, Action unHook)
        {
            this.hook = hook;
            this.unHook = unHook;
        }
    
        public void Push()
        {
            count++;
            if (count == 1)
                unHook();
        }
    
        public void Pop()
        {
            count--;
            if (count == 0)
                hook();
        }
    }
    
    18.02.2013
    Новые материалы

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

    Расширенные методы безопасности для VueJS: реализация аутентификации без пароля
    Руководство, которое поможет вам создавать безопасные приложения в долгосрочной перспективе Безопасность приложений часто упускается из виду в процессе разработки, потому что основная..

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..