중첩 된 컨트롤이있는 DesignMode


87

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

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

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

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

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



8
"Microsoft가 내부적으로 프레임 워크에 수정하도록하는 것이 훨씬 더 좋을 것입니다."라는 +1-누군가의 시간 10 분이면 수만 시간을 절약 할 수 있습니다. 버그에 의존하는 프로그램이 하나 있고 그것에 의해 불편한 100,000 개의 프로그램이 있다면, 하나의 프로그램을 불편하게하지 않기 위해 버그를 유지하는 것은 이치에 맞지 않습니다!
BlueRaja-Danny Pflughoeft 2010

안녕하세요, 2008 년에 게시되었습니다. 이제 수정 되었나요?
Jake

VS 2012 년이 이제 동일하게 유지
Boogier

1
UserControl에 대해 사용자 지정 디자이너를 사용하는 경우 (예 : ControlDesigner에서 파생 된 클래스로 테스트했습니다) EnableDesignMode (subControl)를 호출하면 하위 컨트롤의 DesignMode 속성이 작동하는 것처럼 보입니다. 그러나 우리가 제어 할 수있는 컨테이너를 항상 작성하지는 않기 때문에 이것은 문제에 대한 효과적인 해결책은 아닙니다.
Protongun

답변:


80

이 질문을 다시 살펴보면서 나는 다음과 같은 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를 테스트 하는 것입니다. 결과를 캐시합니다 . "


@Benjol : IsDesignerHosted (아래)는 어떻습니까? (또한 디자인 타임과 런타임을
바꿨다고

@BlueRaja, 나는 여전히 ... 어쩌면 내가 어딘가에 게시해야 해당 프로젝트 어딘가에 디스크에 주위에 거짓말이 있어야합니다
Benjol

1
경험적 실험에 의한 설명을 위해 +1. @Benjol, 이것을 다시 방문 할 기회가 생기면 자식 컨트롤이 디자이너에서 실제로 편집되는 클래스와 다르게 취급 될 수 있으므로 양식 자체의 값에 대한 케이스를 추가 할 수 있습니다. (편집중인 클래스의 생성자는 디자이너에서 실행되지 않습니다.)
Rob Parker

2
그렇다면 반성없이 if(LicenseUseage == LicenseUsageMode.Designtime || IsDesignerHosted)100 % 올바른 접근 방식이 될까요?
Scott Chamberlain

1
참고 것을 IsDesignerHosted 응답을 포함하도록 업데이트되었습니다 LicenseUsage..., 이제 테스트는 간단하게 할 수 있습니다 if (IsDesignerHosted). 다른 방법은 생성자에서 LicenseManager를 테스트하고 결과를 캐시하는 것 입니다.
ToolmakerSteve

32

에서 이 페이지 :

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


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 2010

+1-파생 컨트롤을 디자인하는 동안 구성되는 기본 컨트롤에서도 작동합니다.
mcw 2011

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
Jonathan, 귀하의 답변에 (테스트 된) 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 테스트 사용하여 결과를 캐싱하는 Jonathan의 답변 을 사용하십시오 .
ToolmakerSteve

이 방법의 장점은 어떤 경우에 속성이 필요하지 않은 경우 LicenserManager 테스트가 전혀 필요하지 않다는 것입니다.
Sebastian Werk

2

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


2

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


1

DesignMode는 개인 속성입니다 (내가 말할 수있는 것). 대답은 DesignMode 소품을 노출하는 공용 속성을 제공하는 것입니다. 그런 다음 비 사용자 컨트롤 또는 디자인 모드에있는 컨트롤에 도달 할 때까지 사용자 컨트롤 체인을 계단식으로 백업 할 수 있습니다. 이 같은....

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

     return DesignMode;
  }

모든 UserControl이 MyBaseUserControl에서 상속되는 곳. 또는 "RealDeisgnMode"를 노출하는 인터페이스를 구현할 수 있습니다.

이 코드는 라이브 코드가 아니고 커프스에서 벗어나는 것입니다. :)


1

Parent.DesignMode를 호출 할 수 없다는 것을 깨닫지 못했습니다 (그리고 C #에서도 'protected'에 대해 배웠습니다 ...)

다음은 반사 버전입니다. (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.