문제

I have a splashscreen:

public partial class LoadingScreen : Form
{
    private delegate void CloseDelegate();
    private static LoadingScreen loadingscreen;

    private LoadingScreen()
    {
        InitializeComponent();
    }

    private static void ShowForm()
    {
        loadingscreen = new LoadingScreen();
        Application.Run(loadingscreen);
    }

    public static void ShowLoadingscreen()
    {
        if (loadingscreen != null)
            return;
        System.Threading.Thread thread = new System.Threading.Thread(LoadingScreen.ShowForm);
        thread.IsBackground = true;
        thread.SetApartmentState(System.Threading.ApartmentState.STA);
        thread.Start();
    }

    public static void CloseForm()
    {
        loadingscreen.Invoke(new CloseDelegate(LoadingScreen.CloseFormInternal));
    }

    private static void CloseFormInternal()
    {
        loadingscreen.Close();
        loadingscreen = null;
    }
}

It is called whene the datagrid is busy to refresh (it need some drawing and it takes a while for our hugh data) also whene the user uses the textbox to search something it's triggerd because we need to redraw everything. The code what triggerd the splashscreen:

 private void EditGrid()
    {
        LoadingScreen.ShowLoadingscreen();
        CheckItems();
        EditGridVisibility();
        LoadingScreen.CloseForm();
    } 

when the user types in 3, 4, or 5 characters in the searchbox I get a NullReferenceException on the LoadingScreen.CloseForm(); And it's correct I get a NullRef, because I don't see the form in debugging so there goes something wrong when showing (or closing the last time) but I don't see why.

도움이 되었습니까?

해결책

This error occurs when CloseForm is called before ShowLoadingScreen has had time to spin up it's thread and create loadingScreen.

It is almost always easier to load/process the data on a background thread and display dialogs on your main UI thread. But if that is not possible, ensure the dialog is displayed before dismissing it or abort its creation.

A rough solution would be to make the thread global and...

public static void CloseForm()
{
    if (loadingscreen == null)
    {
        _thread.Abort();
        return;
    }
    loadingscreen.Invoke(new CloseDelegate(CloseFormInternal));
}

다른 팁

I have used John's solution and it worked fine for most of the cases. However, in certain situations, the _thread.Abort() call causes the application to simply crash, without any exceptions. Looking it up, it appears to be very bad practice to use Thread.Abort(), as is killing a thread (see this, this, this, this and many other online), so as others have said, DO NOT USE Thread.Abort()!

Here's a solution that works perfectly:

public static void CloseForm()
{
    // Loop for a maximum of 100ms if the screen hasn't yet been loaded.
    for (var i = 0; i < 100; i++)
    {
        if (loadingScreenForm != null && loadingScreenForm.IsHandleCreated)
        {
            break;
        }

        Thread.Sleep(1);
    }

    // Don't try to close if it is already closed.
    // If the screen form is still null after waiting, it was most likely already closed.
    if (loadingScreenForm != null && loadingScreenForm.IsHandleCreated)
    {
        loadingScreenForm.Invoke(new CloseDelegate(CloseFormInternal));
    }
}

The specifics of my code require that I check whether the handle has been created but you might be able to do without it. In the case where our situation occurs, this will add an overhead of 100ms to the code. You can tweak it if you feel like a tenth of a second is somehow too much.

I know it's quite late but hopefully it will help someone having the same issue in the future.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top