入れ子になったコントロールを持つDesignMode


87

コントロールを開発するときに、DesignModeの問題に対する有用な解決策を見つけた人はいますか?

問題は、コントロールをネストすると、DesignModeが最初のレベルでのみ機能することです。2番目以下のレベルのDesignModeは常にFALSEを返します。

標準的なハックは、実行中のプロセスの名前を調べることでした。それが "DevEnv.EXE"の場合は、スタジオでなければならず、DesignModeは本当にTRUEです。

これに関する問題は、ProcessNameを検索することで、レジストリやその他の奇妙な部分を回避し、最終的にユーザーがプロセス名を表示するために必要な権限を持っていない可能性があることです。さらに、この奇妙なルートは非常に遅いです。したがって、シングルトンを使用するために追加のハックを積み重ねる必要があり、プロセス名を要求したときにエラーがスローされた場合は、DesignModeがFALSEであると想定します。

DesignModeを決定するためのすっきりとした方法が適切です。Microsoftにフレームワークの内部で修正してもらうことは、さらに良いでしょう。



8
「Microsoftにフレームワークの内部で修正してもらうことはさらに優れています」-誰かの時間の10分は、何万人もの人の時間を節約できます。バグに依存するプログラムが1つあり、それによって不便な100,000のプログラムがある場合、そのプログラムを不便にしないようにバグを保持することは意味がありません。
BlueRaja-Danny Pflughoeft 2010

こんにちは、これは2008年に投稿されました。これは修正されましたか?
ジェイク

VS 2012では、これは同じままです
ブージャー14

UserControlにカスタムデザイナーを使用している場合(たとえば、ControlDesignerから派生したクラスでテストした場合)、EnableDesignMode(subControl)を呼び出すと、サブコントロールのDesignModeプロパティが機能するように見えることに注意してください。ただし、これは問題の効果的な解決策ではありませんが、コントロールを格納するコンテナーを常に作成するわけではありません。
Protongun 2014

回答:


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

提案された3つのソリューションを試してみるために、3つのプロジェクトを含む小さなテストソリューションを作成しました。

  • TestApp(winformsアプリケーション)、
  • サブコントロール(dll)
  • SubSubControl(dll)

次に、SubSubControlをSubControlに埋め込み、それぞれをTestApp.Formに埋め込みました。

このスクリーンショットは、実行時の結果を示しています。 実行中のスクリーンショット

このスクリーンショットは、Visual Studioでフォームを開いた結果を示しています。

実行されていないスクリーンショット

結論:思わ反射せずに信頼性がある唯一のコンストラクタがLicenseUsageであり、かつ信頼性のある唯一の外部コンストラクタを(により「IsDesignedHosted」はBlueRaja以下)

PS:(私がテストしていないもの)は、以下を参照してくださいToolmakerSteveさんのコメント:「という注意IsDesignerHosted。答えは今(IsDesignerHosted)場合、テストは簡単にすることができ、... LicenseUsageを含むように更新されました別のアプローチは、コンストラクタでテストLicenseManagerの結果をキャッシュします。」


@Benjol:IsDesignerHosted(下記)はどうですか?(また、デザインタイムとランタイムが交換されていると思います。ランタイム中に何が表示されているかを確認してください)
BlueRaja-Danny Pflughoeft 2010

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

マイクロソフトにバグレポートを提出しました。私はそれがどこに行くのか疑いますが、これは明らかにバグであるため、「仕様による」かどうかにかかわらず、とにかく投票してください。


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

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、2014

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 2017年

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 replyの最適化です。このソリューションは毎回結果を計算するのではなく、最初にのみ結果を計算します(オブジェクトは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;
        }
    }
}

値をキャッシュする場合は、この複雑さを利用する必要はありません。代わりに、Jonathanの回答を使用します。これは、コンストラクターで単純なLicenseManagerテストを使用し、結果をキャッシュします。
ToolmakerSteve 2017年

このメソッドの利点は、プロパティが必要にならない場合は、LicenserManagerテストをまったく必要としないことです。
Sebastian Werk

2

自分でこれに気づいたことは一度もありませんが、コントロールからParentチェーンに戻って、DesignModeがあなたの上のどこかに設定されているかどうかを確認することはできませんか?


2

信頼できるメソッド(DesignMode、LicenseManager)も効率的なメソッド(Process、再帰的なチェック)もないためpublic static bool Runtime { get; private set }、プログラムレベルで使用し、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#でも「保護されている」ことについても学びました...)

これがリフレクティブバージョンです(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

ネストされたUserControlを使用するとき、私は最近Visual Studio 2017でこの問題と戦わなければなりませんでした。上記および他の方法をいくつか組み合わせてから、コードを調整して、これまでのところ問題なく機能する適切な拡張メソッドを作成しました。一連のチェックを実行し、結果を静的ブール変数に格納するため、各チェックは実行時に最大1回だけ実行されます。プロセスはやり過ぎかもしれませんが、コードがスタジオで実行されないようにしています。これが誰かを助けることを願っています。

  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.