2009-02-04 6 views
8

Я только что разместил вопрос о том, как получить делегат для обновления текстового поля в другой форме. Просто, когда я подумал, что у меня есть ответ, используя Invoke ... это происходит. Вот мой код:Ошибка компиляции C#: «Invoke или BeginInvoke нельзя вызвать в элементе управления до тех пор, пока не будет создан дескриптор окна».

Главная форма Код:

using System; 
using System.Drawing; 
using System.Collections; 
using System.ComponentModel; 
using System.Windows.Forms; 
using System.Data; 
using System.IO; 
using System.Data.OleDb; 
using System.Collections.Specialized; 
using System.Text; 
using System.Threading; 

delegate void logAdd(string message); 

namespace LCR_ShepherdStaffupdater_1._0 
{ 
    public partial class Main : Form 
    { 
     public Main() 
     { 
      InitializeComponent(); 
     } 

     public void add(string message) 
     { 
      this.Log.Items.Add(message); 
     } 
     public void logAdd(string message) 
     { /////////////////////////// COMPILER ERROR BELOW /////////// 
      this.Invoke(new logAdd(add), new object[] { message }); // Compile error occurs here  
     }////////////////////////////// COMPILER ERROR ABOVE /////////// 

     private void exitProgramToolStripMenuItem_Click(object sender, EventArgs e) 
     { 
      Application.Exit(); 
     } 
     private void aboutToolStripMenuItem1_Click(object sender, EventArgs e) 
     { 
      Form aboutBox = new AboutBox1(); 
      aboutBox.ShowDialog(); 
     } 

     private void settingsToolStripMenuItem_Click(object sender, EventArgs e) 
     { 
     } 

     private void settingsToolStripMenuItem1_Click(object sender, EventArgs e) 
     { 
      settingsForm.settings.ShowDialog(); 
     } 

     private void synchronize_Click(object sender, EventArgs e) 
     { 
      string message = "Here my message is"; // changed this 
      ErrorLogging.updateLog(message); // changed this 
     } 

    } 

    public class settingsForm 
    { 
     public static Form settings = new Settings(); 
    } 

} 

Logging Код класса:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace LCR_ShepherdStaffupdater_1._0 
{ 
    public class Logging 
    { 
     static Main mainClass = new Main(); 
     static logAdd logAddDelegate; 

     public static void updateLog(string message) 
     { 
      logAddDelegate = mainClass.logAdd; 
      logAddDelegate(message); 
     } 
    } 
} 
  • Compile Error:

    InvalidOperationException was unhandled - Invoke или BeginInvoke нельзя вызвать на элемент управления до . Создан дескриптор окна.

Я уже пытался создать ручку на пункт Log ... но это не сработало. Проблема в том, что у меня нет НИКАКОГО CLUE, что я делаю, и я искал Google экстенсивно только для того, чтобы найти смутные ответы.

Скажите, пожалуйста, как создать дескриптор, прежде чем я вызову этот делегат. Пока вы на нем, дайте мне несколько способов, чтобы я мог сделать этот код более простым. Например, мне не нужны две функции Add ... Мне пришлось это сделать, потому что мне не удалось найти элемент для вызова из класса Logging. Есть ли лучший способ выполнить то, что мне нужно сделать?

Спасибо!

EDIT:

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

Журнал - это мой RichTextBox1 (Log.Items.Add (сообщение)) Я переименовал его в журнал, чтобы его легче было перепечатать.

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

Вы, ребята, должны будете сделать вещи более упрощенными для меня ... и привести примеры. Я не понимаю ПОЛОВИНУ всего, что вы, ребята, говорите здесь ... Я не знаю, как работать с Invoke of methods и Handles. Я исследовал дерьмо из нее тоже ...

ВТОРОЙ EDIT:

Я считаю, что я находится проблема, но не знаю, как это исправить.

В моем классе лесозаготовительного я использую этот код для создания MainClass:

статического Main MainClass = новый Main();

Я создаю совершенно новый проект реплику Main(), в том числе Вход (RichTextBox Я пытаюсь обновить)

Когда я называю updateLog (сообщение) Я считаю, что я пытаюсь обновить Log (richtextbox) для второго объекта Main(), иначе известного как mainClass. Конечно, это сделает мне это исключение, потому что я даже не видел эту копию текущей Main, которую я использую.

Это то, что я снимаюсь для, благодаря одному из людей, которые дали ответ:

Main mainClass = Application.OpenForms.OfType<Main>().First(); 
logAddDelegate = mainClass.logAdd; 
logAddDelegate(message); 

Мне нужно создать MainClass не с новым() оператора, потому что я не хочу, чтобы создать новый чертеж формы, я хочу, чтобы иметь возможность редактировать текущую форму.

Приведенный выше код не работает, я даже не могу найти приложение. Это даже синтаксис C#?

Если я могу заставить приведенный выше код работать, я думаю, что смогу решить мою проблему и, наконец, положить эту проблему на отдых после пары ЧАСОВ поиска ответов.

FINAL EDIT:

Я понял это благодаря одной из следующих пользователей. Вот мой обновленный код:

Главная форма Код:

using System; 
using System.Drawing; 
using System.Collections; 
using System.ComponentModel; 
using System.Windows.Forms; 
using System.Data; 
using System.IO; 
using System.Data.OleDb; 
using System.Collections.Specialized; 
using System.Text; 
using System.Threading; 

delegate void logAdd(string message); 

namespace LCR_ShepherdStaffupdater_1._0 
{ 
    public partial class Main : Form 
    { 
     private static Main mainFormForLogging; 
     public static Main MainFormForLogging 
     { 
      get 
      { 
       return mainFormForLogging; 
      } 
     } 

     public Main() 
     { 
      InitializeComponent(); 
      if (mainFormForLogging == null) 
      { 
       mainFormForLogging = this; 
      } 
     } 

     public void add(string message) 
     { 
      this.Log.Items.Add(message); 
     } 
     public void logAdd(string message) 
     { 
      this.Log.BeginInvoke(new logAdd(add), new object[] { message }); 
     } 

     private void exitProgramToolStripMenuItem_Click(object sender, EventArgs e) 
     { 
      Application.Exit(); 
     } 
     private void aboutToolStripMenuItem1_Click(object sender, EventArgs e) 
     { 
      Form aboutBox = new AboutBox1(); 
      aboutBox.ShowDialog(); 
     } 

     private void settingsToolStripMenuItem_Click(object sender, EventArgs e) 
     { 
     } 

     private void settingsToolStripMenuItem1_Click(object sender, EventArgs e) 
     { 
      settingsForm.settings.ShowDialog(); 
     } 

     private void synchronize_Click(object sender, EventArgs e) 
     { 
      add("test"); 
      Logging.updateLog("testthisone"); 
      //DatabaseHandling.createDataSet(); 
     } 

    } 

    public class settingsForm 
    { 
     public static Form settings = new Settings(); 
    } 

} 

Logging Код класса:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace LCR_ShepherdStaffupdater_1._0 
{ 
    public class Logging 
    { 

     static Main mainClass = Main.MainFormForLogging; 
     static logAdd logAddDelegate; 

     public static void updateLog(string message) 
     { 
      logAddDelegate = mainClass.logAdd; 
      logAddDelegate(message); 
     } 
    } 
} 
+3

Вы абсолютно уверены, что это ошибка компилятора? Это звучит намного больше, как исключение времени выполнения для меня ... –

+0

Вы уверены, что mainClass - это тот же экземпляр Main, на который вы смотрите? Возможно ли, что у вас есть два экземпляра - один создан, а другой нет? –

+0

Ребята ... Я не уверен, что ... эта часть меня так запутала. Я почти не знаю, что происходит дальше. Lol – OneShot

ответ

12

Правильно, я собираюсь начать все заново.

Чтобы понять, что происходит, вам нужно понять, как .NET и Windows связаны друг с другом. .NET работает в Windows и обертывает многие из родных концепций Win32, таких как окно, listview, editbox (имя Win32 для стандартного текстового поля). Это означает, что вы можете иметь действительный экземпляр .NET TextBox или Form, но не иметь базовую версию Windows этого элемента (EditBox или Window). Когда HandleCreated истинно, создается версия Windows.

Ваша проблема возникает из-за того, что что-то приводит к вызову метода logAdd перед созданием окна формы. Это означает, что где-то во время запуска после создания экземпляра формы, но до того, как дескриптор окна был создан, что-то пытается вызвать logAdd. Если вы добавите точку останова в logAdd, вы сможете увидеть, что делает этот вызов. Вы обнаружите, что вызов выполняется в основном экземпляре, который вы создаете в своем классе logger, а НЕ в основном исполняемом экземпляре. Поскольку экземпляр журнала не отображается, дескриптор окна не создается, и поэтому вы получаете свою ошибку.

Общий способ запуска приложения - вызвать Application.Run (новый Main()) в вашем методе запуска, который обычно находится в классе Program и называется Main. Вам нужен ваш регистратор, чтобы указать на этот экземпляр main.

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

public partial class Main : Form 
{ 
    private static Main mainFormForLogging; 
    public static Main MainFormForLogging 
    { 
     get 
     { 
      return mainFormForLogging; 
     } 
    } 

    public Main() 
    { 
     InitializeComponent(); 

     if (mainFormForLogging == null) 
     { 
      mainFormForLogging = this; 
     } 
    } 

    protected void Dispose(bool disposing) 
    { 
     if (disposing) 
     { 
      if (this == mainFormForLogging) 
      { 
       mainFormForLogging = null; 
      } 
     } 

     base.Dispose(disposing); 
    } 
} 
+0

Итак, вы говорите мне: В моем классе ведения журнала тот факт, что я создаю НОВЫЙ объект Main, не имеет к этому никакого отношения? Я просто говорю, я считаю, что я пытаюсь редактировать TextBox, который находится на Main mainClass = new Main(); Я не вижу новый Main, поэтому TextBox на нем никогда не загружается. – OneShot

+0

Нет, ваша новая Майн имеет все, что с ней связано. См. Пример кода, который я только что добавил, как вы можете это решить. –

+0

Я, наконец, сделал это. Большое вам спасибо, ты мой герой. – OneShot

2

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

В WinForms элементы GUI имеют два полунезависимых ресурса: как классы в памяти и как сущности в операционной системе. Таким образом, можно ссылаться на элемент управления в .net, который еще не создан. «Создаваемый дескриптор» относится к тому, что ОС назначает номер, назначаемый ОС, чтобы позволить программам управлять своими свойствами.

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

+0

Но мое текстовое поле создано! Я вижу это, прежде чем нажимать кнопку. Если он не создан ... как мне его создать? Причина У меня нет подсказки ... – OneShot

+1

Вы не вызываете Invoke на свой TextBox, вы вызываете его в форме. Если это вызвано до того, как форма создана, или ПОСЛЕ того, что она была уничтожена, вы получите эту ошибку. –

+0

Я пытаюсь добавить что-то в TextBox, который существует во второй версии main, потому что я делаю «Main myClass = new Main()» Мне нужно выяснить, как создать myClass, не создавая новый проект Main (). – OneShot

0

logAddDelegate(message);

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

+0

Как он не может быть загружен ... прежде чем нажимать кнопку, я смотрю прямо на TextBox. – OneShot

12

Я решил это в прошлом, используя следующий метод:

private void invokeOnFormThread(MethodInvoker method) 
{ 
    if (IsHandleCreated) 
     Invoke(new EventHandler(delegate { method(); })); 
    else 
     method(); 
} 

вызов invokeOnFormThread вместо Invoke. Он будет использовать поток формы только в том случае, если дескриптор уже создан, иначе он будет использовать поток вызывающего.

+0

Я вижу, могу ли я преобразовать это в свой код ... im не совсем уверен, что все это делает и почему я должен это делать. Пока я не могу понять, как это использовать. – OneShot

+0

Это умно. Он позволяет проводить логические тесты без формы. –

+0

Это стандартная идиома для работы с Invoke. это самый правильный ответ. – Celess

0

Это ваш точный код? Вы вызываете this.Log.Items.Add(message); в свой метод add (string), но ваш класс ведения журнала называется Logging, а не Log. Возможно, у вас есть другая форма под названием «Журнал»? Если эта форма не была создана при вызове метода add, вы получите это исключение.

2

Это ошибка времени выполнения, а не ошибка компилятора.

Ваша форма, «Главная», должна отображаться (следовательно, созданный дескриптор окна), прежде чем вы сможете совершать вызовы BeginInvoke или Invoke на нем.

Что я обычно делаю в этих ситуациях, оставьте его в форме, чтобы определить, нужно ли ему использовать вызов BeginInvoke или Invoke. Вы можете проверить это с помощью вызова InvokeRequired (проверьте MSDN).

Итак, для начала я бы избавился от вызова logAddDelegate в методе updateLog класса Loggin. Просто позвоните в форму, чтобы добавить журнал. Как так:

public partial class Main : Form 
{ 
    public Main() 
    { 
     InitializeComponent(); 
    } 

    private delegate void AddNewLogMessageEventHandler(string message); 

    public void AddLogMessage(string message) 
    { 
     object[] args = new object[1]; 
     args[0] = message; 

     if (InvokeRequired) 
      BeginInvoke(new AddNewLogMessageEventHandler(AddLog), args); 
     else 
      Invoke(new AddNewLogMessageEventHandler(AddLog), args); 
    } 

    private void AddLog(string message) 
    { 
     this.Log.Items.Add(message); 
    } 
} 

}

Таким образом, вы можете видеть, сама форма отвечает за определение, если это необходимо вызвать метод асинхронно или нет.

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

+0

Спасибо! Да, я знаю, что это не исправить ... как простой RichTextBox1 (Log) не будет действительной или загруженной формой? – OneShot

+0

Это не текстовое поле, которое недействительно. Это форма. Форма должна иметь свою ручку, созданную до того, как вы вызовете ее. Ручка ТОЛЬКО создается, когда отображается форма. Попробуйте некоторые Console.WriteLines и посмотрите, можете ли вы определить, когда/когда вызовы происходят и в каком порядке. –

+0

Я узнал, что в моем классе Logging я создаю новый экземпляр моего MainClass ... вот почему он еще не создан. Теперь я должен выяснить, как вместо создания нового экземпляра MainClass в моем loggingclass, чтобы создать новый экземпляр того же класса. – OneShot

1

Эта ошибка имеет тенденцию возникать, если вы вызываете окно, которое еще не было «показано». Вы уверены, что не создаете второй экземпляр основного класса с кодом в классе Logging (в частности, в первой строке)? Возможно, основная форма, с которой вы звоните, не является основной формой, на которую вы смотрите. Если вы хотите проверить, добавьте вызов к «MainClass.Show()» только внутри вашего вызова регистрации. Если вы получаете вторую копию основной формы, то проблема заключается в том, что ваш класс ведения журнала не ссылается на правильный «экземпляр» вашей формы.

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

MainClass myMainForm = Application.OpenForms.OfType<MainClass>().First(); 
logAddDelegate = myMainForm.logAdd; 
logAddDelegate(message); 

внутри вашей функции регистрации, а не того, что у вас есть в настоящее время. Разница заключается в том, что вызов Application.OpenForms.OfType(). Сначала зайдите в ваше приложение и получите основную форму ACTUAL, которую вы видите (технически, она будет извлекать первый экземпляр) и сделать ваш вызов на этом формы, напрямую.

Надеюсь, что это поможет.

+0

Боже мой Я думаю, что это может сделать это, но я пока не получил его на работу. Я не могу найти Application.OpenForms ... где это? – OneShot

+0

System.Windows.Forms.Application.OpenForms – GWLlosa

+0

http://stackoverflow.com/questions/513131/c-compile-error-invoke-or-begininvoke-cannot-be-called-on-a-control-until-the -w/513529 # 513529 Он сказал это лучше, чем я. – GWLlosa

1

Это поможет в случае, если кто-либо другой поймает это. Моя проблема: VB.net: «Invoke или BeginInvoke не может быть вызван в элемент управления до тех пор, пока не будет создан дескриптор окна». Я закрыл форму, в которой есть обработчик для вызова события, чтобы обновить делегат, не удаляя обработчик события.

Что я сделал: Когда я закрыл форму, я удалил все обработчики и назначил их обратно, когда я открыл форму. Он решил проблему.

0

Я нашел InvokeRequired не надежен, так что я просто использовать

if (!this.IsHandleCreated) 
{ 
    this.CreateHandle(); 
}