InvalideOperationException / เรียกใช้

รหัสต่อไปนี้ใช้เพื่อทริกเกอร์การเรียกใช้ (รหัสลดลง ฉันละทิ้งการจัดการข้อผิดพลาดในตัวอย่างนี้เพื่อให้ชัดเจนยิ่งขึ้น)

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

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

โดยปกติแล้วมันใช้งานได้ดี แต่บางครั้งถ้าฉันเรียกเมธอดของแบบฟอร์มก็จะได้รับ InvalidOperationException วิธีแผนผังที่จะเรียก

// 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();
        });
    }
}

คือ... ฉันต้องยอมรับว่าฉันมีปัญหาด้านแนวคิดที่นี่

เหตุใดจึงมีข้อยกเว้นถูกส่งมาที่นี่

วิธีการ Frm2 สร้างองค์ประกอบใหม่ (ใน NO มีเพียงสตริงและโครงสร้างที่มีสตริงและบูล) องค์ประกอบสามารถเข้าถึงได้ภายในเมธอด UpdateSomething() เท่านั้น lalayOutControlGroup เป็นสมาชิกของ Frm2

ดังนั้นในความคิดของฉัน ควรแนบการควบคุมใหม่ซึ่งควรสร้างขึ้นในเธรด Frm2 กับการควบคุมเฉพาะของ Frm2 เท่านั้น

แล้วทำไมมันถึงยืนกรานใน FrmMain? (แบบฟอร์มหลักซึ่งเรียกวิธีการของแบบฟอร์มแจ้งการอัพเดตรายการ)

ป.ล. this.InvokeIfRequired ‹- นี่คือ Frm2 จริงๆ แล้ว...


person Offler    schedule 06.06.2013    source แหล่งที่มา
comment
@KenKin นี่คือแผนผังที่โปรแกรมกำลังทำอยู่ ฉันลบชื่อเฉพาะบริษัทและข้อยกเว้นออกเพื่อไม่ให้รบกวน แต่แสดงเฉพาะสิ่งที่ทำเท่านั้น   -  person Offler    schedule 06.06.2013
comment
เนื่องจากคุณได้อัปเดตโค้ดแล้ว ฉันจึงลบความคิดเห็นก่อนหน้าออก   -  person Ken Kin    schedule 06.06.2013
comment
โดยทั่วไปแล้วคุณไม่ควรตรวจสอบว่าจำเป็นต้องเรียกใช้หรือไม่ ณ เวลาคอมไพล์ คุณควรรู้ว่าเมธอดที่กำหนดจะถูกดำเนินการจากเธรด UI หรือไม่ หากคุณไม่ได้อยู่ในเธรด UI ให้โทร Invoke หากคุณไม่ได้อยู่ในเธรด UI หากคุณตรวจสอบอยู่ตลอดเวลาแม้ว่าคุณจะรู้ว่าคุณจะต้องเรียกใช้มันซึ่งสิ้นเปลือง และบางครั้งก็ทำให้คุณลำบากในบางกรณีที่ InvokeRequired เป็นเท็จ แต่คุณจำเป็นต้อง Invoke จริงๆ   -  person Servy    schedule 06.06.2013


คำตอบ (2)


ดังที่เราเห็น การเรียกใช้ ( start) มักจะมีปัญหาอยู่เสมอ ใช้ BeginInrigg แทน คือคำใบ้ของฉัน โดยปกติแล้วจะบอกว่า การเริ่มเรียกใช้การควบคุมจะดำเนินการเมธอดในเธรดเดียวกันกับที่มีการสร้างหมายเลขอ้างอิง

แต่ดูเหมือนว่า หมายเลขอ้างอิงของ form2 ไม่ได้ถูกสร้างขึ้นในเธรดเดียวกัน หรือบางทียังไม่มีอยู่ ลองตรวจสอบและยืนยันสิ่งนี้โปรด

อา. อย่างไรก็ตาม ตั้งค่าสถานะข้อยกเว้น clr ในวิชวลสตูดิโอ เมื่อพวกมันถูกโยนทิ้ง ซึ่งจะช่วยระบุข้อผิดพลาด

person icbytes    schedule 06.06.2013
comment
ถ้าฉันปล่อยให้แสดง ((Control)this).IsHandleCreated ณ จุดที่มันพัง มันจะแสดงเป็น true เนื่องจากนี่คือ Frm2 ไม่ควร InvokeIfNecessary ไม่ควรเรียกใช้ในบริบทที่ถูกต้องที่สร้างหมายเลขอ้างอิง ฉันจะดูได้อย่างไร (VisualStudio 2012) ว่าวัตถุใดถูกสร้างขึ้นในเธรดใด - person Offler; 06.06.2013
comment
ปัญหาอยู่ในตัวอย่างแรกของคุณตามที่ต้องการ และไม่ ฉันต้องเจาะลึกการตั้งค่าเพื่อดูว่าเธรดใดที่สร้างแฮนเดิล แต่มีวิธีเอาคืนแน่นอน - person icbytes; 06.06.2013
comment
สวัสดี คำอธิบายที่ดี แต่ฉันพบปัญหา มันอยู่ในคลาสอื่นโดยสิ้นเชิงจากนั้นที่ตัวดีบักเกอร์หยุดทำงาน (วัตถุถูกเข้าถึงในคลาสอื่นเพื่อบันทึกว่ามีบางอย่างล้มเหลว (ควรเป็นชื่อคลาสเท่านั้น)) พบแล้วในรายการกระทู้ที่ใช้งานอยู่ แต่ฉันยังสงสัยว่าเหตุใดจึงแสดงว่าบรรทัด AddItem จะทำให้เกิดปัญหา - person Offler; 06.06.2013
comment
มันเป็นเพราะว่าข้อยกเว้นวิ่งไปที่ตัวจัดการ NO และนั่นคือที่ที่สายของคุณอยู่ ฉันคิดว่า. แต่ คุณช่วยบอกได้ไหมว่าจะจับด้ายได้อย่างไร? - person icbytes; 06.06.2013
comment
ฉันบอกไม่ได้. ฉันดูรายการเธรดที่ใช้งานอยู่ (Ctrl+D,T) และสิ่งที่พวกเขากำลังทำอยู่ในช่วงเวลาที่มีข้อยกเว้น ในขณะนั้น ตัวบันทึกพยายามเข้าถึงการควบคุมที่มอบให้กับ InvokeIfNecessary และพยายามบันทึกว่าการเรียกใช้นั้นไม่สามารถทำได้ เนื่องจากไม่มีการสร้างหมายเลขอ้างอิง บนเครื่องนี้ดูเหมือนว่าจะในเวลาเดียวกันกับที่มีการเข้าถึงการควบคุมเป็นครั้งที่สอง (เนื่องจากขณะนี้หมายเลขอ้างอิงถูกสร้างขึ้น จึงมีการเรียกใช้ InvoidIfNecessary และหยุดอย่างสนุกสนานในขณะที่บันทึกเนื่องจากบันทึกไม่สามารถเข้าถึงการควบคุมได้) - person Offler; 07.06.2013
comment
อานี้. ฉันรู้แล้ว. บางครั้งคุณต้องมีการจัดการเร็วขึ้นเล็กน้อย สิ่งนี้สามารถทำได้เช่นกัน CreateHandle ต้องเรียกว่าชัดเจน - person icbytes; 07.06.2013

ฉันคิดว่าคุณตรวจสอบ InvokeRequired ด้วยการควบคุมที่ไม่ถูกต้อง

สิ่งที่คุณต้องตรวจสอบด้วย InvokeRequired คือ layoutControlGroup แต่วิธีการขยายของคุณจะตรวจสอบแบบฟอร์มด้วยรหัส this.InvokeIfNecessary โดยที่ this คือ Frm2

นอกจากนี้ คุณได้เรียกใช้ layoutControlGroup.BeginUpdate() แต่ layoutControlGroupCA.EndUpdate() ดูเหมือนว่าการใช้งานไม่สมมาตร

การแก้ไขรหัสอาจเป็น:

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
ดี แต่ .. นั่นไม่ใช่วิธีแก้ปัญหา นอกจากนี้ Visual Studio จะชี้ไปที่บรรทัดเสมอ (หยุดเมื่อมีข้อยกเว้นเกิดขึ้น) ซึ่งจะหยุดที่วิธีอื่น อย่างไรก็ตาม:layoutControlGroup เป็นองค์ประกอบ DevExpress ซึ่งไม่ได้สืบทอดมาจากการควบคุม ดังนั้นlayoutControlGroup.InvoidIfNecessaryจะไม่ทำงาน ฉันยังสงสัยเล็กน้อยเกี่ยวกับรายละเอียดในการอธิบายของคุณ เนื่องจากตัวควบคุมมาจาก Form2 เหตุใดจึงจะไม่อยู่ในบริบทของ Form2 ตามที่เขียนไว้ในโพสต์ต้นฉบับlayoutControlGroup เป็นตัวแปรสมาชิกของ Frm2 - person Offler; 06.06.2013
comment
@Offler: InvokeRequired ใช้เพื่อตรวจสอบว่าการเรียกใช้การควบคุมที่ต้องการอยู่ในบริบทปัจจุบันหรือไม่ ถ้ามันกลับมาจริงก็ไม่ใช่ ค่าที่ส่งคืน InvokeRequired ของคอนเทนเนอร์อาจไม่เหมือนกันกับตัวควบคุมการกำหนดเป้าหมาย - person Ken Kin; 06.06.2013
comment
คุณช่วยอธิบายได้ไหมว่าในกรณีใดคอนเทนเนอร์ (ซึ่งการควบคุมใดที่สามารถเพิ่มได้จากบริบทของเขาเท่านั้น) สามารถมีบริบทอื่นจากนั้นควบคุมได้หรือไม่ - person Offler; 06.06.2013
comment
ใช่. สร้าง Button ชื่อ button1 ในแบบฟอร์มของคุณ และทดสอบโค้ดต่อไปนี้: Debug.Print("{0}", button1.InvokeRequired); (new Thread(() => { Debug.Print("{0}", button1.InvokeRequired); })).Start(); คุณจะได้ผลลัพธ์ที่แตกต่างเป็น InvokeRequired - person Ken Kin; 06.06.2013
comment
@Offler: เนื่องจากวิธีนี้ไม่สามารถแก้ปัญหาของคุณได้ ฉันจึงพร้อมที่จะลบมันแล้ว ขออภัยที่ฉันยังไม่ได้อัปเดตใบอนุญาต DevExpress ของฉัน .. - person Ken Kin; 06.06.2013
comment
@Servy: เหตุใดจึงกู้คืนเนื้อหาของคำตอบที่ถูกลบของฉัน .. ? - person Ken Kin; 06.06.2013
comment
@KenKin ทำไมต้องลบมัน? สาเหตุส่วนใหญ่มาจากการดูว่าคำตอบที่ถูกลบคืออะไรบางครั้งอาจเป็นประโยชน์ บางทีอาจจะถูกหรือใกล้พอที่จะไปทางขวา และอาจได้รับการแก้ไขเพียงพอที่จะใช้งานได้ บางทีอาจเป็นการละเมิด/ไม่เหมาะสมและควรถูกตั้งค่าสถานะ บางทีอาจเป็นคำตอบที่ฉันจะพิจารณาเขียน แต่มันผิดด้วยเหตุผลที่ฉันยังไม่ได้ทำ ยังเห็น; การเห็นว่ามีคนอื่นโพสต์คำตอบและลบออกเมื่อพบว่าผิดอาจเป็นประโยชน์ได้ - person Servy; 06.06.2013