Отключенная кнопка запускает события в WPF - PullRequest
6 голосов
/ 02 марта 2012
private void but_Click(object sender, RoutedEventArgs e)
{
    (sender as Button).IsEnabled = false;
    doSomeThing();//e.g run for more than 20 seconds
    (sender as Button).IsEnabled = true;
}

Когда я нажимаю кнопку в первый раз, она отключается. Затем запускается doSomeThing(), и он содержит обновленный код пользовательского интерфейса или некоторую переменную пользовательского интерфейса. Я имею в виду, что если я нажму кнопку еще раз, пока идет doSomeThing(), то событие but_Click снова запускается после включения этой кнопки назад. Он поддерживает очередь событий, то есть. n раз, которые я нажал.

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

Ответы [ 5 ]

4 голосов
/ 26 октября 2012

Кажется, что некоторые разъяснения в порядке ...

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

Есть несколько возможных решений, и я думаю, что @Amit был на правильном пути - использование потоков, вероятно, лучший путь.

Вот упрощенная версия того, что у меня сработало:

private void Button1_Click(object sender, EventArgs e)
{
    disableControls(); // e.g.- Button1.Enabled = false;

    // run "doSomething" in a separate thread
    new Thread(new ThreadStart(doSomething)).Start();
}

private void doSomething()
{
    // do something... make sure it's thread-safe!!
    // ...

    enableControls();  // a thread safe enabling of relevant controls
}

Хорошее объяснение, почему НЕ использовать Application.DoEvents(), , смотрите здесь

Для поточно-ориентированной манипуляции с состоянием элементов управления см .: Как сделать потокобезопасные вызовы элементов управления Windows Forms

1 голос
/ 18 марта 2018

Другой подход заключается в использовании API-функции PeekMessage, как показано в следующем коде.

using System.Runtime.InteropServices;
using System.Security;

[StructLayout(LayoutKind.Sequential)]
public struct NativeMessage
{
    public IntPtr handle;
    public uint msg;
    public IntPtr wParam;
    public IntPtr lParam;
    public uint time;
    public System.Drawing.Point p;
}

[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool PeekMessage(out NativeMessage message,
    IntPtr handle, uint filterMin, uint filterMax, uint flags);
private const UInt32 WM_MOUSEFIRST = 0x0200;
private const UInt32 WM_MOUSELAST = 0x020D;
public const int PM_REMOVE = 0x0001;

// Flush all pending mouse events.
private void FlushMouseMessages()
{
    NativeMessage msg;
    // Repeat until PeekMessage returns false.
    while (PeekMessage(out msg, IntPtr.Zero,
        WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE))
        ;
}

Этот код включает в себя несколько объявлений для функции API и ее параметров. (Вам также необходимо добавить операторы using для пространств имен System.Runtime.InteropServices и System.Security. Загрузите пример для получения подробной информации.)

Метод FlushMouseMessages вызывает PeekMessage, сообщая ему об отмене любого сообщения в диапазоне от WM_MOUSELAST до PM_REMOVE. Код вызывает PeekMessage несколько раз, пока не вернет false, чтобы указать, что таких сообщений нет.

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

private void but_Click(object sender, RoutedEventArgs e)
{
    (sender as Button).IsEnabled = false;
    doSomeThing();//e.g run for more than 20 seconds
    (sender as Button).IsEnabled = true;
    FlushMouseMessages();
}

Я выбрал вышеуказанный код с сайта http://csharphelper.com/blog/2015/08/flush-click-events-in-c/ Это работает для меня.

1 голос
/ 02 марта 2012

Проблема с этим кодом - doSomeThing() метод запущен в потоке пользовательского интерфейса.Таким образом, кнопка неправильно отключена.Если мы проведем рефакторинг кода так, что метод doSomeThing() будет запущен в другом потоке, он будет работать нормально.Вот простой пример использования BackgroundWorker;однако идея в том, что мы не должны запускать трудоемкие вещи в потоке пользовательского интерфейса.Вот переделанный код:

public partial class ButtonEnableTest : Window
{
    private BackgroundWorker worker = new BackgroundWorker();

    public ButtonEnableTest()
    {
        InitializeComponent();
        this.worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        this.worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
    }

    void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (this.btn.IsEnabled == false)
        {
            this.btn.IsEnabled = true;
        }
    }

    void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        doSomeThing();
    }

    private void doSomeThing()
    {
        int i = 5;
        while ( i > 0)
        {
            Thread.Sleep(TimeSpan.FromMilliseconds(2000));
            System.Diagnostics.Debug.WriteLine("Woke up " + i);
            i--;
        }
    }

    private void button1_Click(object sender, RoutedEventArgs e)
    {
        Button btn = (Button) sender;
        System.Diagnostics.Debug.WriteLine("at ButtonClick");
        if (btn.IsEnabled)
        {
            btn.IsEnabled = false;
            this.worker.RunWorkerAsync();
        }
    }
}

Я не следовал здесь никаким преобразованиям кодирования, так как я просто хотел поделиться своей идеей.Обратите внимание, что я назвал кнопку WPF "btn".

0 голосов
/ 06 июля 2015

У меня была похожая проблема с событием TextChanged, и я решил ее с помощью этого кода:

private void TB_box_TextChanged(object sender, TextChangedEventArgs e)
{
   //detach event
   TB_box1.TextChanged -= TB_box_TextChanged;
   TB_box2.TextChanged -= TB_box_TextChanged;

   //change my value
   TB_box1.Text = "a"
   TB_box2.Text = "b"

   //attach event     
   TB_box1.TextChanged += TB_box_TextChanged;
   TB_box2.TextChanged += TB_box_TextChanged;

}
0 голосов
/ 02 марта 2012
private void but_Click(object sender, RoutedEventArgs e)
{
    Button but = (sender as Button)
    if(but.IsEnabled)
    {
       but.IsEnabled = false;
       doSomeThing();
       but.IsEnabled = true;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...