Application.DoEvents () вызывается постоянно во время работы приложения? - PullRequest
1 голос
/ 13 марта 2011

Я пытался добавить модуль кэширования формы для своего приложения, и я реализовал этот пример msdn: Формирование стека и кэширование

Для тех, кто не может загрузить образец, этот класс содержит фрагмент кода, на который я ссылаюсь:

using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Threading;

namespace GUIFramework {
    public class FormStack : CollectionBase {
        private System.Collections.ArrayList stack = new ArrayList();

    public void Run() {
        do {
            System.Windows.Forms.Application.DoEvents();
        } while(List.Count > 0);
    }

    public void Stop() {
        // nicely destroy each Form
        foreach(StackForm sf in List) {
            sf.Dispose();
        }
        // clear the list to kill the message pump in Run()
        List.Clear();
    }

    public void Push(Type FormType) {
        // only allow 1 Push at a time to maintain cache and stack itegrity
        Monitor.Enter(this);

        foreach(StackForm sf in List) {
            if(sf.GetType().Name.Equals(FormType.Name)) {
                // form is cached so display cached version
                sf.Visible = true;

                // add it to the stack
                stack.Add(FormType.Name);

                return;
            }
        }

        // the Form wasn't cached, so create it
        StackForm form = Preload(FormType);

        // display it
        form.Visible = true;

        // add a close event handler
        form.FormClosed += new FormClose(form_FormClosed);

        // add it to the stack
        stack.Add(FormType.Name);

        Monitor.Exit(this);
    }

    public StackForm Preload(Type FormType) {
        StackForm form = (StackForm)Activator.CreateInstance(FormType);

        // get data on a separate thread
        form.LoadData();

        // build the form
        form.InitializeComponent();

        // wait for the data thread to finish
        form.threadRunning.WaitOne();

        // now populate the controls with any retrieved data
        form.Populate();

        form.MinimizeBox = false;  // required to get close event on PPC!

        // add it to the cache
        List.Add(form);

        return form;
    }

    public void Pop(uint FormsToPop) {
        if(stack.Count <= FormsToPop) {
            throw new Exception("You cannot Pop the entire stack!");
        }
        else {
            // remove from stack but not cache
            for(int i = 0 ; i < FormsToPop ; i++) {
                stack.RemoveAt(stack.Count - 1);
            }

            foreach(StackForm sf in List) {
                // find the last form in the stack
                if(sf.GetType().Name.Equals(stack[stack.Count - 1])) {
                    // make it visible
                    sf.Visible = true;
                }
            }
        }
    }

    private void form_FormClosed() {
        Pop(1);
    }

    public override string ToString() {
        string message = "There are " + List.Count.ToString() + " forms cached\r\n";
        message += "Stack contents:";

        for(int i = stack.Count - 1 ; i >= 0 ; i--) {
            message += "\r\n" + stack[i].ToString();
        }

        return message;
    }
}
}

Метод Run() постоянно вызывает Application.DoEvents(), пока приложение работает. Мне трудно поверить, что это хорошая вещь для приложения. Я хотел бы услышать другие мнения по этому поводу. Благодаря.

1 Ответ

1 голос
/ 14 марта 2011

Для начала нужно понять, как работают приложения WinForms в целом. Глубоко в недрах вызова Application.Run находится фрагмент кода, часто называемый насосом сообщений. Это бесконечный цикл, который вызывает GetMessage, TranslateMessage и DispatchMessage. Application.DoEvents, по сути, вызывает один цикл этого цикла. Цикл DoEvents, на который вы смотрите, просто выполняет эту операцию.

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

Если удалить петлю, что произойдет? Я не вижу ничего, что указывало бы на то, что вы могли бы рисовать артефакты рисования или сбои рисования (что является типичной причиной добавления вызова), но этот код был также написан в CFan 1.0, так что вполне возможно, что нужно было преодолеть ограничение времени выполнения.

Что бы это ни стоило, я пересмотрел и переписал код в 2009 , и в переписанном коде нет цикла (или даже метода Run).

Кстати, в Push есть ошибка, которую я вижу. Цикл foreach имеет return без выполнения Monitor.Exit. Вероятно, я бы переписал этот метод для использования блока lock или finally для вызова Monitor.Exit.

...