システムフォントとdpi設定に自動スケーリングするWinFormsコードを記述する方法


143

イントロ:「WinFormsはDPI /フォント設定に適切に自動スケーリングされないため、WPFに切り替えます」というコメントがたくさんあります。ただし、これは.NET 1.1に基づいていると思います。実際には、.NET 2.0で自動スケーリングを実装するのにかなり優れていたようです。少なくともこれまでの調査とテストに基づいています。ただし、ご存知の方がいらっしゃれば、ぜひご連絡ください。(WPFに切り替えるべきだと言って迷惑を掛けないでください...これは現時点ではオプションではありません。)

質問:

  • WinFormsの何が適切に自動スケーリングされないので、避ける必要がありますか?

  • WinFormsコードを適切に自動スケーリングするように記述する場合、プログラマーはどの設計ガイドラインに従う必要がありますか?

これまでに確認した設計ガイドライン:

以下のコミュニティwikiの回答を参照してください。

それらのいずれかが正しくないか、不十分ですか?他に採用すべきガイドラインはありますか?回避する必要がある他のパターンはありますか?これに関する他のガイダンスは非常にありがたいです。

回答:


127

適切にスケーリングをサポートしないコントロール:

  • LabelAutoSize = Falseし、Font継承されました。Fontプロパティウィンドウで太字で表示されるように、コントロールを明示的に設定します。
  • ListView列幅は拡大縮小されません。ScaleControl代わりにフォームをオーバーライドしてください。この回答を見る
  • SplitContainerさんPanel1MinSizePanel2MinSizeおよびSplitterDistanceプロパティ
  • TextBoxMultiLine = Trueし、Font継承されました。Fontプロパティウィンドウで太字で表示されるように、コントロールを明示的に設定します。
  • ToolStripButtonの画像。フォームのコンストラクタで:

    • セットする ToolStrip.AutoSize = False
    • およびToolStrip.ImageScalingSizeに従って設定CreateGraphics.DpiX.DpiY
    • ToolStrip.AutoSize = True必要に応じて設定します。

    そのAutoSizeままにしておくこともできますが、Trueこれらの手順がないとサイズ変更に失敗することがあります。でその変更をせずに作品の.NET Framework 4.5.2EnableWindowsFormsHighDpiAutoResizing

  • TreeViewさんの画像。およびImageList.ImageSizeに従って設定CreateGraphics.DpiX.DpiYます。の場合StateImageList.NET Framework 4.5.1およびEnableWindowsFormsHighDpiAutoResizing
  • Formのサイズ。Form作成後に手動で固定サイズを拡大縮小します。

設計ガイドライン:

  • すべてのContainerControlsを同じに設定する必要がありますAutoScaleMode = Font。(フォントはDPIの変更とシステムフォントサイズ設定の変更の両方を処理します。DPIはDPIの変更のみを処理し、システムフォントサイズ設定の変更は処理しません。)

  • AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);96dpi(次の箇条書きを参照)およびMS Sans Serifのデフォルトのフォント(箇条書き2つを参照)を想定して、すべてのContainerControlsも同じに設定する必要があります。これは、デザイナーを開いたDPIに基づいてデザイナーによって自動追加されますが、最も古いデザイナーファイルの多くにはありませんでした。おそらく、Visual Studio .NET(VS 2005より前のバージョン)では適切に追加されていませんでした。

  • すべてのデザイナー作業を96dpiで実行します(120dpiに切り替えることもできますが、インターネット上の知恵は96dpiに固執することを示しています。実験はそこにあるためです。設計により、それが変更されるAutoScaleDimensions行だけなので問題ではありません。デザイナーが挿入)。Visual Studioを高解像度ディスプレイ上の仮想96dpiで実行するように設定するには、その.exeファイルを見つけて右クリックしてプロパティを編集し、[互換性]で[高DPIスケーリング動作を上書きする。システムによって実行されるスケーリング]を選択します。

  • MS Sans Serif以外のアプリケーション全体のデフォルトフォントが必要な場合は、リーフコントロールまたは最も基本的なフォームのコンストラクターでのみ、コンテナーレベルでフォントを設定しないでください。(コンテナにフォントを設定すると、AutoScaleModeおよびAutoScaleDimensions設定の設定の後にアルファベット順になるため、そのコンテナの自動スケーリングがオフになっているように見えます。)最も基本的なフォームのコンストラクタでフォントを変更すると、 AutoScaleDimensionsが6x13とは異なる方法で計算する。特に、Segoe UI(Win 10のデフォルトフォント)に変更すると、7x15になります... .designerファイルのすべての寸法を再計算できるように、デザイナーのすべてのフォームをタッチする必要があります。AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);

  • アンカーを使用しRightたりBottom、UserControlにアンカーしたりしないでください。その配置は自動スケーリングされません。代わりに、パネルまたは他のコンテナーをユーザーコントロールにドロップし、他のコントロールをそのパネルにアンカーします。パネルの使用ドックを持っているRightBottomまたはFillあなたのユーザーコントロールインチ

  • ResumeLayoutの最後にInitializeComponent呼び出されたときに、コントロールリスト内のコントロールのみが自動スケーリングされます...コントロールを動的に追加する場合、追加するSuspendLayout(); AutoScaleDimensions = new SizeF(6F, 13F); AutoScaleMode = AutoScaleMode.Font; ResumeLayout();前にそのコントロールをオンにする必要があります。また、位置も調整する必要があります。あなたは次のようにドックモードやレイアウトマネージャを使用していない場合FlowLayoutPanelまたはTableLayoutPanel

  • から派生した基本クラスContainerControlは、にAutoScaleMode設定したままにする必要がInheritあります(classに設定されたデフォルト値ContainerControl。ただし、デザイナーによって設定されたデフォルト値ではありません)。それを他の値に設定し、派生クラスがそれをFontに設定しようとする場合(必要に応じて)、それを設定するFontと、デザイナーのの設定が消去され、AutoScaleDimensions実際に自動スケーリングが無効になります。(このガイドラインと前のガイドラインを組み合わせると、デザイナーで基本クラスをインスタンス化することはできません。すべてのクラスは、基本クラスまたはリーフクラスとして設計する必要があります。)

  • Form.MaxSizeDesigner では静的に/ を使用しないでください。MinSizeそして、MaxSizeフォーム上の他のすべてな限り拡張できません。したがって、すべての作業を96dpiで行う場合、DPI MinSizeが高くなっても問題は発生しませんが、期待したほど制限的ではないかもしれませんがMaxSize、サイズのスケーリングが制限され、問題が発生する可能性があります。必要に応じてMinSize == Size == MaxSize、デザイナでこれを行わないでください。コンストラクタまたはOnLoadオーバーライドでMinSizeそれMaxSizeを行ってください。両方と適切にスケーリングされたサイズに設定してください。

  • 特定のすべてのコントロール、PanelまたはContainerアンカーまたはドッキングを使用する必要があります。それらを混合すると、それによって行われる自動スケーリングPanelは、微妙な奇妙な方法で誤動作することがよくあります。

  • 自動スケーリングを実行すると、フォーム全体をスケーリングしようとします...ただし、そのプロセスで画面サイズの上限に達した場合、それはハードリミットであり、それが原因で失敗する可能性があります(クリップ)スケーリング。したがって、100%/ 96dpiのデザイナのすべてのフォームのサイズが1024x720(1080p画面の150%または4K画面のWindows推奨値である300%に相当)以下であることを確認する必要があります。しかし、巨大なWin10タイトル/キャプションバーを差し引く必要があります... 1000x680の最大サイズのように...デザイナーでは994x642 ClientSizeのようになります。(つまり、ClientSizeでFindAll Referencesを実行して違反者を見つけることができます。)


NumericUpDownまた、Margin適切にスケーリングされません。マージンは2倍に拡大されているようです。1回縮小すると、見栄えが良くなります。
ygoe 2016年

AutoScaleMode = Font非常に大きなフォントを使用していて、Ubuntuでを使用しているユーザーにはうまく機能しません。私たちが好むAutoScaleMode = DPI
KindDragon 2016年

> MultiLine = TrueのTextBoxとフォントが継承されます。一日中狂っています-それが修正でした!本当にありがとう!ちなみに、同じ修正はListBoxコントロールの修正でもあります。:D
neminem

私にとって、継承されたフォントを含むリストボックスは、適切に拡大縮小されません。明示的に設定した後で行います。(.NET 4.7)
PulseJet 2017年


27

私の経験は、現在のトップ投票の回答とはかなり異なっています。.NETフレームワークコードをステップ実行し、参照ソースコードを熟読することで、自動スケーリングが機能するためのすべてが整っており、どこかでそれをめちゃくちゃにする微妙な問題しかないと結論付けました。これは事実であることがわかりました。

適切にリフロー可能/自動サイズのレイアウトを作成すると、ほとんどすべてがVisual Studioで使用されるデフォルト設定(つまり、親フォームでAutoSizeMode = Font、その他すべてで継承)で自動的に正しく機能します。

唯一の問題は、デザイナーのフォームでFontプロパティを設定した場合です。生成されたコードは、割り当てをアルファベット順に並べ替えます。つまり、AutoScaleDimensions に割り当てられFontます。残念ながら、これはWinFormsの自動スケーリングロジックを完全に壊します。

修正は簡単ですが。Fontデザイナーでプロパティを設定しない(フォームコンストラクターで設定する)か、これらの割り当てを手動で並べ替えます(ただし、デザイナーでフォームを編集するたびにこれを実行する必要があります)。ほんの少しの手間で、ほぼ完璧で完全に自動スケーリングされた出来上がり。フォームのサイズも正しくスケーリングされます。


既知の問題が発生したときに、ここにリストします。


1
Fontデザイナで設定しないこと:考えが浮かびます:先に進んでデザイナでフォントを設定し、目的のフォントでデザインできるようにします。次に、コンストラクターで、レイアウト後に、そのフォントプロパティを読み取り、同じ値を再度設定しますか?または、レイアウトをもう一度やり直してもらうだけですか?[警告:私はこのアプローチをテストする理由がありませんでした。]または、Knowleechの回答に従って、デザイナーでピクセルで指定します(そのため、Visual Studioデザイナーは高DPIモニターで再スケーリングしません)。コードでその値を読み取り、ピクセルから変換しますポイントへ(正しいスケーリングを取得するため)。
ToolmakerSteve 2017

1
コードの各ビットには、自動スケールモードの直前に自動スケールの寸法が設定されており、すべて完全にスケールします。ほとんどの場合、順序は重要ではないようです。
ジョシュ

上の回答で推奨AutoScaleDimensionsされてnew SizeF(6F, 13F)いるに設定されていないインスタンスをコードで検索しました。すべてのインスタンスで、フォームのFontプロパティが設定されていることがわかりました(デフォルトではありません)。の場合AutoScaleMode = FontAutoScaleDimensionsフォームのフォントプロパティに基づいて計算されるようです。また、Windowsのコントロールパネルの[ スケーリング]設定がに影響を与えるようです。AutoScaleDimensions
Walter Stabosz

24

.Net Framework 4.7のアプリケーションをターゲットにして、Windows 10 v1703(Creators Update Build 15063)で実行します。Windows 10(v1703)での.Net 4.7により、MSはDPIを大幅に改善しました

.NET Framework 4.7以降、Windowsフォームには、一般的な高DPIおよび動的DPIシナリオの機能強化が含まれています。これらには以下が含まれます:

  • MonthCalendarコントロールやCheckedListBoxコントロールなど、多数のWindowsフォームコントロールのスケーリングとレイアウトの改善。

  • シングルパススケーリング。.NET Framework 4.6以前のバージョンでは、スケーリングが複数のパスを介して実行されていたため、一部のコントロールが必要以上にスケーリングされていました。

  • Windowsフォームアプリケーションの起動後にユーザーがDPIまたはスケール係数を変更する動的DPIシナリオのサポート。

これをサポートするには、アプリケーションマニフェストをアプリケーションに追加し、アプリケーションがWindows 10をサポートしていることを通知します。

<compatibility xmlns="urn:schemas-microsoft.comn:compatibility.v1">
    <application>
        <!-- Windows 10 compatibility -->
        <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
    </application>
</compatibility>

次に、を追加app.configし、アプリをモニターごとに宣言します。これは今はapp.configで行われ、以前のようにマニフェストでは行われません!

<System.Windows.Forms.ApplicationConfigurationSection>
   <add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection> 

このPerMonitorV2は、Windows 10 Creators Updateから新しく追加されました。

DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2

Per Monitor v2とも呼ばれます。元のモニターごとのDPI認識モードに対する進歩。これにより、アプリケーションはトップレベルウィンドウごとに新しいDPI関連のスケーリング動作にアクセスできます。

  • 子ウィンドウのDPI変更通知 -モニターごとのv2コンテキストでは、ウィンドウツリー全体に、発生したDPI変更が通知されます。

  • 非クライアント領域のスケーリング -すべてのウィンドウは、非クライアント領域をDPIセンシティブな方法で自動的に描画します。EnableNonClientDpiScalingの呼び出しは不要です。

  • S のWin32メニューのcaling -パーモニタv2のコンテキストで作成されたすべてのNTUSERメニューごとのモニター方法でスケーリングされます。

  • ダイアログのスケーリング -モニターごとのv2コンテキストで作成されたWin32ダイアログは、DPIの変更に自動的に応答します。

  • comctl32コントロールのスケーリングの向上 -さまざまなcomctl32コントロールにより、モニターごとのv2コンテキストでのDPIスケーリング動作が向上しています。

  • テーマ設定の動作の改善 -モニターごとのv2ウィンドウのコンテキストで開かれたUxThemeハンドルは、そのウィンドウに関連付けられたDPIに関して動作します。

これで、3つの新しいイベントをサブスクライブして、DPIの変更について通知を受けることができます。

  • Control.DpiChangedAfterParent、これは発生します親コントロールまたはフォームのDPI変更イベントが発生した後に、コントロールのDPI設定がプログラムによって変更されたときに発生します。

  • Control.DpiChangedBeforeParent。コントロールのDPI設定が、その親コン​​トロールまたはフォームのDPI変更イベントが発生する前にプログラムで変更されたときに発生します。

  • Form.DpiChangedは、フォームが現在表示されているディスプレイデバイスでDPI設定が変更されたときに発生します。

また、DPIの処理/スケーリングに関する3つのヘルパーメソッドがあります。

  • Control.LogicalToDeviceUnitsは、値を論理ピクセルからデバイスピクセルに変換します。

  • Control.ScaleBitmapLogicalToDevice、ビットマップイメージをデバイスの論理DPIにスケーリングします。

  • Control.DeviceDpiは、現在のデバイスのDPIを返します。

それでも問題が解決しない場合は、app.configエントリからDPIの改善をオプトアウトできます

ソースコードにアクセスできない場合は、Windowsエクスプローラーのアプリケーションプロパティに移動し、互換性に移動して、 System (Enhanced)

ここに画像の説明を入力してください

GDIスケーリングをアクティブにし、DPI処理も改善します。

GDIベースのアプリケーションの場合、WindowsはこれらをモニターごとにDPIスケーリングできるようになりました。つまり、これらのアプリケーションは、魔法のように、モニターごとのDPI対応になります。

これらの手順をすべて実行すると、WinFormsアプリケーションのDPIエクスペリエンスが向上します。ただし、アプリのターゲットを.net 4.7にする必要があり、少なくともWindows 10 Build 15063(Creators Update)が必要です。次のWindows 10 Update 1709では、さらに改善される可能性があります。


12

私が職場で書いたガイド:

WPFは「デバイスに依存しないユニット」で機能します。つまり、すべてのコントロールが高dpi画面に完全に対応します。WinFormsでは、より注意が必要です。

WinFormsはピクセル単位で機能します。テキストはシステムのdpiに従ってスケーリングされますが、スケーリングされていないコントロールによってトリミングされることがよくあります。このような問題を回避するには、明示的なサイズと配置を避ける必要があります。次のルールに従ってください。

  1. どこにいても(ラベル、ボタン、パネル)、AutoSizeプロパティをTrueに設定します。
  2. レイアウトの場合、バニラパネルではなく、FlowLayoutPanel(WPF StackPanelを使用)およびTableLayoutPanel(WPFグリッドを使用)をレイアウトに使用します。
  3. 高dpiマシンで開発している場合、Visual Studioデザイナーはフラストレーションになる可能性があります。AutoSize = Trueを設定すると、コントロールのサイズが画面に合わせて変更されます。コントロールにAutoSizeMode = GrowOnlyが設定されている場合、通常のdpiの場合はこのサイズのままになります。予想よりも大きい。これを修正するには、通常のdpiでコンピューターでデザイナーを開き、右クリックしてリセットします。

3
すべてでAutoSizeのサイズを変更できるダイアログの場合、悪夢になるでしょう。プログラムの実行中にダイアログのサイズを手動で大きくすると、ボタンが大きくなったり小さくなったりしたくありません。
ジョシュ

10

WinFormsを高DPIでうまく動作させるのは非常に難しいことがわかりました。そこで、フォームの動作をオーバーライドするVB.NETメソッドを記述しました。

Public Shared Sub ScaleForm(WindowsForm As System.Windows.Forms.Form)
    Using g As System.Drawing.Graphics = WindowsForm.CreateGraphics
        Dim sngScaleFactor As Single = 1
        Dim sngFontFactor As Single = 1
        If g.DpiX > 96 Then
            sngScaleFactor = g.DpiX / 96
            'sngFontFactor = 96 / g.DpiY
        End If
        If WindowsForm.AutoScaleDimensions = WindowsForm.CurrentAutoScaleDimensions Then
            'ucWindowsFormHost.ScaleControl(WindowsForm, sngFontFactor)
            WindowsForm.Scale(sngScaleFactor)
        End If
    End Using
End Sub

6

最近、この問題に遭遇しました。特に、高dpiシステムでエディターを開いたときのVisual Studioの再スケーリングと組み合わせて。私は維持 するのが最善であると考えましたAutoScaleMode = Fontが、フォームのフォントをデフォルトのフォントに設定し、サイズをポイントではなくピクセル指定しましたFont = MS Sans; 11px。コードでは、私はその後、デフォルトのフォントをリセットしますFont = SystemFonts.DefaultFontし、すべての罰金です。

ちょうど私の2セント。「AutoScaleMode = Fontを維持する」「デザイナーのフォントサイズをピクセル単位で設定する」は、インターネットでは見つけられなかったため、私は共有していると思いました。

私のブログに詳細がありますhttp : //www.sgrottel.de/?p=1581&lang=en


4

アンカーがうまく機能しないことに加えて、さらに一歩進んで、正確な配置(つまり、Locationプロパティを使用)は、フォントスケーリングではうまく機能しないと言います。私は2つの異なるプロジェクトでこの問題に対処する必要がありました。どちらの場合も、すべてのWinFormsコントロールの配置をTableLayoutPanelおよびFlowLayoutPanelを使用するように変換する必要がありました。TableLayoutPanel内でDock(通常はFillに設定されている)プロパティを使用すると、システムフォントDPIでうまく機能し、適切にスケーリングされます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.