programing

WPF 사이즈를 물리 픽셀로 변환하려면 어떻게 해야 합니까?

skycolor 2023. 4. 12. 22:00
반응형

WPF 사이즈를 물리 픽셀로 변환하려면 어떻게 해야 합니까?

해상도에 의존하지 않는 WPF의 폭과 높이를 물리 스크린 픽셀로 변환하는 가장 좋은 방법은 무엇입니까?

WPF 콘텐츠를 (ElementHost 경유로) WinForms 폼으로 표시하고 사이징 로직을 작성하려고 합니다.OS가 디폴트 96dpi로 동작하고 있을 때는 정상적으로 동작하고 있습니다.다만, OS 가 120 dpi 나 그 외의 해상도로 설정되어 있는 경우는 동작하지 않습니다.WinForms 에 관한 한, Width가 96 이라고 보고되는 WPF 요소는 실제로는 120 픽셀이 되기 때문입니다.

시스템에서 "픽셀/인치" 설정을 찾을 수 없습니다.창문들.시스템 파라미터WinForms와 동등한 (시스템)을 사용할 수 있습니다.창문들.Forms.SystemInformation)을 사용하는 더 나은 방법이 있습니까(WinForms API를 사용하여 수동으로 계산을 수행하는 것이 아니라 WPF API를 사용하는 방법 참조).WPF의 「픽셀」을 실제 화면의 픽셀로 변환하는 「최적의 방법」은 무엇입니까?

편집: 또한 WPF 컨트롤이 화면에 표시되기 전에 이 작업을 수행하려고 합니다.비주얼 같아요.PointToScreen을 사용하여 올바른 답을 얻을 수 있지만 컨트롤이 아직 상위 항목이 아니고 InvalidOperation이 표시되므로 사용할 수 없습니다.예외 "이 비주얼은 프레젠테이션에 연결되어 있지 않습니다.출처"를 참조하십시오.

알려진 크기를 장치 픽셀로 변환

시각적 요소가 프리젠테이션에 이미 연결되어 있는 경우소스(예를 들어 화면에 표시되는 창의 일부)는 다음과 같이 변환됩니다.

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의 hWnd를 사용하여 구축하는 것보다 효율이 낮다는 점에 유의하십시오.제로입니다만, HwndSource에 의해 작성된hWnd는 실제 새로 작성된 Window와 동일한 디스플레이 디바이스에 부가되기 때문에 신뢰성이 높다고 생각합니다.이렇게 하면 디스플레이 디바이스마다 DPI가 다르면 올바른 DPI 값을 얻을 수 있습니다.

변환이 완료되면 임의의 크기를 WPF 사이즈에서 픽셀사이즈로 변환할 수 있습니다.

var pixelSize = (Size)transformToDevice.Transform((Vector)wpfSize);

픽셀 크기를 정수로 변환

픽셀 크기를 정수로 변환하려면 다음 작업을 수행합니다.

int pixelWidth = (int)pixelSize.Width;
int pixelHeight = (int)pixelSize.Height;

그러나 보다 견고한 솔루션은 Element가 사용하는 것입니다.호스트:

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));

원하는 크기의 UELement 가져오기

원하는 크기의 UIElement를 얻으려면 측정해야 합니다.일부 상황에서는 다음과 같은 이유로 이미 측정될 수 있습니다.

  1. 이미 쟀잖아
  2. 조상 중 하나를 측정했거나
  3. 프레젠테이션의 일부입니다.소스(예를 들어 표시되는 창에 표시됨)로 Dispatcher Priority 아래를 실행하고 있습니다.측정이 자동으로 이루어졌음을 알 수 있도록 렌더링합니다.

가 아직되지 않은 사용 한 크기 가능한 크기)를하여 Measure 그 중 .new Size(double.PositivieInfinity, double.PositiveInfinity)'CHANGE: 'CHANGE: 'CHANGE: 'CHANGE: 'CHANGE:

element.Measure(availableSize);

측정이 완료되면 매트릭스를 사용하여 원하는 크기를 변환하기만 하면 됩니다.

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

이 코드에서는 원하는 크기가 없는 경우에만 Measure를 호출합니다.이것은 모든 작업을 수행하는 편리한 방법을 제공하지만 다음과 같은 몇 가지 결함이 있습니다.

  1. 요소의 부모가 더 작은 사용 가능한 크기로 전달되었을 수 있습니다.
  2. 실제 Desired Size가 0인 경우 비효율적입니다(반복적으로 재측정됨).
  3. 예기치 않은 타이밍(예를 들어 코드가 Dispatch Priority 이상으로 호출됨)으로 인해 응용 프로그램이 실패하는 버그를 숨길 수 있습니다.렌더링)

이러한 이유로 저는 GetElementPixelSize에서 Measure 콜을 생략하고 클라이언트에 맡기고 싶습니다.

화면 간의 단순한 비율.Working Area 및 System Parameters.작업 영역:

private double PointsToPixels (double wpfPoints, LengthDirection direction)
{
    if (direction == LengthDirection.Horizontal)
    {
        return wpfPoints * Screen.PrimaryScreen.WorkingArea.Width / SystemParameters.WorkArea.Width;
    }
    else
    {
        return wpfPoints * Screen.PrimaryScreen.WorkingArea.Height / SystemParameters.WorkArea.Height;
    }
}

private double PixelsToPoints(int pixels, LengthDirection direction)
{
    if (direction == LengthDirection.Horizontal)
    {
        return pixels * SystemParameters.WorkArea.Width / Screen.PrimaryScreen.WorkingArea.Width;
    }
    else
    {
        return pixels * SystemParameters.WorkArea.Height / Screen.PrimaryScreen.WorkingArea.Height;
    }
}

public enum LengthDirection
{
    Vertical, // |
    Horizontal // ——
}

이것은, 복수의 모니터에서도 정상적으로 동작합니다.

방법을 찾았지만 별로 마음에 들지 않아요.

using (var graphics = Graphics.FromHwnd(IntPtr.Zero))
{
    var pixelWidth = (int) (element.DesiredSize.Width * graphics.DpiX / 96.0);
    var pixelHeight = (int) (element.DesiredSize.Height * graphics.DpiY / 96.0);
    // ...
}

(a) 시스템에 대한 참조가 필요하기 때문에 마음에 들지 않습니다.WPF API를 사용하지 않고 그림을 그리고 (b) 직접 계산해야 합니다. 즉, WPF의 구현 세부사항을 복제해야 합니다..NET 3.5에서는 어떤 요소에 일치하도록 계산 결과를 잘라내야 합니다.호스트에서 AutoSize=true를 사용하지만 이후 버전에서 여전히 정확한지 알 수 없습니다.그물.

효과가 있는 것 같아서 혹시나 도움이 될까 해서 올립니다.하지만 더 나은 답을 가진 사람이 있다면, 제발 저리 가세요.

ObjectBrowser에서 빠르게 검색한 결과 매우 흥미로운 내용을 발견했습니다. 확인해 보십시오.

System.Windows.Form.AutoScaleMode에는 DPI라는 속성이 있습니다.여기 문서가 있습니다.찾고 있는 문서일 수 있습니다.

public const 시스템.창문들.Forms.AutoScaleMode Dpi = 2의 시스템 구성원.창문들.Forms.AutoScale 모드

개요: 디스플레이 해상도를 기준으로 배율을 제어합니다.일반적인 해상도는 96 및 120 DPI입니다.

폼에 적용하면 효과가 있을 거예요.

{filename}

언급URL : https://stackoverflow.com/questions/3286175/how-do-i-convert-a-wpf-size-to-physical-pixels

반응형