DesignMode dengan Kontrol bersarang

87

Adakah yang menemukan solusi yang berguna untuk masalah DesignMode saat mengembangkan kontrol?

Masalahnya adalah jika Anda menumpuk kontrol maka DesignMode hanya berfungsi untuk tingkat pertama. DesignMode tingkat kedua dan yang lebih rendah akan selalu mengembalikan FALSE.

Peretasan standar adalah untuk melihat nama proses yang sedang berjalan dan jika itu adalah "DevEnv.EXE" maka itu harus studio sehingga DesignMode benar-benar BENAR.

Masalah dengan yang mencari ProcessName bekerja melalui registri dan bagian aneh lainnya dengan hasil akhir bahwa pengguna mungkin tidak memiliki hak yang diperlukan untuk melihat nama proses. Selain itu, rute aneh ini sangat lambat. Jadi kami harus menumpuk peretasan tambahan untuk menggunakan singleton dan jika kesalahan muncul saat meminta nama proses, maka asumsikan bahwa DesignMode adalah FALSE.

Cara bersih yang bagus untuk menentukan DesignMode sudah beres. Sebenarnya meminta Microsoft untuk memperbaikinya secara internal ke kerangka akan lebih baik!

John Dyer
sumber
8
1 untuk "membuat Microsoft memperbaikinya secara internal ke kerangka kerja akan jauh lebih baik" - sepuluh menit waktu seseorang akan menghemat puluhan ribu orang berjam-jam. Jika ada satu program yang mengandalkan bug dan 100.000 yang merasa tidak nyaman karenanya, tidak masuk akal untuk menyimpan bug untuk menghindari gangguan pada satu program!
BlueRaja - Danny Pflughoeft
Hai, ini telah diposting pada tahun 2008. Apakah sekarang sudah diperbaiki?
Jake
Di VS 2012 ini tetap sama sekarang
Boogier
1
Perhatikan bahwa jika menggunakan desainer khusus untuk UserControl (misalnya, saya telah menguji dengan kelas yang berasal dari ControlDesigner), kemudian memanggil EnableDesignMode (subControl) tampaknya membuat properti DesignMode dari subkontrol berfungsi. Ini bukan solusi yang efektif untuk masalah namun karena kami tidak selalu membuat wadah yang menampung kendali kami.
Protongun

Jawaban:

81

Meninjau kembali pertanyaan ini, saya sekarang telah 'menemukan' 5 cara berbeda untuk melakukan ini, yaitu sebagai berikut:

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"))
}

Untuk mencoba dan memahami tiga solusi yang diusulkan, saya membuat sedikit solusi uji - dengan tiga proyek:

  • TestApp (aplikasi winforms),
  • Subkontrol (dll)
  • SubSubControl (dll)

Saya kemudian menyematkan SubSubControl di SubControl, lalu salah satu dari masing-masing di TestApp.Form.

Tangkapan layar ini menunjukkan hasil saat dijalankan. Tangkapan layar saat berjalan

Tangkapan layar ini menunjukkan hasil dengan formulir terbuka di Visual Studio:

Tangkapan layar tidak berjalan

Kesimpulan: Tampaknya tanpa refleksi satu-satunya yang dapat diandalkan dalam konstruktor adalah LicenseUsage, dan satu-satunya yang dapat diandalkan di luar konstruktor adalah 'IsDesignedHosted' (oleh BlueRaja di bawah)

NB: Lihat komentar ToolmakerSteve di bawah ini (yang belum saya uji): "Perhatikan bahwa jawaban IsDesignerHosted telah diperbarui untuk menyertakan LicenseUsage ..., jadi sekarang pengujiannya dapat berupa if (IsDesignerHosted). Pendekatan alternatif adalah menguji LicenseManager di konstruktor dan menyimpan hasilnya ke dalam cache . "

Benjol
sumber
@Benjol: Bagaimana dengan IsDesignerHosted (di bawah)? (Juga, saya pikir Anda memiliki waktu desain dan runtime yang ditukar, periksa apa yang dikatakannya selama runtime)
BlueRaja - Danny Pflughoeft
@BlueRaja, saya pasti masih memiliki proyek yang tergeletak di suatu tempat di disk, mungkin saya harus mempostingnya di suatu tempat ...
Benjol
1
+1 untuk klarifikasi dengan eksperimen empiris. @Benjol, Jika Anda mendapat kesempatan untuk mengunjungi kembali ini, Anda dapat menambahkan kasus untuk nilai di formulir itu sendiri karena kontrol anak mungkin diperlakukan berbeda dari kelas yang sebenarnya sedang diedit di desainer. (Perhatikan bahwa konstruktor kelas yang sedang diedit tidak dieksekusi di desainer.)
Rob Parker
2
Jadi, tanpa refleksi if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted)akan menjadi pendekatan yang 100% benar?
Scott Chamberlain
1
Perhatikan bahwa jawaban IsDesignerHosted telah diperbarui untuk disertakan LicenseUsage..., jadi sekarang tesnya bisa jadi if (IsDesignerHosted). Pendekatan alternatif adalah menguji LicenseManager di konstruktor dan menyimpan hasilnya ke dalam cache .
ToolmakerSteve
32

Dari halaman ini :

( [Sunting 2013] Diedit untuk bekerja di konstruktor, menggunakan metode yang disediakan oleh @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;
    }
}

Saya telah mengirimkan laporan bug dengan Microsoft; Saya ragu itu akan pergi ke mana pun, tetapi pilihlah itu, karena ini jelas bug (apakah itu "berdasarkan desain" atau tidak ).

BlueRaja - Danny Pflughoeft
sumber
29

Mengapa Anda tidak memeriksa LicenseManager.UsageMode. Properti ini dapat memiliki nilai LicenseUsageMode.Runtime atau LicenseUsageMode.Designtime.

Apakah Anda ingin kode hanya dijalankan di runtime, gunakan kode berikut:

if (LicenseManager.UsageMode == LicenseUsageMode.Runtime)
{
  bla bla bla...
}
hopla
sumber
8
+1 Saya telah menggunakan ini juga. Apa yang membuat orang tersandung adalah bahwa DesignMode tidak akan berfungsi di konstruktor.
Nicholas Piasecki
1
@Nicholas: Ini juga tidak berfungsi di kontrol anak. Itu hanya rusak.
BlueRaja - Danny Pflughoeft
+1 - ini juga bekerja pada kontrol dasar yang sedang dibangun selama desain kontrol turunan.
mcw
7

Ini adalah metode yang saya gunakan di dalam formulir:

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

Dengan cara ini, hasilnya akan benar, meskipun salah satu properti DesignMode atau LicenseManager gagal.

husayt
sumber
1
Ya, ini akan berfungsi dalam bentuk seperti yang Anda katakan. Tapi saya ingin menunjukkan bahwa itu tidak bekerja di luar konstruktor dalam kontrol pengguna cucu.
Anlo
5

Saya menggunakan metode LicenseManager, tetapi menyimpan nilai dari konstruktor untuk digunakan selama masa pakai instance.

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

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

Versi 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
Jonathan
sumber
1
Jonathan, saya telah menambahkan versi VB (teruji) ke jawaban Anda.
ToolmakerSteve
3

Kami menggunakan kode ini dengan sukses:

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";
  }
}
juFo
sumber
3

Saran saya adalah optimalisasi @ blueraja-danny-pflughoeft balasan . Solusi ini tidak menghitung hasil setiap kali tetapi hanya pertama kali (objek tidak dapat mengubah UsageMode dari desain ke runtime)

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;
        }
    }
}
pengguna2785562
sumber
Jika Anda akan meng-cache nilai, tidak ada alasan untuk pergi ke kompleksitas ini. Sebagai gantinya, gunakan jawaban Jonathan , yang menggunakan pengujian LicenseManager sederhana di konstruktor , menyimpan hasilnya ke dalam cache.
ToolmakerSteve
Menurut saya manfaat dari metode ini adalah, metode ini bahkan tidak memerlukan pengujian LicenserManager sama sekali, jika properti tidak pernah diperlukan dalam beberapa kasus.
Sebastian Werk
2

Saya tidak pernah terperangkap oleh ini sendiri, tetapi tidak bisakah Anda berjalan kembali ke rantai Induk dari kontrol untuk melihat apakah DesignMode disetel di mana saja di atas Anda?

Will Dean
sumber
2

Karena tidak ada metode yang dapat diandalkan (DesignMode, LicenseManager) atau efisien (Proses, pemeriksaan rekursif), saya menggunakan public static bool Runtime { get; private set }pada tingkat program dan secara eksplisit mengaturnya di dalam metode Main ().

Boris B.
sumber
1

DesignMode adalah milik pribadi (dari apa yang saya tahu). Jawabannya adalah menyediakan properti publik yang mengekspos prop DesignMode. Kemudian Anda dapat membuat cadangan rantai kontrol pengguna hingga Anda menjalankan kontrol non-pengguna atau kontrol yang ada dalam mode desain. Sesuatu seperti ini....

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

     return DesignMode;
  }

Di mana semua UserControl Anda diwarisi dari MyBaseUserControl. Atau Anda dapat menerapkan antarmuka yang mengekspos "RealDeisgnMode".

Harap dicatat bahwa kode ini bukanlah kode langsung, hanya sekedar renungan. :)

Craig
sumber
1

Saya tidak menyadari bahwa Anda tidak dapat memanggil Parent.DesignMode (dan saya telah belajar sesuatu tentang 'dilindungi' di C # juga ...)

Berikut adalah versi reflektifnya: (Saya menduga mungkin ada keuntungan kinerja untuk menjadikan designModeProperty bidang statis)

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;
}
Will Dean
sumber
0

Saya harus mengatasi masalah ini baru-baru ini di Visual Studio 2017 saat menggunakan UserControls bersarang. Saya menggabungkan beberapa pendekatan yang disebutkan di atas dan di tempat lain, kemudian mengubah kode sampai saya memiliki metode ekstensi yang layak yang berfungsi dengan baik sejauh ini. Ini melakukan urutan pemeriksaan, menyimpan hasilnya dalam variabel boolean statis sehingga setiap pemeriksaan hanya dilakukan paling banyak hanya sekali pada waktu proses. Prosesnya mungkin berlebihan, tetapi mencegah kode dijalankan di studio. Semoga ini bisa membantu seseorang.

  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;
  }
RB Davidson
sumber