Обработчик событий больше не запускается

#c# #events #event-handling

#c# #Мероприятия #обработка событий

Вопрос:

Сегодня днем я был на работе, пытаясь использовать backgroundworker для чтения из довольно большого XML-файла, расположенного в моей сборке. Он работал довольно хорошо до того момента, пока я не решил переименовать имя объекта backgroundworker. После того, как я создал свое решение, оно сообщило мне, что оно было успешным. После запуска моей программы и ее тестирования я заметил, что событие DoWork backgroundworker вообще отказывается запускаться. Когда я добавил приведенный ниже код в событие RunWorkerCompleted для Backgroundworker, все, что я получил, это окно сообщения, содержащее большое жирное ничто. MessageBox.Show(e.Result " " e.Error); Переименование его обратно в то, что было, также не помогло. Я написал весь код сам, поэтому здесь не задействованы сторонние приложения.

Вот код, который я использовал для настройки работы с backgroundworker

 private volatile List<string> listItems = new List<string>();
    private BackgroundWorker _populateListItems = new BackgroundWorker();
    private string currentConnection;
    #endregion
    public frmSettings()
    {
        InitializeComponent();

        //I will be able to track how far the list is populated with the following command.
        _populateListItems.WorkerReportsProgress = true;
        //This will fire if there is an error in the xml file.
        _populateListItems.WorkerSupportsCancellation = true;
        //Assign the job to be done for the backgroundworker
        _populateListItems.DoWork  =new DoWorkEventHandler(_populateListItems_DoWork);
        _populateListItems.ProgressChanged  = new ProgressChangedEventHandler(_populateListItems_ProgressChanged);
        //When the worker is finished, the following event will fire: All UI controls will be updated.
        _populateListItems.RunWorkerCompleted  = new RunWorkerCompletedEventHandler(_populateListItems_RunWorkerCompleted);

        _populateListItems.RunWorkerAsync();
    }

    void _populateListItems_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        prgProgress.Value = e.ProgressPercentage;
        lblProgress.Text = e.ProgressPercentage.ToString()   "%";
    }

    private void _populateListItems_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //The operation is completed/Cancelled/Erroneous. The list will now be populated with the available information.
        MessageBox.Show(e.Result   " "   e.Error);
        foreach (string item in listItems)
        {
            liConnections.Items.Add(item);
        }

        //This shows the user which connection is currently used.
        lblCurrentSelection.Text = currentConnection;
    }

    private void _populateListItems_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            //The following lines of code are the variables that are going to be used.
            // xReadSettings will make the XmlReader xRead Ignore whitespaces and comments, if any.
            // assemblyExecuted will get information from which assembly gets Executed.
            // filename Will get the filename of the settings.xml file that is going to be used.
            // SettingsStream will open the stream for reading and writing, using the GetManifestResourceStream() method from the currently executed assembly.
            XmlReaderSettings xReadSettings = new XmlReaderSettings();
            Assembly assemblyExecuted = Assembly.GetExecutingAssembly();
            string filename = String.Format("{0}.Settings.xml", assemblyExecuted.GetName().Name);
            Stream settingsStream = assemblyExecuted.GetManifestResourceStream(filename);

            xReadSettings.IgnoreComments = true;
            xReadSettings.IgnoreWhitespace = true;

            //Finally the xmlReader object is created using the settingstream, and its settings.
            //While the stream reads, it checks whether the nodes accessed are elements of the xml file.
            //if it is an element that is accessed, then we check whether the element which is accessed is a connection string to the database
            //if it is a connectionstring to the database, we will check if the connection has a name.
            //if it has a name, we get the name attribute, and add it to our list. The list is volatile, so it will be up to date, because this
            //background thread is updating it.
            //To Update the progress of the backgroundworker, we need to know the amount of elements in the XML File. Xpath will be used for this.
            XmlDocument xdoc = new XmlDocument();
            xdoc.Load(settingsStream);
            XmlNodeList nodes = xdoc.SelectNodes("*"); //Xpath - select all.
            int totalElementsToRead = nodes.Count;
            int totalElementsRead = 0;
            using (XmlReader xRead = XmlReader.Create(settingsStream, xReadSettings))
            {
                while (xRead.Read())
                {
                    if (xRead.NodeType == XmlNodeType.Element)
                    {
                        if (xRead.Name == "ConnectionString")
                        {
                            if (xRead.HasAttributes)
                            {
                                string attribute = xRead.GetAttribute("name").ToString();
                                listItems.Add(attribute);
                            }
                        }
                        if (xRead.Name == "CurrentConnection")
                        {
                            xRead.Read(); //gets the value of <CurrentConnection>
                            currentConnection = xRead.Value.Trim();
                        }
                        totalElementsRead  ;
                        _populateListItems.ReportProgress(totalElementsRead / totalElementsToRead * 100);
                    }
                }
            }
        }
        catch
        {
            _populateListItems.CancelAsync();
        }
    }
  

Простите теорию в комментариях к ней. Я объясняю это как можно лучше.

Мой вопрос в том, может ли кто-нибудь увидеть, возможно, где это пошло не так? Почему событие внезапно не запускается? Предполагается, что он заполняет список элементами из моего XML-файла (нетронутый, работал до переименования). Запуск пошаговой отладки также доказал, что он пропускает мой обработчик событий DoWork.

Комментарии:

1. Действительно ли этот вопрос настолько сложен?o

2. Он начал работать, когда я перезапустил свой компьютер, по какой-то причине он говорит, что мой корневой элемент в файле xml отсутствует. ничего страшного

3. XmlDocument xdoc = новый XmlDocument(); xdoc.Load(settingstream); Узлы XmlNodeList = xdoc. SelectNodes(«*»); Эти строки должны быть удалены. Проблема устранена.

4. Это проблема, когда вы поглощаете исключения, как вы это делали. Если вы удалите try..catch , то Error свойство the RunWorkerCompletedEventArgs будет заполнено соответствующим исключением.

Ответ №1:

Я думаю, проблема в том, что вы вызываете RunWorkerAsync из конструктора, и ProgressChanged сбой происходит из-за того, что форма еще не видна. Попробуйте переместить вызов RunWorkerAsync в обработчик Show событий формы.

Хорошо, итак, проблема заключалась в исключении внутри обработчика DoWork событий, которое поглощалось try..catch блоком.

Подводя итог проблемам с вашим кодом:

  • try..catch блок, который поглощает все исключения и затрудняет отладку.
  • Вызов RunWorkerAsync изнутри конструктора формы.
  • Доступ к объектам потока пользовательского интерфейса из рабочего потока без надлежащей синхронизации / блокировки.
  • Вызов CancelAsync изнутри обработчика DoWork событий.

Комментарии:

1. Впереди вас: P на самом деле меня укусили. Список объектов. Add(атрибут); добавляет эти элементы в мой список: private volatile List<string> ListItems = new List<string>(); что касается других изменений, стоит попробовать

2. Не повезло. Я прокомментировал все в progresschanged и добавил _populateListItems . RunWorkerAsync(); к показанному обработчику событий формы. он немедленно вызывает обработчик событий runworkercompleted.

3. вы переместили RunWorkerAsync вызов из конструктора формы? Кроме того, вы не должны вызывать CancelAsync изнутри DoWork обработчика событий.

4. теперь все улажено. ваш совет сработал, хотя я сделал это до того, как вы его предложили. cancelasync был удален, и, вы будете ненавидеть меня за это, runworkerasync все еще находится в конструкторе (переместил его обратно). Ошибка возникла, когда я прочитал XML-файл с XmlDocument xdoc = new XmlDocument(); xdoc.Load(settingstream); Узлы XmlNodeList = xdoc. SelectNodes(«*»); , он переместил указатель в потоке в конец, в результате чего приложение не смогло найти корневой элемент. Он выдал исключение (которое было скрыто с помощью try ..catch), теперь все сделано и исправлено. спасибо за вашу поддержку, приятель.

5. Хорошо, что мой совет помог. Причина, по которой мне не нравится вызов RunWorkerAsync изнутри конструктора, заключается в том, что он может иметь побочные эффекты, которые требуют, чтобы форма была видимой или полностью сконструированной, что не гарантируется.