2008-09-08 35 views
58

Tôi muốn một màn hình giật gân hiển thị khi ứng dụng đang tải. Tôi có một hình thức với một điều khiển khay hệ thống gắn liền với nó. Tôi muốn màn hình splash hiển thị trong khi biểu mẫu này tải, mất một chút thời gian kể từ khi nó truy cập API dịch vụ web để điền một số menu thả xuống. Tôi cũng muốn làm một số thử nghiệm cơ bản cho các phụ thuộc trước khi tải (nghĩa là dịch vụ web có sẵn, tệp cấu hình có thể đọc được). Khi mỗi giai đoạn của khởi động đi, tôi muốn cập nhật màn hình giật gân với tiến trình.Màn hình giật gân đa luồng trong C#?

Tôi đã đọc rất nhiều về luồng, nhưng tôi bị lạc vào nơi điều này cần được kiểm soát từ (phương thức main()?). Tôi cũng thiếu cách hoạt động của Application.Run(), đây có phải là nơi mà các chủ đề cho việc này nên được tạo ra không? Bây giờ, nếu biểu mẫu có điều khiển khay hệ thống là biểu mẫu "đang hoạt động", thì giật gân có đến từ đó không? Nó sẽ không tải cho đến khi hình thức được hoàn thành anyway?

Tôi không tìm kiếm một handout mã, nhiều hơn một thuật toán/cách tiếp cận để tôi có thể con số này ra một lần và cho tất cả :)

Trả lời

47

Vâng, đối với một ứng dụng ClickOnce rằng tôi triển khai trong quá khứ, chúng tôi sử dụng không gian tên Microsoft.VisualBasic để xử lý luồng màn hình giật gân. Bạn có thể tham khảo và sử dụng hội đồng Microsoft.VisualBasic từ C# trong .NET 2.0 và nó cung cấp rất nhiều dịch vụ tốt đẹp.

  1. Có biểu mẫu chính kế thừa từ Microsoft.VisualBasic.WindowsFormsApplicationBase
  2. Ghi đè các "OnCreateSplashScreen" phương pháp như vậy:

    protected override void OnCreateSplashScreen() 
    { 
        this.SplashScreen = new SplashForm(); 
        this.SplashScreen.TopMost = true; 
    } 
    

Rất đơn giản, nó cho thấy SplashForm của bạn (mà bạn cần phải tạo ra) trong khi tải đang diễn ra, sau đó đóng nó tự động sau khi hình thức chính đã hoàn thành tải. Điều này thực sự làm cho mọi việc đơn giản, và VisualBasic.WindowsFormsApplicationBase là tất nhiên cũng được thử nghiệm bởi Microsoft và có rất nhiều chức năng có thể làm cho cuộc sống của bạn dễ dàng hơn nhiều trong Winforms, ngay cả trong một ứng dụng 100% C#.

Vào cuối ngày, tất cả đều là IL và bytecode, vậy tại sao không sử dụng?

+2

Đây là giải pháp thanh lịch nhất vì nó sử dụng chức năng tích hợp được tóm tắt rất tốt bởi thư viện Visual Basic. Tôi không biết làm thế nào để tạo ra một chương trình VB ApplicationBase, vì vậy nghiên cứu thêm là cần thiết ... Tôi sẽ bổ sung thêm thông tin cho đầy đủ. Cảm ơn vì tất cả những phản hồi tuyệt vời!! –

+0

Tôi đã phải sửa một số màn hình giật gân thủ công bằng văn bản - sử dụng chức năng được hỗ trợ như Guy gợi ý chắc chắn là cách tuyệt vời để đi. :) –

+1

Ý tưởng hay nhưng không hoạt động trên khung nhỏ gọn. – Damien

6

Một cách đơn giản là một cái gì đó sử dụng như thế này là main():

<STAThread()> Public Shared Sub Main() 

    splash = New frmSplash 
    splash.Show() 

    ' Your startup code goes here... 

    UpdateSplashAndLogMessage("Startup part 1 done...") 

    ' ... and more as needed... 

    splash.Hide() 
    Application.Run(myMainForm) 
End Sub 

Khi .NET CLR khởi động ứng dụng của bạn, nó tạo chủ đề 'chính' và bắt đầu thực hiện chính() trên chuỗi đó. Application.Run (myMainForm) ở cuối có hai điều:

  1. Khởi động máy chủ thông báo của Windows, sử dụng luồng đã thực hiện chủ yếu() làm luồng GUI.
  2. Chỉ định 'biểu mẫu chính' của bạn làm 'biểu mẫu tắt' cho ứng dụng. Nếu người dùng đóng biểu mẫu đó, thì Application.Run() chấm dứt và kiểm soát trở về chính của bạn(), nơi bạn có thể thực hiện bất kỳ tắt nào bạn muốn.

Không cần phải tạo ra một chuỗi để xử lý cửa sổ giật gân, và thực tế đây là một ý tưởng tồi, vì sau đó bạn sẽ phải sử dụng kỹ thuật an toàn luồng để cập nhật nội dung giật từ chính ().

Nếu bạn cần các chủ đề khác để thực hiện các thao tác nền trong ứng dụng của mình, bạn có thể sinh ra chúng từ main(). Chỉ cần nhớ đặt Thread.IsBackground là True, để chúng sẽ chết khi chủ đề chính/GUI chấm dứt. Nếu không, bạn sẽ phải sắp xếp để chấm dứt tất cả các chủ đề khác của mình, hoặc họ sẽ giữ cho ứng dụng của bạn còn sống (nhưng không có GUI) khi chủ đề chính chấm dứt.

47

Bí quyết là tạo chuỗi riêng biệt chịu trách nhiệm cho màn hình hiển thị giật gân.
Khi bạn chạy ứng dụng .net tạo chủ đề chính và tải biểu mẫu (chính) được chỉ định. Để che giấu công việc khó khăn, bạn có thể ẩn biểu mẫu chính cho đến khi tải xong.

Giả sử rằng Form1 - là hình thức chính của bạn và SplashForm là cấp cao nhất, borderles hình thức giật gân đẹp:

private void Form1_Load(object sender, EventArgs e) 
{ 
    Hide(); 
    bool done = false; 
    ThreadPool.QueueUserWorkItem((x) => 
    { 
     using (var splashForm = new SplashForm()) 
     { 
      splashForm.Show(); 
      while (!done) 
       Application.DoEvents(); 
      splashForm.Close(); 
     } 
    }); 

    Thread.Sleep(3000); // Emulate hardwork 
    done = true; 
    Show(); 
} 
+1

Tôi thích phương pháp này, cảm ơn chồi, tôi thường sử dụng điều này. – Echostorm

+0

Đây có lẽ là phương pháp tốt nhất mà tôi đã thấy để làm màn hình giật gân. Hoạt động BEAUTIFULLY trên .NET gọn nhẹ với ứng dụng PC bỏ túi của tôi. – Damien

+5

@aku: Có nên sử dụng Application.DoEvents() trong ứng dụng không? Tôi đọc rất nhiều về việc không sử dụng chức năng này trên internet như http://blogs.msdn.com/jfoscoding/archive/2005/08/06/448560.aspx –

7

Tôi nghĩ rằng sử dụng một số phương pháp như aku's hoặc Guy's là con đường để đi, nhưng một vài điều để lấy đi từ các ví dụ cụ thể:

  1. Những tiền đề cơ bản sẽ được hiển thị giật gân của bạn trên một riêng biệt chủ đề càng sớm càng tốt. Đó là cách tôi sẽ gầy, tương tự như những gì aku minh họa, vì đó là cách tôi quen thuộc nhất. Tôi đã không nhận thức được chức năng VB Guy đã đề cập. Và, thậm chí nghĩ rằng đó là thư viện VB, anh ấy đúng - tất cả đều là IL cuối cùng. Vì vậy, ngay cả khi nó cảm thấy bẩn nó không phải là tất cả những gì xấu! :) Tôi nghĩ bạn sẽ muốn chắc chắn rằng một trong hai VB cung cấp một thread riêng biệt cho trong đó ghi đè hoặc bạn tạo ra một mình - chắc chắn nghiên cứu đó.

  2. Giả sử bạn tạo một chuỗi khác để hiển thị hình ảnh giật gân này, bạn sẽ cần phải cẩn thận với các bản cập nhật giao diện người dùng chéo. Tôi mang lại điều này bởi vì bạn đã đề cập đến quá trình cập nhật. Về cơ bản, để an toàn, bạn cần gọi hàm cập nhật (mà bạn tạo) trên biểu mẫu giật bằng cách sử dụng ủy nhiệm. Bạn chuyển giao đại biểu đó cho hàm Invoke trên đối tượng biểu mẫu của màn hình giật gân của bạn. Trong thực tế, nếu bạn gọi trực tiếp biểu mẫu giật gân để cập nhật các phần tử tiến trình/giao diện người dùng trên đó, bạn sẽ nhận được một ngoại lệ với điều kiện bạn đang chạy trên .Net 2.0 CLR. Theo quy tắc chung, bất kỳ phần tử giao diện người dùng nào trên biểu mẫu phải được cập nhật bởi chuỗi đã tạo nó - đó là điều Form.Invoke đảm bảo.

Cuối cùng, tôi có thể chọn tạo vùng giật (nếu không sử dụng quá tải VB) trong phương pháp chính của mã của bạn. Đối với tôi, điều này tốt hơn là có hình thức chính thực hiện việc tạo ra đối tượng và bị ràng buộc chặt chẽ với nó. Nếu bạn sử dụng cách tiếp cận đó, tôi khuyên bạn nên tạo một giao diện đơn giản mà màn hình splash thực hiện - một cái gì đó giống như IStartupProgressListener - nó nhận các cập nhật tiến trình khởi động thông qua một hàm thành viên. Điều này sẽ cho phép bạn dễ dàng hoán đổi vào/ra một trong hai lớp nếu cần, và độc đáo tách riêng mã. Biểu mẫu giật gân cũng có thể biết thời điểm đóng chính nó nếu bạn thông báo khi khởi động hoàn tất.

9

Tôi khuyên bạn nên gọi số Activate(); trực tiếp sau số Show(); cuối cùng trong câu trả lời do aku cung cấp.

Trích dẫn MSDN:

Kích hoạt một hình thức mang nó vào trước nếu đây là hoạt động ứng dụng, hoặc nó nhấp nháy chú thích cửa sổ nếu đây không phải là hoạt động ứng dụng. Biểu mẫu phải hiển thị để phương thức này có hiệu lực.

Nếu bạn không kích hoạt biểu mẫu chính, nó có thể được hiển thị đằng sau bất kỳ cửa sổ đang mở nào khác, làm cho nó trông hơi ngớ ngẩn.

5

Tôi đã đăng một bài viết về kết hợp màn hình giật gân trong ứng dụng tại lập trình. Đó là đa luồng và có thể quan tâm của bạn

Yet Another Splash Screen in C#

9

Đây là một câu hỏi cũ, nhưng tôi vẫn tiếp tục đi ngang qua nó khi cố gắng tìm một giải pháp màn hình splash luồng cho WPF có thể bao gồm hình ảnh động.

Dưới đây là những gì tôi cuối cùng chắp ghép:

App.xaml:

<Application Startup="ApplicationStart" … 

App.XAML.cs:

void ApplicationStart(object sender, StartupEventArgs e) 
{ 
     var thread = new Thread(() => 
     { 
      Dispatcher.CurrentDispatcher.BeginInvoke ((Action)(() => new MySplashForm().Show())); 
      Dispatcher.Run(); 
     }); 
     thread.SetApartmentState(ApartmentState.STA); 
     thread.IsBackground = true; 
     thread.Start(); 

     // call synchronous configuration process 
     // and declare/get reference to "main form" 

     thread.Abort(); 

     mainForm.Show(); 
     mainForm.Activate(); 
    } 
14

Sau khi tìm kiếm khắp nơi trên Google và SO cho các giải pháp, đây là yêu thích của tôi: http://bytes.com/topic/c-sharp/answers/277446-winform-startup-splash-screen

FormSplash.cs:

public partial class FormSplash : Form 
{ 
    private static Thread _splashThread; 
    private static FormSplash _splashForm; 

    public FormSplash() { 
     InitializeComponent(); 
    } 

    /// <summary> 
    /// Show the Splash Screen (Loading...) 
    /// </summary> 
    public static void ShowSplash() 
    { 
     if (_splashThread == null) 
     { 
      // show the form in a new thread 
      _splashThread = new Thread(new ThreadStart(DoShowSplash)); 
      _splashThread.IsBackground = true; 
      _splashThread.Start(); 
     } 
    } 

    // called by the thread 
    private static void DoShowSplash() 
    { 
     if (_splashForm == null) 
      _splashForm = new FormSplash(); 

     // create a new message pump on this thread (started from ShowSplash) 
     Application.Run(_splashForm); 
    } 

    /// <summary> 
    /// Close the splash (Loading...) screen 
    /// </summary> 
    public static void CloseSplash() 
    { 
     // need to call on the thread that launched this splash 
     if (_splashForm.InvokeRequired) 
      _splashForm.Invoke(new MethodInvoker(CloseSplash)); 

     else 
      Application.ExitThread(); 
    } 
} 

Program.cs:

static class Program 
{ 
    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    [STAThread] 
    static void Main(string[] args) 
    { 
     // splash screen, which is terminated in FormMain 
     FormSplash.ShowSplash(); 

     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 
     // this is probably where your heavy lifting is: 
     Application.Run(new FormMain()); 
    } 
} 

FormMain.cs

... 

    public FormMain() 
    { 
     InitializeComponent();    

     // bunch of database access, form loading, etc 
     // this is where you could do the heavy lifting of "loading" the app 
     PullDataFromDatabase(); 
     DoLoadingWork();    

     // ready to go, now close the splash 
     FormSplash.CloseSplash(); 
    } 

tôi có vấn đề với giải pháp Microsoft.VisualBasic - Làm việc tìm thấy trên XP, nhưng trên Windows 2003 Terminal Server, hình thức ứng dụng chính sẽ hiển thị (sau màn hình giật gân) trong nền và thanh tác vụ sẽ nhấp nháy. Và đưa một cửa sổ lên trước/lấy nét trong mã là một toàn bộ các loại sâu khác mà bạn có thể sử dụng cho Google/SO.

+0

Thanx, giải pháp tốt đẹp! – nightcoder

5
private void MainForm_Load(object sender, EventArgs e) 
{ 
    FormSplash splash = new FormSplash(); 
    splash.Show(); 
    splash.Update(); 
    System.Threading.Thread.Sleep(3000); 
    splash.Hide(); 
} 

Tôi nhận được thông tin này từ Internet ở đâu đó nhưng dường như không thể tìm thấy lại. Đơn giản nhưng hiệu quả.

4

Tôi thích câu trả lời của Aku rất nhiều, nhưng mã dành cho C# 3.0 trở lên vì nó sử dụng hàm lambda. Đối với những người cần sử dụng mã trong C# 2.0, đây là mã sử dụng đại biểu ẩn danh thay vì hàm lambda. Bạn cần một hình thức thắng trên cùng được gọi là formSplash với FormBorderStyle = None. Thông số TopMost = True của biểu mẫu là quan trọng vì màn hình giật gân có thể trông giống như xuất hiện sau đó biến mất nhanh chóng nếu nó không ở trên cùng. Tôi cũng chọn StartPosition=CenterScreen để có vẻ như ứng dụng chuyên nghiệp sẽ làm gì với màn hình giật gân. Nếu bạn muốn có hiệu ứng mát hơn, bạn có thể sử dụng thuộc tính TrasparencyKey để tạo màn hình giật gân có hình dạng bất thường.

private void formMain_Load(object sender, EventArgs e) 
    { 

    Hide(); 
    bool done = false; 
    ThreadPool.QueueUserWorkItem(delegate 
    { 
     using (formSplash splashForm = new formSplash()) 
     { 
      splashForm.Show(); 
      while (!done) 
       Application.DoEvents(); 
      splashForm.Close(); 
     } 
    }, null); 

    Thread.Sleep(2000); 
    done = true; 
    Show(); 
    } 
3

Tôi không đồng ý với các câu trả lời khác đề xuất WindowsFormsApplicationBase. Theo kinh nghiệm của tôi, nó có thể làm chậm ứng dụng của bạn. Để chính xác, trong khi nó chạy hàm tạo của biểu mẫu của bạn song song với màn hình giật gân, nó trì hoãn sự kiện Hiển thị của biểu mẫu của bạn.

Hãy xem xét một ứng dụng (không có màn hình giật gân) với hàm tạo mất 1 giây và trình xử lý sự kiện trên Hiển thị mất 2 giây. Ứng dụng này có thể sử dụng sau 3 giây.

Nhưng giả sử bạn cài đặt màn hình giật gân sử dụng WindowsFormsApplicationBase. Bạn có thể nghĩ rằng MinimumSplashScreenDisplayTime trong 3 giây là hợp lý và sẽ không làm chậm ứng dụng của bạn. Nhưng, hãy thử, ứng dụng của bạn sẽ mất 5 giây để tải.


class App : WindowsFormsApplicationBase 
{ 
    protected override void OnCreateSplashScreen() 
    { 
     this.MinimumSplashScreenDisplayTime = 3000; // milliseconds 
     this.SplashScreen = new Splash(); 
    } 

    protected override void OnCreateMainForm() 
    { 
     this.MainForm = new Form1(); 
    } 
} 

public Form1() 
{ 
    InitializeComponent(); 
    Shown += Form1_Shown; 
    Thread.Sleep(TimeSpan.FromSeconds(1)); 
} 

void Form1_Shown(object sender, EventArgs e) 
{ 
    Thread.Sleep(TimeSpan.FromSeconds(2)); 
    Program.watch.Stop(); 
    this.textBox1.Text = Program.watch.ElapsedMilliseconds.ToString(); 
} 

Kết luận: không sử dụng WindowsFormsApplicationBase nếu ứng dụng của bạn có một handler trên các sự kiện Slown. Bạn có thể viết mã tốt hơn chạy song song với cả hai hàm khởi tạo và sự kiện được hiển thị.

0

Thực sự việc cắt xén ở đây là không cần thiết.

Cho phép logic nghiệp vụ của bạn tạo sự kiện bất cứ khi nào bạn muốn cập nhật màn hình giật gân.

Sau đó, để biểu mẫu của bạn cập nhật màn hình giật gân tương ứng trong phương thức được nối với tổ chức sự kiện.

Để phân biệt các bản cập nhật, bạn có thể kích hoạt các sự kiện khác nhau hoặc cung cấp dữ liệu trong một lớp được kế thừa từ EventArgs.

Bằng cách này bạn có thể thay đổi màn hình giật gân đẹp mắt mà không gặp phải bất kỳ sự đau đầu đa luồng nào.

Thực tế với điều này, bạn thậm chí có thể hỗ trợ, ví dụ: hình ảnh gif trên biểu mẫu giật gân. Để nó hoạt động, hãy gọi cho Application.DoEvents() trong trình xử lý của bạn:

private void SomethingChanged(object sender, MyEventArgs e) 
{ 
    formSplash.Update(e); 
    Application.DoEvents(); //this will update any animation 
} 
Các vấn đề liên quan