DesignMode พร้อมตัวควบคุมที่ซ้อนกัน


87

มีใครพบวิธีแก้ปัญหา DesignMode ที่เป็นประโยชน์เมื่อพัฒนาตัวควบคุมบ้าง?

ปัญหาคือถ้าคุณซ้อนตัวควบคุม DesignMode จะทำงานในระดับแรกเท่านั้น DesignMode ระดับที่สองและต่ำกว่าจะส่งคืน FALSE เสมอ

การแฮ็กมาตรฐานจะดูชื่อของกระบวนการที่กำลังทำงานอยู่และถ้าเป็น "DevEnv.EXE" ก็ต้องเป็นสตูดิโอดังนั้น DesignMode จึงเป็นจริง

ปัญหาเกี่ยวกับที่กำลังมองหา ProcessName ทำงานผ่านรีจิสทรีและส่วนแปลก ๆ อื่น ๆ โดยผลลัพธ์สุดท้ายที่ผู้ใช้อาจไม่มีสิทธิ์ที่จำเป็นในการดูชื่อกระบวนการ นอกจากนี้เส้นทางแปลก ๆ นี้ช้ามาก ดังนั้นเราจึงต้องกองแฮ็กเพิ่มเติมเพื่อใช้ซิงเกิลตันและหากเกิดข้อผิดพลาดเมื่อขอชื่อกระบวนการให้ถือว่า DesignMode เป็น FALSE

วิธีที่ดีในการกำหนด DesignMode เป็นไปตามลำดับ การให้ Microsoft แก้ไขภายในเฟรมเวิร์กจะดียิ่งขึ้น!


blogpost อธิบายการแก้ปัญหาที่เป็นไปได้ในการแก้ไขปัญหา
Max Galkin

8
+1 สำหรับ "การให้ Microsoft แก้ไขภายในเฟรมเวิร์กจะดียิ่งขึ้น" - เวลา 10 นาทีของใครบางคนจะช่วยประหยัดเวลาให้กับผู้คนนับหมื่นชั่วโมงต่อชิ้น หากมีโปรแกรมใดโปรแกรมหนึ่งที่อาศัยจุดบกพร่องและ 100,000 รายการที่ไม่สามารถใช้งานได้ก็ไม่สมเหตุสมผลที่จะเก็บข้อบกพร่องไว้เพื่อหลีกเลี่ยงความไม่สะดวกในโปรแกรมเดียว!
BlueRaja - Danny Pflughoeft

สวัสดีนี่โพสต์ในปี 2008 ตอนนี้ได้รับการแก้ไขแล้วหรือยัง?
Jake

ใน VS 2012 ตอนนี้ยังคงเหมือนเดิม
Boogier

1
โปรดสังเกตว่าหากใช้ตัวออกแบบที่กำหนดเองสำหรับ UserControl (เช่นฉันได้ทดสอบกับคลาสที่ได้มาจาก ControlDesigner) การเรียก EnableDesignMode (subControl) ดูเหมือนจะทำให้คุณสมบัติ DesignMode ของการควบคุมย่อยทำงาน นี่ไม่ใช่วิธีแก้ปัญหาที่มีประสิทธิภาพ แต่เนื่องจากเราไม่ได้เขียนคอนเทนเนอร์ที่ควบคุมของเราเสมอไป
Protongun

คำตอบ:


81

เมื่อทบทวนคำถามนี้ตอนนี้ฉันได้ 'ค้นพบ' 5 วิธีที่แตกต่างกันในการทำสิ่งนี้ซึ่งมีดังนี้:

System.ComponentModel.DesignMode property

System.ComponentModel.LicenseManager.UsageMode property

private string ServiceString()
{
    if (GetService(typeof(System.ComponentModel.Design.IDesignerHost)) != null) 
        return "Present";
    else
        return "Not present";
}

public bool IsDesignerHosted
{
    get
    {
        Control ctrl = this;

        while(ctrl != null)
        {
            if((ctrl.Site != null) && ctrl.Site.DesignMode)
                return true;
            ctrl = ctrl.Parent;
        }
        return false;
    }
}
public static bool IsInDesignMode()
{
    return System.Reflection.Assembly.GetExecutingAssembly()
         .Location.Contains("VisualStudio"))
}

ในการลองใช้โซลูชันสามข้อที่เสนอฉันได้สร้างโซลูชันทดสอบเล็กน้อยโดยมีสามโครงการ:

  • TestApp (แอปพลิเคชัน winforms)
  • SubControl (dll)
  • SubSubControl (dll)

จากนั้นฉันฝัง SubSubControl ใน SubControl จากนั้นหนึ่งในแต่ละรายการใน TestApp.Form

ภาพหน้าจอนี้แสดงผลลัพธ์เมื่อทำงาน ภาพหน้าจอของการทำงาน

ภาพหน้าจอนี้แสดงผลลัพธ์โดยเปิดแบบฟอร์มใน Visual Studio:

ภาพหน้าจอของการไม่ทำงาน

สรุป: ดูเหมือนว่าหากไม่มีการสะท้อนสิ่งเดียวที่เชื่อถือได้ภายในตัวสร้างคือ LicenseUsage และสิ่งเดียวที่เชื่อถือได้นอกตัวสร้างคือ 'IsDesignedHosted' (โดยBlueRajaด้านล่าง)

PS: ดูความคิดเห็นของ ToolmakerSteve ด้านล่าง (ซึ่งฉันยังไม่ได้ทดสอบ): "โปรดทราบว่าคำตอบของIsDesignerHostedได้รับการอัปเดตเพื่อรวม LicenseUsage ... ดังนั้นตอนนี้การทดสอบอาจเป็นได้ง่ายๆถ้า (IsDesignerHosted) แนวทางอื่นคือทดสอบ LicenseManager ในตัวสร้าง และแคชผลลัพธ์ "


@ Benjol: แล้ว IsDesignerHosted (ด้านล่าง) ล่ะ? (นอกจากนี้ฉันคิดว่าคุณมีการสลับเวลาออกแบบและรันไทม์ตรวจสอบสิ่งที่กล่าวระหว่างรันไทม์)
BlueRaja - Danny Pflughoeft

@BlueRaja ฉันยังต้องมีโครงการนั้นวางอยู่บนดิสก์บางทีฉันควรโพสต์ไว้ที่ไหนสักแห่ง ...
Benjol

1
+1 สำหรับการชี้แจงโดยการทดลองเชิงประจักษ์ @Benjol หากคุณมีโอกาสกลับมาทบทวนอีกครั้งคุณอาจเพิ่มกรณีสำหรับค่าในแบบฟอร์มเองเนื่องจากการควบคุมเด็กอาจได้รับการปฏิบัติที่แตกต่างจากคลาสที่แก้ไขในตัวออกแบบ (โปรดทราบว่าตัวสร้างของคลาสที่กำลังแก้ไขจะไม่ถูกดำเนินการในตัวออกแบบ)
Rob Parker

2
ดังนั้นหากไม่มีการไตร่ตรองif(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted)จะเป็นแนวทางที่ถูกต้อง 100% หรือไม่?
Scott Chamberlain

1
โปรดทราบว่าคำตอบ IsDesignerHostedได้รับการปรับปรุงให้มีดังนั้นตอนนี้การทดสอบสามารถเป็นเพียงLicenseUsage... if (IsDesignerHosted)วิธีทางเลือกคือการทดสอบ LicenseManager ในตัวสร้างและแคชผล
ToolmakerSteve

32

จากหน้านี้ :

( [แก้ไข 2013]แก้ไขเพื่อทำงานในคอนสตรัคเตอร์โดยใช้วิธีการจัดทำโดย @hopla)

/// <summary>
/// The DesignMode property does not correctly tell you if
/// you are in design mode.  IsDesignerHosted is a corrected
/// version of that property.
/// (see https://connect.microsoft.com/VisualStudio/feedback/details/553305
/// and http://stackoverflow.com/a/2693338/238419 )
/// </summary>
public bool IsDesignerHosted
{
    get
    {
        if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
            return true;

        Control ctrl = this;
        while (ctrl != null)
        {
            if ((ctrl.Site != null) && ctrl.Site.DesignMode)
                return true;
            ctrl = ctrl.Parent;
        }
        return false;
    }
}

ฉันได้ส่งรายงานข้อบกพร่องกับ Microsoft แล้ว ฉันสงสัยว่ามันจะไปได้ทุกที่ แต่ให้โหวตต่อไปเพราะเห็นได้ชัดว่านี่เป็นข้อบกพร่อง (ไม่ว่าจะเป็น"โดยการออกแบบ" )


29

ทำไมคุณไม่ตรวจสอบ LicenseManager.UsageMode คุณสมบัตินี้สามารถมีค่า LicenseUsageMode.Runtime หรือ LicenseUsageMode.Designtime

คุณต้องการให้รหัสทำงานในรันไทม์เท่านั้นหรือไม่ให้ใช้รหัสต่อไปนี้:

if (LicenseManager.UsageMode == LicenseUsageMode.Runtime)
{
  bla bla bla...
}

8
+1 ผมก็เคยใช้เช่นกัน สิ่งที่ผู้คนเดินทางไปคือ DesignMode จะไม่ทำงานในตัวสร้าง
Nicholas Piasecki

1
@Nicholas: นอกจากนี้ยังไม่ทำงานในการควบคุมเด็ก มันแตกง่าย
BlueRaja - Danny Pflughoeft

+1 - มันยังทำงานบนตัวควบคุมพื้นฐานที่สร้างขึ้นระหว่างการออกแบบการควบคุมที่ได้รับ
mcw

7

นี่เป็นวิธีที่ฉันใช้ในแบบฟอร์ม:

    /// <summary>
    /// Gets a value indicating whether this instance is in design mode.
    /// </summary>
    /// <value>
    ///     <c>true</c> if this instance is in design mode; otherwise, <c>false</c>.
    /// </value>
    protected bool IsDesignMode
    {
        get { return DesignMode || LicenseManager.UsageMode == LicenseUsageMode.Designtime; }
    }

วิธีนี้ผลลัพธ์จะถูกต้องแม้ว่าคุณสมบัติ DesignMode หรือ LicenseManager จะล้มเหลวก็ตาม


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

5

ฉันใช้เมธอด LicenseManager แต่แคชค่าจากตัวสร้างเพื่อใช้ตลอดอายุการใช้งานของอินสแตนซ์

public MyUserControl()
{
    InitializeComponent();
    m_IsInDesignMode = (LicenseManager.UsageMode == LicenseUsageMode.Designtime);
}

private bool m_IsInDesignMode = true;
public bool IsInDesignMode { get { return m_IsInDesignMode; } }

เวอร์ชัน VB:

Sub New()
    InitializeComponent()

    m_IsInDesignMode = (LicenseManager.UsageMode = LicenseUsageMode.Designtime)
End Sub

Private ReadOnly m_IsInDesignMode As Boolean = True
Public ReadOnly Property IsInDesignMode As Boolean
    Get
        Return m_IsInDesignMode
    End Get
End Property

1
โจนาธานฉันได้เพิ่มเวอร์ชัน VB (ทดสอบแล้ว) ให้กับคำตอบของคุณ
ToolmakerSteve

3

เราใช้รหัสนี้อย่างประสบความสำเร็จ:

public static bool IsRealDesignerMode(this Control c)
{
  if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
    return true;
  else
  {
    Control ctrl = c;

    while (ctrl != null)
    {
      if (ctrl.Site != null && ctrl.Site.DesignMode)
        return true;
      ctrl = ctrl.Parent;
    }

    return System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv";
  }
}

3

คำแนะนำของฉันคือการเพิ่มประสิทธิภาพของการ @ blueraja-danny-pflughoeft ตอบกลับ โซลูชันนี้ไม่ได้คำนวณผลลัพธ์ทุกครั้ง แต่ในครั้งแรกเท่านั้น (วัตถุไม่สามารถเปลี่ยน UsageMode จากการออกแบบเป็นรันไทม์)

private bool? m_IsDesignerHosted = null; //contains information about design mode state
/// <summary>
/// The DesignMode property does not correctly tell you if
/// you are in design mode.  IsDesignerHosted is a corrected
/// version of that property.
/// (see https://connect.microsoft.com/VisualStudio/feedback/details/553305
/// and https://stackoverflow.com/a/2693338/238419 )
/// </summary>
[Browsable(false)]
public bool IsDesignerHosted
{
    get
    {
        if (m_IsDesignerHosted.HasValue)
            return m_IsDesignerHosted.Value;
        else
        {
            if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)
            {
                m_IsDesignerHosted = true;
                return true;
            }
            Control ctrl = this;
            while (ctrl != null)
            {
                if ((ctrl.Site != null) && ctrl.Site.DesignMode)
                {
                    m_IsDesignerHosted = true;
                    return true;
                }
                ctrl = ctrl.Parent;
            }
            m_IsDesignerHosted = false;
            return false;
        }
    }
}

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

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

2

ฉันไม่เคยถูกจับได้ด้วยตัวเอง แต่คุณไม่สามารถย้อนกลับไปที่ Parent chain จากการควบคุมเพื่อดูว่า DesignMode ถูกตั้งค่าไว้ที่ใดเหนือคุณหรือไม่?


2

เนื่องจากไม่มีวิธีใดที่เชื่อถือได้ (DesignMode, LicenseManager) หรือมีประสิทธิภาพ (กระบวนการตรวจสอบซ้ำ) ฉันใช้public static bool Runtime { get; private set }ที่ระดับโปรแกรมและตั้งค่าอย่างชัดเจนภายในเมธอด Main ()


1

DesignMode เป็นคุณสมบัติส่วนตัว (จากสิ่งที่ฉันบอกได้) คำตอบคือการจัดเตรียมทรัพย์สินสาธารณะที่เปิดเผย DesignMode prop จากนั้นคุณสามารถ cascasde สำรองห่วงโซ่ของการควบคุมผู้ใช้จนกว่าคุณจะเข้าสู่การควบคุมที่ไม่ใช่ผู้ใช้หรือตัวควบคุมที่อยู่ในโหมดออกแบบ อะไรทำนองนี้ ....

  public bool RealDesignMode()
  {
     if (Parent is MyBaseUserControl)
     {
        return (DesignMode ? true : (MyBaseUserControl) Parent.RealDesignMode;
     }

     return DesignMode;
  }

โดยที่ UserControls ทั้งหมดของคุณสืบทอดมาจาก MyBaseUserControl หรือคุณสามารถใช้อินเทอร์เฟซที่แสดง "RealDeisgnMode"

โปรดทราบว่ารหัสนี้ไม่ใช่รหัสสดเพียงแค่ปิดการผูกมัด :)


1

ฉันไม่รู้ว่าคุณไม่สามารถเรียก Parent.DesignMode ได้ (และฉันได้เรียนรู้บางอย่างเกี่ยวกับ 'การป้องกัน' ใน C # ด้วย ... )

นี่คือเวอร์ชันสะท้อนแสง: (ฉันสงสัยว่าอาจมีข้อได้เปรียบด้านประสิทธิภาพในการทำให้ designModeProperty เป็นฟิลด์แบบคงที่)

static bool IsDesignMode(Control control)
{
    PropertyInfo designModeProperty = typeof(Component).
      GetProperty("DesignMode", BindingFlags.Instance | BindingFlags.NonPublic);

    while (designModeProperty != null && control != null)
    {
        if((bool)designModeProperty.GetValue(control, null))
        {
            return true;
        }
        control = control.Parent;
    }
    return false;
}

0

ฉันต้องต่อสู้กับปัญหานี้เมื่อเร็ว ๆ นี้ใน Visual Studio 2017 เมื่อใช้ UserControls แบบซ้อนกัน ฉันรวมหลายวิธีที่กล่าวถึงข้างต้นและที่อื่น ๆ จากนั้นปรับแต่งโค้ดจนกว่าฉันจะมีวิธีการขยายที่เหมาะสมซึ่งใช้งานได้ดีจนถึงตอนนี้ ดำเนินการตามลำดับของการตรวจสอบจัดเก็บผลลัพธ์ในตัวแปรบูลีนแบบคงที่ดังนั้นการตรวจสอบแต่ละครั้งจะดำเนินการได้มากที่สุดเพียงครั้งเดียวเท่านั้นในเวลารัน กระบวนการนี้อาจจะใช้งานมากเกินไป แต่เป็นการป้องกันไม่ให้โค้ดทำงานในสตูดิโอ หวังว่านี่จะช่วยใครบางคนได้

  public static class DesignTimeHelper
  {
    private static bool? _isAssemblyVisualStudio;
    private static bool? _isLicenseDesignTime;
    private static bool? _isProcessDevEnv;
    private static bool? _mIsDesignerHosted; 

    /// <summary>
    ///   Property <see cref="Form.DesignMode"/> does not correctly report if a nested <see cref="UserControl"/>
    ///   is in design mode.  InDesignMode is a corrected that property which .
    ///   (see https://connect.microsoft.com/VisualStudio/feedback/details/553305
    ///   and https://stackoverflow.com/a/2693338/238419 )
    /// </summary>
    public static bool InDesignMode(
      this Control userControl,
      string source = null)
      => IsLicenseDesignTime
         || IsProcessDevEnv
         || IsExecutingAssemblyVisualStudio
         || IsDesignerHosted(userControl);

    private static bool IsExecutingAssemblyVisualStudio
      => _isAssemblyVisualStudio
         ?? (_isAssemblyVisualStudio = Assembly
           .GetExecutingAssembly()
           .Location.Contains(value: "VisualStudio"))
         .Value;

    private static bool IsLicenseDesignTime
      => _isLicenseDesignTime
         ?? (_isLicenseDesignTime = LicenseManager.UsageMode == LicenseUsageMode.Designtime)
         .Value;

    private static bool IsDesignerHosted(
      Control control)
    {
      if (_mIsDesignerHosted.HasValue)
        return _mIsDesignerHosted.Value;

      while (control != null)
      {
        if (control.Site?.DesignMode == true)
        {
          _mIsDesignerHosted = true;
          return true;
        }

        control = control.Parent;
      }

      _mIsDesignerHosted = false;
      return false;
    }

    private static bool IsProcessDevEnv
      => _isProcessDevEnv
         ?? (_isProcessDevEnv = Process.GetCurrentProcess()
                                  .ProcessName == "devenv")
         .Value;
  }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.