ฉันจะรันโค้ดหลังจากที่โหลดแบบฟอร์มได้อย่างไร


127

ใน. NET Windows Forms มีเหตุการณ์ที่เริ่มทำงานก่อนที่ฟอร์มจะถูกโหลด (Form.Load) แต่ไม่มีเหตุการณ์ที่เกี่ยวข้องที่จะเริ่มทำงานหลังจากที่ฟอร์มโหลด ฉันต้องการเรียกใช้ตรรกะบางอย่างหลังจากโหลดแบบฟอร์มแล้ว

ใครช่วยแนะนำวิธีแก้ปัญหาได้ไหม


แม้ว่าคำถามนี้จะมีคำตอบที่ดีมาก แต่ก็ควรที่จะพูดถึงสิ่งนี้: docs.microsoft.com/en-us/dotnet/framework/winforms/…
Rishav

คำตอบ:


193

คุณสามารถใช้เหตุการณ์ "Shown": MSDN - Form.Shown

"เหตุการณ์ที่แสดงจะถูกยกขึ้นในครั้งแรกที่มีการแสดงแบบฟอร์มเท่านั้นการย่อขนาดการขยายใหญ่การคืนค่าการซ่อนการแสดงหรือการทำให้ไม่ถูกต้องและการทาสีใหม่จะไม่ทำให้เหตุการณ์นี้เกิดขึ้น"


10
สำหรับฉันดูเหมือนว่าตัวจัดการที่แสดงจะถูกเรียกใช้ในขณะที่ฟอร์มกำลังโหลด ... ฉันผิดหรือเปล่า?
ckonig

3
เก่า แต่ทอง ... ใช่คุณคิดผิด GUI ไม่สามารถรันงานคู่ขนานสิ่งที่สำคัญในการทำบางสิ่งในขณะที่การดำเนินการอื่นเสร็จสิ้น
Dennis Ziolkowski

2
หากใน Load event handler มีรหัสที่เรียกใช้ Application.DoEvents () เหตุการณ์ที่แสดงจะเริ่มทำงานก่อนที่ตัวจัดการเหตุการณ์ Load จะดำเนินการเสร็จสิ้น เนื่องจากเหตุการณ์ที่แสดงอยู่ในคิวข้อความโดยใช้ FormBeginInvoke (ShownEvent) และ DoEvents () บังคับให้เริ่มทำงานก่อนที่ Load จะเสร็จสิ้น
Artemix

1
มันไม่เพียงพอสำหรับฉันใน C # ฉันต้องเพิ่มShown += Form1_Shown;ตามที่แนะนำในเธรดอื่น
ocramot

11
คุณควรเพิ่ม This.Refresh (); ภายในเหตุการณ์ Shown ก่อนตรรกะของคุณและมันจะเก็บและรีเฟรชแบบฟอร์มเพื่อโหลดเต็มก่อนที่ตรรกะของคุณจะเริ่มทำงาน
Aylian Craspa

49

บางครั้งฉันใช้ (ในโหลด)

this.BeginInvoke((MethodInvoker) delegate {
  // some code
});

หรือ

this.BeginInvoke((MethodInvoker) this.SomeMethod);

(เปลี่ยน "this" เป็นตัวแปรรูปแบบของคุณหากคุณจัดการเหตุการณ์บนอินสแตนซ์อื่นที่ไม่ใช่ "this")

สิ่งนี้จะผลักการเรียกไปยังลูปแบบฟอร์ม windows ดังนั้นจึงได้รับการประมวลผลเมื่อฟอร์มกำลังประมวลผลคิวข้อความ

[อัปเดตตามคำขอ]

เมธอด Control.Invoke / Control BeginInvoke มีไว้สำหรับใช้กับเธรดและเป็นกลไกในการพุชงานไปยังเธรด UI โดยปกติจะใช้โดยเธรดของผู้ปฏิบัติงานเป็นต้น ControlInvoke ทำการโทรแบบซิงโครนัสโดยที่ - เป็น ControlBeginInvoke ทำการโทรแบบอะซิงโครนัส

โดยปกติสิ่งเหล่านี้จะใช้เป็น:

SomeCodeOrEventHandlerOnAWorkerThread()
{
  // this code running on a worker thread...
  string newText = ExpensiveMethod(); // perhaps a DB/web call

  // now ask the UI thread to update itself
  this.Invoke((MethodInvoker) delegate {
      // this code runs on the UI thread!
      this.Text = newText;
  });
}

ทำได้โดยการส่งข้อความไปยังคิวข้อความของ windows เธรด UI (ในบางจุด) ยกเลิกการจัดคิวข้อความประมวลผลผู้รับมอบสิทธิ์และส่งสัญญาณให้ผู้ปฏิบัติงานทราบว่าเสร็จสิ้น ... จนถึงขณะนี้ดีมาก ;-p

ตกลง; แล้วจะเกิดอะไรขึ้นถ้าเราใช้ Control.Invoke / ControlBeginInvoke บนเธรด UI มัน copes ... ถ้าคุณเรียก ControlInvoke มันสมเหตุสมผลมากพอที่จะรู้ว่าการบล็อกคิวข้อความจะทำให้เกิดการชะงักงันทันทีดังนั้นหากคุณอยู่ในเธรด UI อยู่แล้วมันก็รันโค้ดทันที ... ไม่ช่วยเรา ...

แต่ ControlBeginInvoke ทำงานแตกต่างกัน: มันจะผลักดันงานไปยังคิวเสมอแม้ว่าเราจะอยู่ในเธรด UI แล้วก็ตาม นี่เป็นวิธีง่ายๆในการพูดว่า "อีกสักครู่" แต่หากไม่มีตัวจับเวลาที่ไม่สะดวก ฯลฯ (ซึ่งก็ยังคงต้องทำแบบเดิมอยู่ดี!)


1
ไม่เข้าใจอย่างสมบูรณ์ ช่วยอธิบายอีกนิดได้ไหม
Torbjørn

สวัสดีเครื่องหมายเป็นไปได้ไหมที่จะทำให้แบบฟอร์มตอบสนองในขณะที่กระบวนการเสร็จสมบูรณ์ซึ่งเรียกว่าใน BeginInvoke?
huMpty duMpty

WPF เทียบเท่าอะไร
mrid

6

ครั้งแรกจะไม่เริ่ม "AfterLoading"
เพียงแค่ลงทะเบียนเพื่อเริ่มการโหลดครั้งต่อไป

private void Main_Load(object sender, System.EventArgs e)
{
    //Register it to Start in Load 
    //Starting from the Next time.
    this.Activated += AfterLoading;
}

private void AfterLoading(object sender, EventArgs e)
{
    this.Activated -= AfterLoading;
    //Write your code here.
}

5

ฉันมีปัญหาเดียวกันและแก้ไขได้ดังนี้:

อันที่จริงฉันต้องการแสดงข้อความและปิดโดยอัตโนมัติหลังจากผ่านไป 2 วินาที สำหรับสิ่งนั้นฉันต้องสร้างรูปแบบง่ายๆ (แบบไดนามิก) และหนึ่งป้ายกำกับที่แสดงข้อความหยุดข้อความเป็นเวลา 1,500 มิลลิวินาทีเพื่อให้ผู้ใช้อ่าน และปิดแบบฟอร์มที่สร้างขึ้นแบบไดนามิก เหตุการณ์ที่แสดงเกิดขึ้นหลังจากเหตุการณ์โหลด ดังนั้นรหัสคือ

Form MessageForm = new Form();
MessageForm.Shown += (s, e1) => { 
    Thread t = new Thread(() => Thread.Sleep(1500)); 
    t.Start(); 
    t.Join(); 
    MessageForm.Close(); 
};

2

คุณยังสามารถลองใส่รหัสของคุณในเหตุการณ์ที่เปิดใช้งานของแบบฟอร์มหากคุณต้องการให้เกิดขึ้นเมื่อเปิดใช้งานแบบฟอร์ม คุณจะต้องใส่บูลีน "ได้ดำเนินการ" ตรวจสอบว่าควรจะรันเมื่อเปิดใช้งานครั้งแรกเท่านั้น


1

คำถามนี้เป็นคำถามเก่าและขึ้นอยู่กับว่าคุณต้องเริ่มกิจวัตรเมื่อใด เนื่องจากไม่มีใครต้องการข้อยกเว้นการอ้างอิงที่เป็นโมฆะจึงควรตรวจสอบค่าว่างก่อนจากนั้นจึงใช้ตามความจำเป็น เพียงอย่างเดียวอาจช่วยให้คุณหายเศร้าได้มาก

สาเหตุที่พบบ่อยที่สุดสำหรับคำถามประเภทนี้คือเมื่อคอนเทนเนอร์หรือชนิดการควบคุมแบบกำหนดเองพยายามเข้าถึงคุณสมบัติที่กำหนดค่าเริ่มต้นภายนอกคลาสที่กำหนดเองซึ่งคุณสมบัติเหล่านั้นยังไม่ได้รับการเตรียมใช้งานซึ่งอาจทำให้เกิดค่า null เพื่อเติมข้อมูลและอาจทำให้เกิดข้อยกเว้นการอ้างอิงว่าง ในประเภทวัตถุ หมายความว่าชั้นเรียนของคุณกำลังทำงานก่อนที่จะเริ่มต้นอย่างสมบูรณ์ - ก่อนที่คุณจะตั้งค่าคุณสมบัติของคุณเสร็จเป็นต้นอีกเหตุผลที่เป็นไปได้สำหรับคำถามประเภทนี้คือเมื่อใดที่จะต้องแสดงกราฟิกแบบกำหนดเอง

เพื่อให้ตอบคำถามได้ดีที่สุดเกี่ยวกับเวลาที่จะเริ่มเรียกใช้โค้ดหลังจากเหตุการณ์โหลดแบบฟอร์มคือการมอนิเตอร์ข้อความ WM_Paint หรือเชื่อมโยงโดยตรงกับเหตุการณ์การระบายสี ทำไม? เหตุการณ์สีจะเริ่มทำงานก็ต่อเมื่อโมดูลทั้งหมดโหลดเต็มตามเหตุการณ์การโหลดแบบฟอร์มของคุณ หมายเหตุ: This.visible == true ไม่เป็นจริงเสมอไปเมื่อตั้งค่าเป็นจริงดังนั้นจึงไม่ได้ใช้เพื่อจุดประสงค์นี้เลยยกเว้นเพื่อซ่อนฟอร์ม

ต่อไปนี้เป็นตัวอย่างที่สมบูรณ์ของวิธีเริ่มเรียกใช้โค้ดของคุณหลังจากเหตุการณ์โหลดแบบฟอร์ม ขอแนะนำว่าคุณอย่าผูกลูปข้อความระบายสีโดยไม่จำเป็นดังนั้นเราจะสร้างเหตุการณ์ที่จะเริ่มเรียกใช้โค้ดของคุณนอกลูปนั้น

using System.Windows.Forms;

เนมสเปซ MyProgramStartingPlaceExample {

/// <summary>
/// Main UI form object
/// </summary>
public class Form1 : Form
{

    /// <summary>
    /// Main form load event handler
    /// </summary>
    public Form1()
    {
        // Initialize ONLY. Setup your controls and form parameters here. Custom controls should wait for "FormReady" before starting up too.
        this.Text = "My Program title before form loaded";
        // Size need to see text. lol
        this.Width = 420;

        // Setup the sub or fucntion that will handle your "start up" routine
        this.StartUpEvent += StartUPRoutine;

        // Optional: Custom control simulation startup sequence:
        // Define your class or control in variable. ie. var MyControlClass new CustomControl;
        // Setup your parameters only. ie. CustomControl.size = new size(420, 966); Do not validate during initialization wait until "FormReady" is set to avoid possible null values etc. 
        // Inside your control or class have a property and assign it as bool FormReady - do not validate anything until it is true and you'll be good! 
    }

    /// <summary>
    /// The main entry point for the application which sets security permissions when set.
    /// </summary>
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }


    #region "WM_Paint event hooking with StartUpEvent"            
    //
    // Create a delegate for our "StartUpEvent"
    public delegate void StartUpHandler();
    //
    // Create our event handle "StartUpEvent"
    public event StartUpHandler StartUpEvent;
    //
    // Our FormReady will only be set once just he way we intendded
    // Since it is a global variable we can poll it else where as well to determine if we should begin code execution !!
    bool FormReady;
    //
    // The WM_Paint message handler: Used mostly to paint nice things to controls and screen
    protected override void OnPaint(PaintEventArgs e)
    {
        // Check if Form is ready for our code ?
        if (FormReady == false) // Place a break point here to see the initialized version of the title on the form window
        {
            // We only want this to occur once for our purpose here.
            FormReady = true;
            //
            // Fire the start up event which then will call our "StartUPRoutine" below.
            StartUpEvent();
        }
        //
        // Always call base methods unless overriding the entire fucntion
        base.OnPaint(e);
    }
    #endregion


    #region "Your StartUp event Entry point"
    //
    // Begin executuing your code here to validate properties etc. and to run your program. Enjoy!
    // Entry point is just following the very first WM_Paint message - an ideal starting place following form load
    void StartUPRoutine()
    {
        // Replace the initialized text with the following
        this.Text = "Your Code has executed after the form's load event";
        //
        // Anyway this is the momment when the form is fully loaded and ready to go - you can also use these methods for your classes to synchronize excecution using easy modifications yet here is a good starting point. 
        // Option: Set FormReady to your controls manulaly ie. CustomControl.FormReady = true; or subscribe to the StartUpEvent event inside your class and use that as your entry point for validating and unleashing its code.
        //
        // Many options: The rest is up to you!
    }
    #endregion

}

}


สิ่งนี้ดูเหมือนจะยืดเยื้ออย่างไม่น่าเชื่อและมีข้อได้เปรียบมากกว่าการจับภาพงาน Shown หรือไม่?
Steve Smith

0

ฉันรู้ว่านี่เป็นโพสต์เก่า แต่นี่คือวิธีที่ฉันได้ทำ:

    public Form1(string myFile)
    {
        InitializeComponent();
        this.Show();
        if (myFile != null)
        {
            OpenFile(myFile);
        }
    }

    private void OpenFile(string myFile = null)
    {
            MessageBox.Show(myFile);
    }

-9

คุณสามารถปิดแบบฟอร์มของคุณได้หลังจากดำเนินการเสร็จสิ้น ..

//YourForm.ActiveForm.Close ();

    LoadingForm.ActiveForm.Close();
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.