program tip

중첩 된 컨트롤이있는 DesignMode

radiobox 2020. 9. 14. 08:16
반응형

중첩 된 컨트롤이있는 DesignMode


컨트롤을 개발할 때 DesignMode 문제에 대한 유용한 솔루션을 찾은 사람이 있습니까?

문제는 컨트롤을 중첩하면 DesignMode가 첫 번째 수준에서만 작동한다는 것입니다. 두 번째 및 하위 수준 DesignMode는 항상 FALSE를 반환합니다.

표준 해킹은 실행중인 프로세스의 이름을 확인하는 것이었고 "DevEnv.EXE"이면 스튜디오 여야하므로 DesignMode는 실제로 TRUE입니다.

ProcessName을 찾는 문제는 레지스트리 및 기타 이상한 부분을 통해 작동하며 사용자가 프로세스 이름을 보는 데 필요한 권한이 없을 수도 있습니다. 또한이 이상한 경로는 매우 느립니다. 그래서 우리는 싱글 톤을 사용하기 위해 추가 핵을 쌓아야했고 프로세스 이름을 요청할 때 오류가 발생하면 DesignMode가 FALSE라고 가정합니다.

DesignMode를 결정하는 좋은 방법은 순서입니다. Microsoft가 내부적으로 프레임 워크에 수정하도록하는 것이 훨씬 더 좋을 것입니다!


이 질문을 다시 살펴보면서 나는 다음과 같은 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)

그런 다음 SubControl에 SubSubControl을 삽입 한 다음 TestApp.Form에 각각 하나씩 삽입했습니다.

이 스크린 샷은 실행시 결과를 보여줍니다. 달리기 스크린 샷

이 스크린 샷은 Visual Studio에서 열린 양식의 결과를 보여줍니다.

실행되지 않는 스크린 샷

결론 : 리플렉션없이 생성자 에서 신뢰할 수있는 유일한 것은 LicenseUsage이고 생성자 외부 에서 신뢰할 수있는 유일한 것은 'IsDesignedHosted'( 아래 BlueRaja 제공 ) 인 것으로 보입니다.

추신 : 아래 ToolmakerSteve의 의견을 참조하십시오 (테스트하지 않았습니다) : " IsDesignerHosted 답변이 LicenseUsage를 포함하도록 업데이트되었으므로 이제 테스트는 단순히 (IsDesignerHosted) 일 수 있습니다. 대체 접근 방식은 생성자에서 LicenseManager를 테스트 하는 것입니다. 결과를 캐시합니다 . "


에서 이 페이지 :

( [Edit 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에 버그 보고서제출 했습니다 . 나는이 (그것의 여부 버그 분명히으로는, 어디든 갈하지만 어쨌든 그것을 투표 의심 "디자인은" ).


LicenseManager.UsageMode를 확인하지 않는 이유는 무엇입니까? 이 속성은 LicenseUsageMode.Runtime 또는 LicenseUsageMode.Designtime 값을 가질 수 있습니다.

코드를 런타임에서만 실행하려면 다음 코드를 사용하십시오.

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

이것은 양식 내부에서 사용하는 방법입니다.

    /// <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 속성 중 하나가 실패하더라도 결과가 정확합니다.


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

이 코드를 성공적으로 사용합니다.

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

내 제안은 @ 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;
        }
    }
}

나는 이것에 의해 잡힌 적이 없지만 DesignMode가 당신 위에 설정되어 있는지 확인하기 위해 컨트롤에서 부모 체인으로 돌아갈 수는 없습니까?


신뢰할 수있는 (DesignMode, LicenseManager) 또는 효율적인 (Process, recursive checks) 메서드가 없기 때문에 public static bool Runtime { get; private set }at 프로그램 수준을 사용 하고 Main () 메서드 내부에서 명시 적으로 설정합니다.


DesignMode is a private property (from what I can tell). The answer is to provide a public property that exposes the DesignMode prop. Then you can cascasde back up the chain of user controls until you run into a non-user control or a control that is in design mode. Something like this....

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

     return DesignMode;
  }

Where all your UserControls inherit from MyBaseUserControl. Alternatively you could implement an interface that exposes the "RealDeisgnMode".

Please note this code is not live code, just off the cuff musings. :)


I hadn't realised that you can't call Parent.DesignMode (and I have learned something about 'protected' in C# too...)

Here's a reflective version: (I suspect there might be a performance advantage to making designModeProperty a static field)

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

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

참고 URL : https://stackoverflow.com/questions/34664/designmode-with-nested-controls

반응형