Ошибка компиляции C #: «Invoke или BeginInvoke не могут быть вызваны для элемента управления, пока не будет создан дескриптор окна». - PullRequest
8 голосов
/ 04 февраля 2009

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

Основной код формы:

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();
    }

}

Код класса регистрации:

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);
        }
    }
}
  • Ошибка компиляции:

    Исключение InvalidOperationException было необработанный - Invoke или BeginInvoke не может быть вызван на элемент управления до дескриптор окна создан.

Я уже пытался создать дескриптор для элемента журнала ... но это не сработало. Проблема в том, что я не знаю, что я делаю, и я искал в Google * , только чтобы найти расплывчатые ответы.

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

Спасибо !!!

EDIT:

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

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

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

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

ВТОРОЕ РЕДАКТИРОВАНИЕ:

Мне кажется, я обнаружил проблему, но не знаю, как ее исправить.

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

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

Я создаю совершенно новую копию проекта для Main (), включая Log (richtextbox, который я пытаюсь обновить)

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

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

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

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

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

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

Окончательное редактирование:

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

Основной код формы:

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();
    }

}

Код класса регистрации:

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);
        }
    }
}

Ответы [ 9 ]

12 голосов
/ 04 февраля 2009

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

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

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

12 голосов
/ 05 февраля 2009

Хорошо, я собираюсь начать снова.

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

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

Общий способ запуска приложения - вызвать Application.Run (new 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);
    }
}
2 голосов
/ 05 февраля 2009

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

Ваша форма, "Main", должна быть отображена (следовательно, дескриптор окна создан), прежде чем вы сможете вызывать 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);
    }
 }

}

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

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

2 голосов
/ 04 февраля 2009

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

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

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

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

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

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

1 голос
/ 05 февраля 2009

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

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

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

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

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

0 голосов
/ 15 июня 2012

Я нашел InvokeRequired ненадежным, поэтому я просто использую

if (!this.IsHandleCreated)
{
    this.CreateHandle();
}
0 голосов
/ 04 февраля 2009

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

0 голосов
/ 04 февраля 2009

logAddDelegate (сообщение);

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

...