既知のサイズをデバイス ピクセルに変換する
ビジュアル要素が既に PresentationSource にアタッチされている場合 (たとえば、画面に表示されるウィンドウの一部である場合)、変換は次のように検出されます。
var source = PresentationSource.FromVisual(element);
Matrix transformToDevice = source.CompositionTarget.TransformToDevice;
そうでない場合は、HwndSource を使用して一時的な hWnd を作成します。
Matrix transformToDevice;
using(var source = new HwndSource(new HwndSourceParameters()))
transformToDevice = source.CompositionTarget.TransformToDevice;
これは、IntPtr.Zero の hWnd を使用して構築するよりも効率が悪いことに注意してください。ただし、HwndSource によって作成された hWnd は、実際に新しく作成されたウィンドウと同じディスプレイ デバイスに接続されるため、信頼性が高いと考えています。そうすれば、異なるディスプレイ デバイスの DPI が異なる場合でも、適切な DPI 値を確実に取得できます。
変換が完了したら、任意のサイズを WPF サイズからピクセル サイズに変換できます。
var pixelSize = (Size)transformToDevice.Transform((Vector)wpfSize);
ピクセル サイズを整数に変換する
ピクセル サイズを整数に変換する場合は、次のように簡単に実行できます。
int pixelWidth = (int)pixelSize.Width;
int pixelHeight = (int)pixelSize.Height;
しかし、より堅牢なソリューションは、ElementHost で使用されるものです。
int pixelWidth = (int)Math.Max(int.MinValue, Math.Min(int.MaxValue, pixelSize.Width));
int pixelHeight = (int)Math.Max(int.MinValue, Math.Min(int.MaxValue, pixelSize.Height));
UIElement の目的のサイズを取得する
UIElement の目的のサイズを取得するには、それが測定されていることを確認する必要があります。状況によっては、次のいずれかの理由で、すでに測定されています。
- あなたはすでにそれを測定しました
- その祖先の 1 つを測定した、または
- これは、PresentationSource の一部であり (たとえば、可視ウィンドウ内にあります)、DispatcherPriority.Render の下で実行しているため、測定が既に自動的に行われていることがわかります。
ビジュアル要素がまだ測定されていない場合は、必要に応じてコントロールまたはその先祖の 1 つで Measure を呼び出し、使用可能なサイズを渡す必要があります (またはnew Size(double.PositivieInfinity, double.PositiveInfinity)、コンテンツに合わせてサイズを変更する場合:
element.Measure(availableSize);
測定が完了したら、行列を使用して DesiredSize を変換するだけです。
var pixelSize = (Size)transformToDevice.Transform((Vector)element.DesiredSize);
すべてを一緒に入れて
要素のピクセル サイズを取得する方法を示す簡単な方法を次に示します。
public Size GetElementPixelSize(UIElement element)
{
Matrix transformToDevice;
var source = PresentationSource.FromVisual(element);
if(source!=null)
transformToDevice = source.CompositionTarget.TransformToDevice;
else
using(var source = new HwndSource(new HwndSourceParameters()))
transformToDevice = source.CompositionTarget.TransformToDevice;
if(element.DesiredSize == new Size())
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
return (Size)transformToDevice.Transform((Vector)element.DesiredSize);
}
このコードでは、DesiredSize が存在しない場合にのみ Measure を呼び出すことに注意してください。これはすべてを行う便利な方法を提供しますが、いくつかの欠点があります。
- 要素の親がより小さな availableSize を渡した可能性があります
- 実際の DesiredSize がゼロの場合は非効率です (繰り返し再測定されます)。
- 予期しないタイミングでアプリケーションが失敗する原因となるバグを隠す可能性があります (たとえば、コードが DispatchPriority.Render 以上で呼び出される)。
これらの理由から、私は GetElementPixelSize で Measure 呼び出しを省略し、クライアントに任せたいと思います。