InvalideOperationException / panggil

kode berikut digunakan untuk memicu pemanggilan (kode dikurangi, saya mengabaikan penanganan kesalahan dalam contoh ini agar lebih jelas)

public static void InvokeIfNecessary(this Control control, MethodInvoker methodInvoker)
{
    if (control != null && !control.IsDisposed && !control.Disposing)
    {

        if (control.InvokeRequired)
        {
            control.Invoke(methodInvoker);
        }
        else
        {
            methodInvoker();
        }
    }
}

Biasanya ini berfungsi dengan baik, tetapi terkadang jika saya memanggil metode Formulir, InvalidOperationException diberikan. Metode skematis yang akan dipanggil

// in a Frm2:
internal void UpdateSomething()
{
    List<NO> myObjects = frmMain.NO.GetNOs();

    if (null != myObjects)
    {
        this.InvokeIfNecessary(() =>
        {
            layoutControlGroup.BeginUpdate(); // DevExpress Layoutcontrolgroup

            foreach (NO aObject in myObjects)
            {
                if(...) // if already a control for the object exist update it.
                {
                    // update

                }
                else
                {
                    // add item
                    LayoutControlItem layoutControlItem = new LayoutControlItem();
                    // create new control
                    Control control = CreateNewControl(aObject);
                    layoutControlItem.Control = control;
                    // do some stuff with visibility and size of control
                    ...
                    layoutControlGroup.AddItem(layoutControlItem); // <-- And here the InvalidOperationException occurs.
                    /// The message is (translated
                    /// InvalidOperationException was not handled by usercode
                    /// The acces on the Control FrmMain was done from another Thrad then the thread which created it.
                    ...;
                }
            }


            ...;

            layoutControlGroupCA.EndUpdate();
        });
    }
}

Yah... Saya harus mengakui bahwa saya memiliki masalah konseptual di sini.

Mengapa Pengecualian dilempar ke sini?

Metode Frm2 membuat elemen baru (Dalam NO hanya ada string dan struct dengan string dan bool). Elemen ini hanya diakses dalam metode UpdateSomething(). LayOutControlGroup adalah anggota Frm2.

Jadi menurut saya hanya Kontrol baru yang harus dibuat di Thread Frm2 yang harus dilampirkan ke Kontrol khusus Frm2.

Jadi mengapa ia bersikeras pada FrmMain? (formulir utama, yang memanggil metode formulir untuk menginformasikan tentang pembaruan item)

P.S. this.InvokeIfRequired ‹- ini sebenarnya Frm2...


person Offler    schedule 06.06.2013    source sumber
comment
@KenKin ini adalah skema apa yang dilakukan program ini. Saya menghapus nama spesifik perusahaan, dan pengecualian sehingga tidak mengganggu, tetapi hanya menunjukkan fungsinya.   -  person Offler    schedule 06.06.2013
comment
Karena Anda telah memperbarui kodenya, saya menghapus komentar sebelumnya.   -  person Ken Kin    schedule 06.06.2013
comment
Pada umumnya Anda tidak perlu memeriksa apakah Anda perlu memanggil. Anda harus mengetahui, pada waktu kompilasi, apakah metode tertentu akan dieksekusi dari thread UI atau tidak. Jika Anda tidak berada di thread UI, panggil Invoke, jika iya, jangan. Jika Anda terus-menerus memeriksa bahkan ketika Anda tahu Anda harus memanggilnya, itu hanya sia-sia, dan kadang-kadang menyusahkan Anda dalam beberapa kasus di mana InvokeRequired salah tetapi Anda benar-benar perlu Invoke.   -  person Servy    schedule 06.06.2013


Jawaban (2)


Jadi, seperti yang kita lihat, selalu ada masalah dengan ( mulai) pemanggilan. Gunakan BeginInvoke sebagai gantinya. adalah petunjukku. Biasanya dikatakan, bahwa pemanggilan awal pada kontrol mengeksekusi metode di thread yang sama tempat pegangan dibuat.

Namun tampaknya, pegangan form2 belum dibuat di thread yang sama, ATAU mungkin belum ada. Coba periksa dan verifikasi ini.

Ah. Ngomong-ngomong, tandai pengecualian, clr, di studio visual, saat dilempar. Ini membantu menemukan kesalahannya.

person icbytes    schedule 06.06.2013
comment
Jika saya membiarkan tampilan ((Control)this).IsHandleCreated pada titik di mana ia rusak, itu menunjukkan kebenaran. Karena ini adalah Frm2, bukankah InvokeIfNecessary tidak boleh dipanggil dalam konteks yang benar di mana pegangan itu dibuat? Bagaimana saya bisa melihat (VisualStudio 2012) di thread mana suatu objek dibuat? - person Offler; 06.06.2013
comment
Masalahnya ada pada cuplikan pertama Anda, seperti yang terlihat bagi saya. Dan tidak, saya harus menggali pengaturan untuk mencari tahu, thread mana yang membuat pegangannya. Tapi pasti ada metode untuk mengambilnya kembali. - person icbytes; 06.06.2013
comment
Hai, penjelasan yang bagus, tetapi saya menemukan masalahnya. Itu berada di kelas yang sama sekali berbeda saat debugger berhenti. (objek diakses di kelas lain untuk mencatat bahwa ada sesuatu yang gagal (seharusnya hanya nama kelas)) . Sekarang ditemukan di daftar thread aktif. Tapi saya masih penasaran mengapa ini menunjukkan bahwa baris AddItem akan menyebabkan masalah. - person Offler; 06.06.2013
comment
ini karena di mana pengecualian tersebut bertemu dengan NO handler. Dan di situlah garis Anda berada. Saya seharusnya. Tapi, bisakah Anda memberi tahu, bagaimana cara mendapatkan pegangan dengan benangnya? - person icbytes; 06.06.2013
comment
Aku tidak bisa mengatakannya. Saya melihat daftar utas aktif (Ctrl+D,T) dan apa yang mereka lakukan pada saat pengecualian. Pada saat itu logger mencoba mengakses Kontrol yang diberikan kepada InvokeIfNecessary dan mencoba mencatat bahwa pemanggilan tidak dapat dilakukan, karena tidak ada pegangan yang dibuat. Pada mesin ini tampaknya pada saat yang sama ketika Kontrol diakses untuk kedua kalinya (karena sekarang pegangan telah dibuat, InvokeIfNecessary dipanggil, dan secara lucu itu berhenti ketika sedang log karena log tidak dapat mengakses kontrol.) - person Offler; 07.06.2013
comment
Ah, ini. Saya tahu itu. Terkadang Anda memerlukan pegangan sedikit lebih awal. Hal ini juga dapat dicapai. CreateHandle harus disebut eksplisit. - person icbytes; 07.06.2013

Saya pikir Anda memeriksa InvokeRequired dengan kontrol yang salah.

Yang perlu Anda periksa dengan InvokeRequired adalah layoutControlGroup, tetapi metode ekstensi Anda memeriksa formulir dengan kode this.InvokeIfNecessary di mana this adalah Frm2.

Selain itu, Anda memanggil layoutControlGroup.BeginUpdate() tetapi layoutControlGroupCA.EndUpdate(), sepertinya penggunaannya tidak simetris.

Koreksi kodenya mungkin:

internal void UpdateSomething() {
    var myObjects=frmMain.NO.GetNOs();

    if(null!=myObjects) {
        MethodInvoker beginUpdate=() => layoutControlGroup.BeginUpdate();
        MethodInvoker endUpdate=() => layoutControlGroup.EndUpdate();

        layoutControlGroup.InvokeIfNecessary(beginUpdate);

        foreach(NO aObject in myObjects)
            if(SomeCondition) {
                // update
            }
            else {
                LayoutControlItem layoutControlItem=new LayoutControlItem();
                Control control=CreateNewControl(aObject);
                layoutControlItem.Control=control;

                MethodInvoker addItem=
                    () => {
                        layoutControlGroup.AddItem(layoutControlItem);
                    };

                layoutControlGroup.InvokeIfNecessary(addItem);
            }

        layoutControlGroup.InvokeIfNecessary(endUpdate);
    }
}
person Ken Kin    schedule 06.06.2013
comment
Bagus, tapi.. itu bukan solusinya. Juga Visual Studio selalu menunjuk ke garis (berhenti ketika pengecualian dilemparkan) itu terhenti pada metode lain. Omong-omong: layoutControlGroup adalah elemen DevExpress, yang tidak mewarisi kontrol. Oleh karena itu layoutControlGroup.InvokeIfNecessary tidak akan berfungsi. Saya juga sedikit penasaran dengan detail penjelasan Anda. Karena kontrolnya berasal dari Form2, bagaimana mungkin kontrol tersebut tidak berada dalam konteks Form2? Seperti yang tertulis di postingan asli, layoutControlGroup adalah variabel anggota Frm2 - person Offler; 06.06.2013
comment
@Offler: InvokeRequired digunakan untuk memeriksa apakah pemanggilan kontrol yang diinginkan ada dalam konteks saat ini. Jika hasilnya benar, berarti tidak. Nilai kembalian InvokeRequired penampung mungkin tidak sama dengan kontrol penargetan. - person Ken Kin; 06.06.2013
comment
dapatkah Anda menjelaskan dalam hal apa wadah (kontrol mana yang hanya dapat ditambahkan dari konteksnya?) dapat memiliki konteks lain selain kontrolnya? - person Offler; 06.06.2013
comment
Ya. Buat Button bernama button1 di formulir Anda, dan uji kode berikut: Debug.Print("{0}", button1.InvokeRequired); (new Thread(() => { Debug.Print("{0}", button1.InvokeRequired); })).Start(); Anda akan mendapatkan hasil InvokeRequired yang berbeda. - person Ken Kin; 06.06.2013
comment
@Offler: Karena ini tidak menyelesaikan masalah Anda, saya siap menghapusnya. Maaf untuk itu saya belum memperbarui lisensi DevExpress saya .. - person Ken Kin; 06.06.2013
comment
@Servy: Mengapa memulihkan konten jawaban saya yang dihapus ..? - person Ken Kin; 06.06.2013
comment
@KenKin Mengapa menghapusnya? Terutama karena kadang-kadang membantu untuk melihat jawaban yang dihapus. Mungkin benar, atau cukup dekat dengan benar, dan bisa diperbaiki agar bisa diterapkan, mungkin kasar/tidak pantas dan harus ditandai, mungkin itu jawaban yang akan saya pertimbangkan untuk ditulis, tapi itu salah karena alasan yang belum saya miliki belum terlihat; melihat orang lain memposting jawabannya dan menghapusnya ketika terbukti salah dapat membantu. - person Servy; 06.06.2013