Почему свойство элемента управления Windows Forms не может быть изменено во время события OnPaint

Я использую событие OnPaint (C#), чтобы нарисовать что-то в моей форме. Я хочу получить значение переменной во время процесса OnPaint. Но я не могу получить его во время, только до или после процесса OnPaint...

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

Я пытался добавить события Thread, Timer и ValueChanged, но все еще не могу получить значение. Код довольно длинный (он предназначен для создания HeatMap из некоторых данных).

Я увеличиваю значение в некоторых циклах for во время события и вызываю событие OnPaint с помощью функции "Invalidate()".

Я надеюсь быть ясным, не вставляя мой код (это очень долго)! Благодарю.

С кодом это лучше: (упрощенно)

public partial class HeatPainter : UserControl
{
    public long _progress = 0; //My counter

    public HeatPainter()
    {
        InitializeComponent();
    }

    public void DrawHeatMap(List<List<int>> Items, decimal Value, int MaxStacks, int Factor, string FileName)
    {
        if (_allowPaint) //If the control is ready to process
        {
            timer1.Start();
            _progress = 0;
            _allowPaint = false;
            Invalidate();
        }
    }


    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        for (int Pass = _factor; Pass >= 0; Pass--)
        {
            //Some draw stuff
            //...
            _progress++;
        }
     }
     private void timer1_Tick(object sender, EventArgs e)
    {
        Console.WriteLine(_progress);
    }
}

1 ответ

Решение

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

Так что стоит взглянуть на задачу с другой точки зрения. Что если вы создадите изображение (например, Как динамически создать изображение jpg в памяти с помощью.NET?) В параллельном потоке (или другой конструкции параллелизации http://www.dotnetperls.com/backgroundworker)? После того, как это нарисовано, вы установите его в качестве фона или .Image некоторых PictureBox. Форма будет отзывчивой все время.

Вам нужно будет выполнить синхронизацию (чтобы обновить индикатор выполнения), но это не такая сложная задача ( поток не обновляет элемент управления индикатором выполнения - C#, приложение Windows Forms на C# - обновление графического интерфейса пользователя из другого потока И класса?).

И на будущее: потоки и BackgroundWorkers постепенно уходят из мира.NET. Они все еще используются в.NET < 4.0, но.NET 4.0 и выше предоставляют лучшие абстракции для асинхронных операций. Я рекомендую вам прочитать о задачах и Async Await. Они больше подходят для многих сценариев "запускай, работай, получай результат по завершении".

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

public event EventHandler ProgressChanged;

и вызвать это событие в коде, который создает изображение при изменении свойства Progress, Только не забудьте про синхронизацию и диспетчеризацию (см. Выше).

Другие вопросы по тегам