Ошибка компиляции C #:“Вызов Invoke или BeginInvoke не может быть вызван для элемента управления до тех пор, пока не будет создан дескриптор окна”.

StackOverflow https://stackoverflow.com/questions/513131

Вопрос

Я только что опубликовал вопрос о том, как заставить делегата обновлять текстовое поле в другой форме.Как раз тогда, когда я думал, что у меня есть ответ, используя 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 широко только для того, чтобы найти расплывчатые ответы.

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

Спасибо!!!

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

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

Журнал регистрации это мой richTextBox1 (Журнал.Товары.Добавить (сообщение)) Я переименовал его в Log, чтобы его было проще перепечатать.

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

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

ВТОРАЯ ПРАВКА:

Я полагаю, что обнаружил проблему, но не знаю, как ее устранить.

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

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

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

Когда я вызываю updateLog (сообщение) Я полагаю, что пытаюсь обновить журнал (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);
        }
    }
}
Это было полезно?

Решение

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

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

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

Общий способ запуска приложения - вызвать Application .Run(new Main()) в вашем методе запуска, который обычно находится в классе Program и называется 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);
    }
}

Другие советы

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

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

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

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

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

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

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

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

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

Итак, для начала, я бы избавился от вызова logaddelegate в методе 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);
    }
 }

}

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

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

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

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

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

внутри вашей функции журнала вместо того, что у вас есть в данный момент.Разница в том, что вызов приложения.OpenForms.OfType().First войдет в ваше приложение и получит ФАКТИЧЕСКУЮ основную форму, которую вы видите (технически, он получит первый ее экземпляр) и выполнит ваш вызов непосредственно в этой форме.

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

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

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

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

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

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

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

if (!this.IsHandleCreated)
{
    this.CreateHandle();
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top