web-dev-qa-db-ja.com

WPFアプリケーションのDPI認識を無効にする

良い一日!

私はしばらくの間WPFアプリに取り組んでおり(学習体験として、ああ、それは学習体験でした)、ついにリリースの準備が整いました。リリースとは、それを私のHTPCにインストールすることを意味します。HTPCは、私の映画コレクションを閲覧するために使用されます。

1920 * 1080を実行するが通常のDPI設定で動作する私のPCで設計しましたが、HTPC/TVは同じ解像度ですが、明らかな理由により高いDPI設定で実行しています。

問題は、私のアプリがHTPCで無駄になり、ビジュアルに関する限りほとんどすべてを台無しにしてしまうことです。私はこれが悪い設計(平均culpa)によるものであることを知っていますが、それは私だけが使用するアプリケーションなので、完全な再設計ではなく、迅速な修正を探しています。次のコードをAssemblyInfo.csに追加することで、アプリがDPI対応にならないようにすることができると私は読んだ。

[Assembly: System.Windows.Media.DisableDpiAwareness]

ただし、効果がないようで、アプリの動作は同じです。

誰かが私を正しい方向に向けることができますか?

ありがとう、ヨハン

26
Johan Verbelen

DPIAwareness

いくつかのアイデア(試していない):

XPで実行していますか?このオプションは、そのプラットフォームでは機能しない可能性があります。

以下は、おそらく同じDpiAwarenessオプションを設定するための異なる方法です。

  • eXEの「互換モード」設定を確認してください...そのプロパティを右クリックし、「ディスプレイスケーリングを無効にする」をオンにしてください

  • マニフェストを作成し、気づいていないと言う

    <Assembly xmlns="urn:schemas-Microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-Microsoft-com:asm.v3" >
     ...
      <asmv3:application>
        <asmv3:windowsSettings xmlns="http://schemas.Microsoft.com/SMI/2005/WindowsSettings">
          <dpiAware>false</dpiAware>
        </asmv3:windowsSettings>
      </asmv3:application>
     ...
    </Assembly>
    
  • SetProcessDPIAwareを呼び出します(これは、ウィンドウが作成される前、つまり、ウィンドウが作成される前に呼び出す必要があります)

    http://msdn.Microsoft.com/en-gb/library/windows/desktop/ms633543(v = vs.85).aspx

IsProcessDPIAwareを呼び出して、アプリプロセスに設定が適用されているかどうかを確認できます。

DPIを見つける

現在使用しているDPIを確認する方法は次のとおりです。

CompositionTargetPresentationSourceを介してWindowにアクセスし、使用しているDPIスケーリングを確認します.....その後、値を使用してスケーリング調整を行うことができます。あなたの「もの」(サイズ/長さなどがデバイスに依存しない単位で指定されている)。これにより、より高いDPIが有効になっているためにスケールアップされても、物理的なピクセルの使用量が爆発することはありません(...さまざまな方法で、たとえばViewBox、または要素の幅などの計算)。

double dpiX, double dpiY;
PresentationSource presentationsource = PresentationSource.FromVisual(mywindow);

if (presentationsource != null) // make sure it's connected
{
    dpiX = 96.0 * presentationsource.CompositionTarget.TransformToDevice.M11;
    dpiY = 96.0 * presentationsource.CompositionTarget.TransformToDevice.M22;
}

スケーリング調整を行う

  • ViewBoxトリックを使用する

    以前に行ったこの回答を参照してください。DPIスケーリングに関係なく、Canvasは「ネイティブ」ピクセルとして解釈される位置を使用できました。

    WPF for LCD screen full HD

  • TransFormToDeviceCompositionTargetスケーリング行列にアクセスし、そこからそのスケーリングを元に戻す新しい行列を計算して、LayoutTransformまたはRenderTransformで使用します。

  • dIP(デバイスに依存しない位置)の位置/長さを明示的に変更するメソッドを使用します(コンバーターに置くこともできます)。ウィンドウサイズを特定のピクセルサイズに一致させる場合は、このようにすることができます。

34
Colin Smith

このブログ記事Decorator サブクラスを使用してそれを行う方法を説明します。

つまり、次のようなクラスを作成します。

namespace MyNamespace
{
    public class DpiDecorator : Decorator
    {
        public DpiDecorator()
        {
            this.Loaded += (s, e) =>
            {
                Matrix m = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice;
                ScaleTransform dpiTransform = new ScaleTransform(1 / m.M11, 1 / m.M22);
                if (dpiTransform.CanFreeze)
                    dpiTransform.Freeze();
                this.LayoutTransform = dpiTransform;
            };
        }
    };
};

次に、xmlns:custom="clr-namespace:MyNamespace"などをトップレベルのウィンドウ定義に追加し、DPIに依存しないユーザーインターフェイスを<custom:DpiDecorator>...</custom:DpiDecorator>で囲みます。

6
ulatekh

上記のどれも、WPFのdpiスケーリングを本当に無効にしません。

これは、WPFによって.net 4.6でdpiスケーリングが計算される方法です。1)すべてのビジュアルで使用される構成ターゲットであるHwndTarget。 2)ビジュアルの基本クラスであるUIElementおよびdpiスケーリング計算結果が格納される場所。

WPFは、最初のビジュアルが準備されたときにグローバルスケーリングが1回計算されるようにします。これは、HwndTarget {private static bool _setDpi = true}のブール値によって制御されます。 dpiを計算した後、_setDpiフィールドはfalseに設定され、dpi対応のメソッドは次のようなコードを使用したショートカットです{if(!HwndTarget._setDpi)return;}

同様のことがすべてのUIElementで発生します。これは、{private static bool _setDpi = true}を使用して同じパターンを持ち、初めて計算できるようにします。

次のチェックは、None、Process、またはMonitorのProcessDpiAwarenessから行われます。 WPFが個々のモニターを考慮しないようにするには、ProcessDpiAwarenessをProcess(int 1)に設定する必要があります。

最後にdpiが計算されると、結果はUIElementのDpiScaleXValuesおよびDpiScaleYValuesと呼ばれる2つのリストに格納されます。したがって、これらを初期化して値を修正する必要があります。

これが私が自分で使用するサンプルコードです(.net 4.6でのみ機能します):

        var setDpiHwnd = typeof(HwndTarget).GetField("_setDpi", BindingFlags.Static | BindingFlags.NonPublic);
        setDpiHwnd?.SetValue(null, false);

        var setProcessDpiAwareness = typeof(HwndTarget).GetProperty("ProcessDpiAwareness", BindingFlags.Static | BindingFlags.NonPublic);
        setProcessDpiAwareness?.SetValue(null, 1, null);

        var setDpi = typeof(UIElement).GetField("_setDpi", BindingFlags.Static | BindingFlags.NonPublic);

        setDpi?.SetValue(null, false);

        var setDpiXValues = (List<double>)typeof(UIElement).GetField("DpiScaleXValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);

        setDpiXValues?.Insert(0, 1);

        var setDpiYValues = (List<double>)typeof(UIElement).GetField("DpiScaleYValues", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);

        setDpiYValues?.Insert(0, 1);
3
Calin Pirtea

簡単な修正は、ウィンドウのコンテンツを Viewbox でラップすることです。 Viewboxの直接の子に明示的なWidthとHeightがあり、アスペクト比が両方のマシンで同じである場合、これですぐに起動して実行できるはずです。

2
Joe White

DpiAwarenessはDPI仮想化にのみ影響します。 Windowsでは、Windows 7および8のカスタム解像度ボックスで「Use Windows XP style scaling)」と表示され、チェックを付けると無効になり、チェックを外すと有効になります(メインのフォントサイズに適用することを忘れないでください)。画面を変更した場合!ボックスをOKにするだけでは不十分です。)

DPI仮想化をオフにしている場合、アプリがサポートするOSにアプリが伝えることには何の違いもありません。常に標準スケーリングを使用します。オンになっていると、真のスケーリング(認識)と、アプリ全体でバイリニアのサイズ変更をスラップすること(認識されない)の間に違いが生じます。

私は別の警告を覚えました、DPI virtはAeroガラスが機能しないとまったく機能しません。

2
SilverbackNet

VB.NETを使用している場合、app.manifestにアクセスする方法は次のとおりです。

enter image description here

次に、この部分のコメントを外して「false」に設定します

enter image description here

0
Carlos Borau

Windows 10で.NET 4.8ランタイムに更新した後、ProcessDpiAwarenessを設定するためのCalinのアプローチで次の例外が発生しました

System.ArgumentException: 'タイプ' System.Int32 'のオブジェクトはタイプ' System.Nullable`1 [MS.Win32.NativeMethods + PROCESS_DPI_AWARENESS] 'に変換できません。

コードを以下に変更すると例外は解決されますが、同じ結果が得られるかどうかはわかりません。

var processDpiAwareness = typeof(HwndTarget).GetProperty("ProcessDpiAwareness", BindingFlags.Static | BindingFlags.NonPublic);

if (processDpiAwareness != null)
{
      var underlyingType = Nullable.GetUnderlyingType(processDpiAwareness.PropertyType);

      if (underlyingType != null)
      {
           var val = Enum.Parse(underlyingType, "PROCESS_SYSTEM_DPI_AWARE");
           processDpiAwareness.SetValue(null, val, null);
      }
 }
0
Paul G

Calin Pirteaの答えは、DPIスケーリングを本当に無効にします。

アプリケーションを100%を超える表示スケールに拡大し、ぼやけた画像を受け入れたい場合は、Carlos Borauの答えが有効です。これが.NET 4.6(C#)の方法です。

1アプリケーションプロジェクトにマニフェストファイルを追加する

  1. プロジェクトを右クリックし、[追加]-> [新しいアイテム]
  2. General-> Application Manifest Fileを選択します

2以下のコメントを外してdpiAware = falseに設定します。

  <application xmlns="urn:schemas-Microsoft-com:asm.v3">
      <windowsSettings>
          <dpiAware xmlns="http://schemas.Microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
      </windowsSettings>
  </application>
0
Siamca