DesignMode với các Điều khiển lồng nhau


87

Có ai đã tìm ra giải pháp hữu ích cho vấn đề DesignMode khi phát triển các điều khiển không?

Vấn đề là nếu bạn lồng các điều khiển thì DesignMode chỉ hoạt động ở cấp độ đầu tiên. DesignMode cấp thứ hai trở xuống sẽ luôn trả về FALSE.

Bản hack tiêu chuẩn là xem xét tên của quá trình đang chạy và nếu nó là "DevEnv.EXE" thì nó phải là studio, do đó DesignMode thực sự là TRUE.

Vấn đề là tìm kiếm ProcessName hoạt động theo cách của nó thông qua sổ đăng ký và các phần kỳ lạ khác với kết quả cuối cùng là người dùng có thể không có quyền cần thiết để xem tên quy trình. Ngoài ra tuyến đường kỳ lạ này rất chậm. Vì vậy, chúng tôi đã phải bổ sung thêm các bản hack để sử dụng một singleton và nếu lỗi được đưa ra khi yêu cầu tên tiến trình thì hãy giả sử rằng DesignMode là FALSE.

Một cách tốt để xác định DesignMode là theo thứ tự. Việc được Microsoft sửa chữa nội bộ cho khuôn khổ sẽ còn tốt hơn!



8
+1 vì "yêu cầu Microsoft sửa lỗi nội bộ cho khuôn khổ sẽ còn tốt hơn" - mười phút thời gian của ai đó sẽ tiết kiệm cho hàng chục nghìn người hàng giờ đồng hồ. Nếu có một chương trình dựa trên một lỗi và 100.000 gây bất tiện cho nó, bạn nên giữ lại lỗi đó để tránh gây bất tiện cho một chương trình đó!
BlueRaja - Danny Pflughoeft

Xin chào, điều này đã được đăng vào năm 2008. Điều này hiện đã được khắc phục chưa?
Jake

Trong VS 2012, điều này vẫn giữ nguyên hiện tại
Boogier

1
Lưu ý rằng nếu sử dụng trình thiết kế tùy chỉnh cho UserControl (ví dụ: tôi đã thử nghiệm với một lớp dẫn xuất từ ​​ControlDesigner), thì việc gọi EnableDesignMode (subControl) dường như làm cho thuộc tính DesignMode của điều khiển phụ hoạt động. Tuy nhiên, đây không phải là một giải pháp hiệu quả cho vấn đề vì chúng tôi không phải lúc nào cũng tạo ra vùng chứa chứa quyền kiểm soát của chúng tôi.
Protongun

Câu trả lời:


80

Xem lại câu hỏi này, bây giờ tôi đã 'phát hiện ra' 5 cách khác nhau để thực hiện việc này, như sau:

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

Để thử và nắm bắt được ba giải pháp được đề xuất, tôi đã tạo một giải pháp thử nghiệm nhỏ - với ba dự án:

  • TestApp (ứng dụng winforms),
  • SubControl (dll)
  • SubSubControl (dll)

Sau đó, tôi đã nhúng SubSubControl vào SubControl, sau đó nhúng một SubSubControl vào trong TestApp.Form.

Ảnh chụp màn hình này hiển thị kết quả khi chạy. Ảnh chụp màn hình chạy

Ảnh chụp màn hình này hiển thị kết quả với biểu mẫu mở trong Visual Studio:

Ảnh chụp màn hình không chạy

Kết luận: Có vẻ như nếu không có sự phản ánh thì duy nhất đáng tin cậy bên trong hàm tạo là LicenseUsage và duy nhất đáng tin cậy bên ngoài hàm tạo là 'IsDesignedHosted' (bởi BlueRaja bên dưới)

Tái bút: Xem nhận xét của ToolmakerSteve bên dưới (mà tôi chưa thử nghiệm): "Lưu ý rằng câu trả lời IsDesignerHosted đã được cập nhật để bao gồm LicenseUsage ..., vì vậy bây giờ kiểm tra có thể chỉ đơn giản là nếu (IsDesignerHosted). Một cách tiếp cận thay thế là kiểm tra LicenseManager trong hàm tạo và lưu kết quả vào bộ nhớ cache . "


@Benjol: Còn IsDesignerHosted (bên dưới) thì sao? (Ngoài ra, tôi nghĩ rằng bạn có thời gian thiết kế và thời gian chạy hoán đổi, kiểm tra những gì nó nói trong thời gian chạy)
BlueRaja - Danny Pflughoeft

@BlueRaja, tôi vẫn phải có dự án mà nằm xung quanh nơi nào đó trên đĩa, có lẽ tôi nên đăng nó ở đâu đó ...
Benjol

1
+1 để làm rõ bằng một thử nghiệm thực nghiệm. @Benjol, Nếu bạn có cơ hội truy cập lại điều này, bạn có thể thêm trường hợp cho các giá trị trong chính biểu mẫu vì các điều khiển con có thể được xử lý khác với lớp thực sự đang được chỉnh sửa trong trình thiết kế. (Lưu ý rằng hàm tạo của lớp đang được chỉnh sửa không được thực thi trong trình thiết kế.)
Rob Parker

2
Vì vậy, không có phản ánh if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted)sẽ là cách tiếp cận đúng 100%?
Scott Chamberlain

1
Lưu ý rằng câu trả lời IsDesignerHosted đã được cập nhật để bao gồm LicenseUsage..., vì vậy bây giờ bài kiểm tra có thể chỉ đơn giản là if (IsDesignerHosted). Một cách tiếp cận thay thế là kiểm tra LicenseManager trong hàm tạo và lưu kết quả vào bộ nhớ cache .
Thợ làm công cụ Đến

32

Từ trang này :

( [Chỉnh sửa 2013] Đã chỉnh sửa để hoạt động trong trình tạo, sử dụng phương pháp được cung cấp bởi @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;
    }
}

Tôi đã gửi báo cáo lỗi với Microsoft; Tôi nghi ngờ nó sẽ đi đến đâu, nhưng dù sao hãy bình chọn nó, vì đây rõ ràng là một lỗi (cho dù đó có phải là "do thiết kế" hay không ).


29

Tại sao bạn không kiểm tra LicenseManager.UsageMode. Thuộc tính này có thể có các giá trị LicenseUsageMode.Runtime hoặc LicenseUsageMode.Designtime.

Bạn muốn mã chỉ chạy trong thời gian chạy, hãy sử dụng mã sau:

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

8
+1 Tôi cũng đã sử dụng cái này. Điều khiến mọi người quan tâm là DesignMode sẽ không hoạt động trong một hàm tạo.
Nicholas Piasecki

1
@Nicholas: Nó cũng không hoạt động trong kiểm soát trẻ em. Nó chỉ đơn giản là bị hỏng.
BlueRaja - Danny Pflughoeft

+1 - nó cũng hoạt động trên các điều khiển cơ sở đang được xây dựng trong quá trình thiết kế điều khiển dẫn xuất.
mcw

7

Đây là phương pháp tôi sử dụng bên trong các biểu mẫu:

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

Bằng cách này, kết quả sẽ chính xác, ngay cả khi một trong hai thuộc tính DesignMode hoặc LicenseManager không thành công.


1
Có, điều này sẽ hoạt động trong các biểu mẫu như bạn nói. Nhưng tôi muốn chỉ ra rằng điều đó không hoạt động bên ngoài hàm tạo trong điều khiển người dùng cháu.
Anlo

5

Tôi sử dụng phương thức LicenseManager, nhưng lưu giá trị từ phương thức khởi tạo để sử dụng trong suốt thời gian tồn tại của phiên bản.

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

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

Phiên bản 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
Jonathan, tôi đã thêm phiên bản VB (đã thử nghiệm) vào câu trả lời của bạn.
ToolmakerSteve

3

Chúng tôi sử dụng mã này thành công:

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

Đề xuất của tôi là tối ưu hóa câu trả lời @ blueraja-danny-pflughoeft . Giải pháp này không tính toán kết quả mọi lúc mà chỉ tính toán ở lần đầu tiên (một đối tượng không thể thay đổi Chế độ sử dụng từ thiết kế sang thời gian chạy)

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

Nếu bạn định lưu vào bộ nhớ cache một giá trị, không có lý do gì để đi đến sự phức tạp này. Thay vào đó, hãy sử dụng câu trả lời của Jonathan , sử dụng thử nghiệm LicenseManager đơn giản trong phương thức khởi tạo , lưu vào bộ nhớ đệm kết quả.
Thợ làm công cụ Đến

Tôi nghĩ rằng lợi ích của phương pháp này là nó thậm chí không cần kiểm tra LicenserManager, nếu thuộc tính không bao giờ cần thiết trong một số trường hợp.
Sebastian Werk

2

Bản thân tôi chưa bao giờ gặp phải vấn đề này, nhưng bạn không thể sao lưu chuỗi Parent từ điều khiển để xem liệu DesignMode có được đặt ở bất kỳ đâu phía trên bạn không?


2

Vì không có phương pháp nào đáng tin cậy (DesignMode, LicenseManager) hoặc hiệu quả (Quy trình, kiểm tra đệ quy) nên tôi đang sử dụng một public static bool Runtime { get; private set }cấp độ chương trình và thiết lập rõ ràng nó bên trong phương thức Main ().


1

DesignMode là một tài sản riêng (theo những gì tôi có thể nói). Câu trả lời là cung cấp một thuộc tính công khai hiển thị đề xuất DesignMode. Sau đó, bạn có thể sao lưu chuỗi điều khiển của người dùng cho đến khi bạn gặp phải điều khiển không phải người dùng hoặc điều khiển đang ở chế độ thiết kế. Chuyện như thế này….

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

     return DesignMode;
  }

Nơi tất cả UserControl của bạn kế thừa từ MyBaseUserControl. Ngoài ra, bạn có thể triển khai một giao diện hiển thị "RealDeisgnMode".

Xin lưu ý rằng mã này không phải là mã trực tiếp, chỉ là suy nghĩ ngoài vòng bít. :)


1

Tôi đã không nhận ra rằng bạn không thể gọi Parent.DesignMode (và tôi cũng đã học được điều gì đó về 'protected' trong C # ...)

Đây là một phiên bản phản chiếu: (Tôi nghi ngờ có thể có một lợi thế về hiệu suất khi biến designModeProperty thành một trường tĩnh)

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

Tôi đã phải giải quyết vấn đề này gần đây trong Visual Studio 2017 khi sử dụng UserControls lồng nhau. Tôi kết hợp một số cách tiếp cận được đề cập ở trên và các nơi khác, sau đó chỉnh sửa mã cho đến khi tôi có một phương pháp mở rộng phù hợp hoạt động được chấp nhận cho đến nay. Nó thực hiện một chuỗi kiểm tra, lưu trữ kết quả trong các biến boolean tĩnh, vì vậy mỗi lần kiểm tra chỉ được thực hiện nhiều nhất một lần duy nhất tại thời điểm chạy. Quá trình này có thể quá mức cần thiết, nhưng nó đang giữ cho mã không thực thi trong studio. Hy vọng điều này sẽ giúp ai đó.

  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;
  }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.