2011-12-27 2 views
-1

Я работаю над следующей программой, которая касается потоков и приоритетов потоков.C# Null Reference Exception

Я создал форму окна, содержащую богатое текстовое поле (DisplayTextBox) и две кнопки (StartButton для запуска приложения и ExitButton для закрытия приложения).

В форме я создаю несколько потоков и запускаю их один за другим. Метод, используемый каждым потоком, находится в классе Threading. Этот метод относится к PrintOnScreen().

В этом методе я добавляю имя потока, а также его приоритет для str типа StringBuilder. Затем я хочу отобразить содержимое str в DisplayTextBox (которое находится в Form.cs).

Однако, я получаю сообщение об ошибке «Ошибка NullReferenceException: необработанный объект: объект не установлен в экземпляр объекта». Линия, где возникает ошибка, следующая:

DisplayTextBox.Text = Convert.ToString (str);

Не могли бы вы помочь мне решить эту ошибку? Благодарю.

Редактировать

Спасибо всем за вашу помощь. Чтобы решить проблему, я скопировал метод PrintOnScreen в класс Form.cs и отменил Threading.cs.

Я позже использовал код, предоставленный Анандом, и разместил его ниже t2.Join(). Теперь это работает как шарм.

+0

Знаете ли вы, какой объект является нулевым? Есть только два возможных ответа на этот вопрос: первым будет ** DisplayTextBox **, второй будет ** str **, конечно, я не вижу, где вы передаете ссылку на DisplayTextBox в форме. Threading наследует Form1, что не означает, что он имеет ссылку на элементы управления Form1. –

+0

Даже если вы исправите свой код и передадите ссылку на ** DisplayTextBox **, вам придется вызывать изменение текста, так как вы не можете изменять элементы управления в отдельном потоке, отличном от основного потока пользовательского интерфейса. –

+0

Вы должны действительно учитывать синхронизацию доступа к данным, к которым вы обращаетесь из нескольких потоков. Например, вы пишете оба потока в одном экземпляре StringBuilder и итерации коллекции Threads. – Jan

ответ

3

Проблема возникает в конструкторе вашей формы. Вы снова объявляете DisplayText как локальный элемент, так что поле вашей формы не инициализируется. Измените свой конструктор следующим образом:

private void Form1_Load(object sender, EventArgs e) 
{ 
    DescTextBox.Visible = false; 
    DisplayTextBox = new RichTextBox(); 
    DisplayTextBox.Location = new Point(15, 31); 
    DisplayTextBox.Height = 258; 
    DisplayTextBox.Width = 303; 
    panel1.Controls.Add(DisplayTextBox); 
} 

и он должен работать так, как вы ожидаете.

Редактировать: Но остерегайтесь проблем, которые вы получите, когда хотите изменить элементы пользовательского интерфейса из рабочего потока работника. В этих случаях вам придется использовать шаблон вызова.

1

Вы не можете связываться с элементами управления из любой другой темы, кроме темы основного приложения. Для этого вам нужно использовать диспетчер. Посмотрите здесь: http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.aspx

EDIT
@Fischermaen - Хех, не заметил этого, но он все равно не будет работать из-за линии, где он писал, что не удается. Он по-прежнему необходимо использовать диспетчер установить свойство Text на контроле от неосновного нитей

EDIT 2
Связанных темы:
How to update the GUI from another thread in C#?
In WinForms, why can't you update UI controls from other threads?

+0

Спасибо за предложения относительно диспетчера. Я попробую и дам вам знать, работает ли это. Спасибо всем :) – Joe

1

Я попробовал это в простом консольном приложении и он работал нормально. Проблема с DisplayTextBox. Если вы хотите взаимодействовать с любым элементом управления, вам нужно использовать поток пользовательского интерфейса или диспетчер. Используйте следующий код в классе Threading, вместо

DisplayTextBox.Text = Convert.ToString(str); 

использовать это один

Dispatcher.Invoke(DispatcherPriority.Normal, 
        new Action(
      delegate() 
      { 
       DisplayTextBox.Text = Convert.ToString(str); 
      } 
     )); 
+0

Точно. В консольном приложении он работает нормально. Проблемы, которые он мне дает, - это когда я пытаюсь преобразовать его в приложение Windows. – Joe

+0

Попробуйте приведенный выше код. Он должен работать нормально. – Anand

+0

Я вставил код и дал мне ошибку - имя «Диспетчер» не существует в текущем контексте. – Joe

0

Um. Не вижу здесь, но часть причины, по которой вы испытываете трудности, - это отсутствие инкапсуляции и плохого управления жизненным циклом в вашем коде.

Проиллюстрируйте str внутри метода PrintOnScreen, дайте ему лучшее имя, а appranrent не должен быть переменной-членом, не нужно вообще публиковать его.

Вместо того, чтобы иметь пустот результата, которые он возвращает строку результата

например,

SomeTextBox = PrintOnScreen(); // (GetThreadDetails might be a better name...) 

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

И не делают

public SomeType SomeName; 

сделать это свойство и дать ему добытчика и сеттер, даже если это короткая форма

общественного SomeType SomeName {получить; набор;}

Любой другой фрагмент кода может сделать что-то действительно глупое, как

Form1.SomeType = (SomeOtherType)someVar; // with appaling consequences. 

Ничто должно иметь более одного владельца, любой другой путь ведет к гибели.

PS str.ToString() был бы лучшим вариантом, чем Convert.ToString (str);

0

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

Я думаю, что вы пытаетесь решить проблему с доступом к потоку, поднятую SOReader. Это неправильный способ сделать это.

Смежные вопросы