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

В моем приложении есть NumericUpDown, но это опасно. При изменении значения весь документ стирается. Из-за этого я хотел бы дать пользователю предупреждение (даже если он случайно нажмет OK, он может отменить это).

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

private bool ignoreValueChanged = false;

private void numFoobar_ValueChanged(object sender, EventArgs e)
{
    if (ignoreValueChanged)
    {
        ignoreValueChanged = false;
        return;
    }

    if (MessageBox.Show("This will erase the entire document. Are you sure?", "Confirmation", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
    {
        ignoreValueChanged = true;
        numFoobar.Value = oldValue; // The ValueChanged event gets called again =/
        return;
    }

    // More code
}

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


person John Smith    schedule 19.08.2011    source источник
comment
Не могли бы вы поставить «замок» на этот элемент управления, чтобы он не был включен, и пользователь должен был нажать кнопку, чтобы включить его. В целом кажется опасной вещью в вашем приложении   -  person ScruffyDuck    schedule 19.08.2011
comment
Даже если вы заблокируете его, как только ваш основной поток завершит задачу в этом событии, событие, которое было поставлено в очередь, возьмет задание на его обработку. Так что это круговая петля каким-то образом. Да, это опасно, но должен быть способ решить эту проблему.   -  person Zenwalker    schedule 19.08.2011
comment
Что касается события Validating, вы пытались добавить его в обработчики событий для элемента управления? То есть что-то вроде numFoobar.Validating += new CancelEventHandler(numFoobar_ValueChanged)? Наверно не поможет, но решил выкинуть на всякий случай.   -  person Tim    schedule 19.08.2011
comment
Я не имел в виду такую ​​​​блокировку, поэтому она была в «Я хотел заставить пользователя выполнить действие, такое как нажатие кнопки, прежде чем он сможет получить доступ к числовому элементу управления.   -  person ScruffyDuck    schedule 19.08.2011
comment
@Tim: Не уверен, что ты имеешь в виду. Событие проверки не вызывается при изменении значения.   -  person John Smith    schedule 19.08.2011
comment
@John Smith, он вызывается, только что попробовал пример здесь (посмотрите код в моем сообщении с ответом, пожалуйста), и он вызывается. Пробуем это на фреймворке SharepDevelop и 3.5 :)   -  person Zenwalker    schedule 19.08.2011
comment
@John Smith - Вот почему я предложил попробовать подписаться на мероприятие; может быть дело не в том, что оно не вызывается, а в том, что нет никакого метода (делегата), подписанного на это событие для его обработки. Опять же, просто мысль - она ​​может быть недействительной.   -  person Tim    schedule 19.08.2011
comment
Ах, прошу прощения. Я неправильно понял numFoobar_ValueChanged вместо numFoobar_Validating. Тем не менее, я не уверен, что это должно решить. Проблема все еще существует.   -  person John Smith    schedule 19.08.2011
comment
@Джон Смит - нет проблем. Я только что попробовал, и это не сработало, хотя событие подписано. Bizzare – зачем проводить мероприятие, которое не срабатывает? Если он не унаследован от базового класса Control?   -  person Tim    schedule 19.08.2011


Ответы (4)


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

Но я также думаю о том, как проверить, подписано ли событие уже или нет. Но вышеописанный метод даст вам половину решения.

Здесь я попробовал это немного, и, похоже, это работает, но я не могу понять, как проверить, подписано ли уже то же самое событие или нет.

void NumericUpDown1ValueChanged(object sender, EventArgs e)
        {
            if(numericUpDown1.Value > 10)
            {numericUpDown1.ValueChanged -= new System.EventHandler(this.NumericUpDown1ValueChanged);
            numericUpDown1.Text = "5";
            }               
            else numericUpDown1.ValueChanged += NumericUpDown1ValueChanged;//Here i need to first check if already it is subscribed or not before such that i dont want to subscribe double time
        }
person Zenwalker    schedule 19.08.2011
comment
Я полагаю, это может сработать. Хотя выглядит небрежно. Если бы только был способ изменить значение NumericUpDown без срабатывания события. - person John Smith; 19.08.2011

Погуглил, вот что может сработать:

private void numFoobar_ValueChanged(object sender, EventArgs e)
{

    this.ValidateChildren();
}

private void numFoobar_Validating(object sender, CancelEventArgs e)
{

    if (MessageBox.Show("This will erase the entire document. Are you sure?", "Confirmation", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
    {
        e.Cancel = true;
    }
}

Обратите внимание, что вам нужно будет сбросить значение, так как отмена проверки не изменит значение. Но это единственный способ запустить событие Validating.

Метод ContainerControl.ValidateChildren

Однако есть несколько проблем, которые нужно решить с этим:

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

  2. Я играл со сбросом значения в событии ValueChanged, но это снова вызывало событие Validating.

Я немного поиграю с этим и посмотрю, смогу ли я найти для вас более надежное решение.

person Tim    schedule 19.08.2011
comment
Это, безусловно, шаг в правильном направлении, но с ним есть три проблемы. 1) Обработчик вызывается при выходе из приложения. 2) Это не останавливает изменение значения (не большая проблема) и 3) Я не могу иметь более одного NumericUpDown с проверкой, потому что вызов this.ValidateChildren(); вызывает событие Validating для всех NumericUpDown, а не только для одного. - person John Smith; 19.08.2011
comment
Я пока в тупике. Простого способа сделать то, что вы хотите, может не быть, если только вы не хотите потратить время на написание собственного NumericUpDown, производного от элемента управления .NET (даже в этом случае вы можете не получить желаемую функциональность). Я еще не готов сдаться... но пока пора спать. К сожалению :( - person Tim; 19.08.2011

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

person Miserable Variable    schedule 19.08.2011

Немного погуглил. Во-первых, я придумал это:

typeof(NumericUpDown).GetField("currentValue", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(numericUpDown1, 5m);

Что работает, но это отражение, и оно кажется немного чрезмерным, поэтому я отказался от него. Затем я нашел это:

C# winforms numericupdown control

И основывал свое решение на втором ответе, что, если честно, не так уж и плохо.

person John Smith    schedule 19.08.2011