«Умная» связанная полоса прокрутки и элементы управления Edit? - PullRequest
2 голосов
/ 17 июня 2009

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

По сути, у меня есть горизонтальная полоса прокрутки (в диапазоне от 0 до 1000) и элемент управления для редактирования, который представляет положение полосы прокрутки, деленное на 1000, так что пользователь может использовать любую полосу прокрутки для выбора диапазона чисел от 0 до 1 с точностью до 3 десятичных знаков (.001, .002, ..., .987 и т. д.) или введите свой номер в поле для редактирования. Когда они прокручивают полосу прокрутки, число в элементе управления редактирования изменяется, отражая новую позицию прокрутки. Когда вводится новый номер, полоса прокрутки устанавливает себя в новую позицию, отражающую введенный номер. Тем временем я также выполняю некоторые вычисления с этим числом по мере его изменения (либо с помощью полосы прокрутки, либо с помощью элемента управления редактирования) и отображаю результаты в другом диалоговом окне.

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

Я использую переменную с двойным значением fuelMargin для управления моим элементом управления и переменную CScrollBar, которая называется FuelScroll для обработки полосы прокрутки.

В моем событии HSCROLL я установил регулятор редактирования в положение прокрутки / 1000. Никаких проблем там нет; когда пользователь прокручивает полосу прокрутки, окно редактирования корректно обновляется.

Что касается поля ввода, моей первой попыткой было событие ONCHANGE:

void MarginDlg::OnEnChangeFueledit()
{
    CEdit* editBox;
    editBox = (CEdit*)GetDlgItem(IDC_FUELEDIT);

    CString editString;
    editBox->GetWindowText(editString);

    if (editString.Compare(".") != 0 && editString.Compare("0.") != 0
        && editString.Compare(".0") != 0 && editString.Compare("0.0") != 0
        && editString.Compare(".00") != 0 && editString.Compare("0.00") != 0)
    {
        UpdateData();
        UpdateData(FALSE);

        if (fuelMargin > 1)
        {
            UpdateData();
            fuelMargin = 1;
            UpdateData(FALSE);
        }
        if (fuelMargin < 0)
        {
            UpdateData();
            fuelMargin = 0;
            UpdateData(FALSE);
        }
        fuelScroll.SetScrollPos(int(fuelMargin*1000));
    }
}

Сначала мне понадобился оператор if, чтобы не выполнять UpdateData (), когда пользователь пытается набрать число, например .5 или .05 или .005. Тем не менее, он производит несколько странных поведений; когда пользователь пытается набрать что-то вроде .56, после .5 выполняется UpdateData (), число становится равным 0,5, и курсор перемещается в крайнее левое положение, поэтому, если он попытается ввести .56, он случайно окажется набрав 60,5 - что соответствует 1, поскольку я не позволю им вводить числа ниже 0 или выше 1. Однако, если они введут 0,56, такого поведения избегают.

Для второй попытки я закомментировал событие ONCHANGE и вместо него поместил событие ONKILLFOCUS:

void MarginDlg::OnEnKillfocusFueledit()
{
    UpdateData();
    UpdateData(FALSE);

    if (fuelMargin > 1)
    {
        UpdateData();
        fuelMargin = 1;
        UpdateData(FALSE);
    }
    if (fuelMargin < 0)
    {
        UpdateData();
        fuelMargin = 0;
        UpdateData(FALSE);
    }
    fuelScroll.SetScrollPos(int(fuelMargin*1000));
}

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

Я хочу, чтобы результаты вычислялись по мере ввода чисел в поле; Я хочу, чтобы полоса прокрутки двигалась по мере набора цифр. Но я не хочу, чтобы ввод был прерван, т. Е. Фактические цифры в поле изменились или курсор каким-либо образом переместился.

Предложения

Спасибо!

Ответы [ 2 ]

2 голосов
/ 17 июня 2009

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

Учитывая, что вы пытаетесь установить достаточно сложное взаимодействие между элементами управления, я бы рекомендовал вообще не выполнять проверку в OnChange (), чтобы, когда пользователь печатал, он мог печатать то, что хочет. (именно так все равно работают большинство числовых элементов управления редактированием). Когда пользователь закрывает диалоговое окно, элементы управления включены (или щелкают кнопку, которая каким-либо образом использует данные), тогда должна быть запущена проверка и показана соответствующая ошибка.

Как только вы освободитесь от проверки в OnChange (), вы можете исправить проблему с «перемещением курсора», просто не вызывая UpdateData () в OnChange (). Вместо этого просто проанализируйте число из «editString» и, если оно находится в допустимом диапазоне, обновите полосу прокрутки. Таким образом, полоса прокрутки обновляется по мере ввода пользователем данных, и если они вводят недопустимое значение, полоса прокрутки остается на месте, и они получат ошибку при переходе к следующему этапу. Как то так (не проверено):

void MarginDlg::OnEnChangeFueledit()
{
  CString editString;
  GetDlgItem(IDC_FUELEDIT)->GetWindowText(editString);

  double editValue;
  if ((sscanf(editString,"%lf",&editValue) == 1)
  {
    if (editValue >= 0.0) && (editValue <= 1.0))
      fuelScroll.SetScrollPos(int(editValue*1000));
  }
}

Единственная оставшаяся важная проблема, на которую следует обратить внимание, это то, что, если пользователь вводит какое-то недопустимое значение или число вне допустимого диапазона, то элемент управления для редактирования и полоса прокрутки будут не синхронизированы. Самый простой способ справиться с этим - просто решить, что элемент управления для редактирования является «основным» значением: то есть, когда мы хотим узнать, что ввел пользователь, мы всегда смотрим на элемент управления для редактирования, а не на полосу прокрутки, и проверяем данные. .

Что касается вашего второго подхода, одним из возможных решений может быть реализация обработчика сообщений таймера: в обработчике таймера вы можете сказать эквивалент «если пользователь ничего не набрал в течение секунды, я буду считать, что они» готово, и разобрать номер и обновить полосу прокрутки ". Но я не очень заинтересован в этом как в решении.

0 голосов
/ 17 июня 2009

Я бы посоветовал наблюдать за клавишей Enter и затем выполнять UpdateData (), а также OnKillFocus и OnChange. Чем удобнее пользователь, тем лучше.

Далее, убедитесь, что ваша подпрограмма UpdateData () работает только в нужном вам направлении:

Если в элементе редактирования введено «.5», запустите процедуру UpdateData () при возникновении события OnChange, но обязательно обновляйте только полосу прокрутки. Не беспокойтесь об обновлении элемента управления «0.5» до тех пор, пока не будет поднят OnKillFocus. Любые обновления в обратном направлении (к элементу управления редактированием) будут мешать вашему курсору. Если ваш элемент управления редактирования каким-либо образом привязан к этой двойной переменной и автоматически обновляется при изменении переменной var, рассмотрите возможность оставить элемент double и ваш элемент управления редактирования отделенными друг от друга, пока не будет вызвано событие OnKillFocus.

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

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

...