Неправильный PointToScreen с использованием DesktopDPIOverride

Установка ползунка «Изменить размер всех элементов» Control Panel\Appearance and Personalization\Display на «Больше» (который изменяет эту запись реестра: HKEY_CURRENT_USER\Control Panel\Desktop\DesktopDPIOverride) приводит к неправильному расчету метода Control.PointToScreen(). Это можно воспроизвести, используя следующий класс Class1 в форме Windows:

public class Class1 : Control
{
  protected override void OnPaint(PaintEventArgs e)
  {
    base.OnPaint(e);

    Draw(e.ClipRectangle, e.Graphics);
  }

  private void Draw(Rectangle rect, Graphics graphics)
  {
    Pen pen = new Pen(Color.Red);
    pen.Width = 2;

    graphics.DrawRectangle(pen, rect);
  }

  protected override void OnMouseDown(MouseEventArgs e)
  {
    base.OnMouseDown(e);

    Point p = this.PointToScreen(new Point(0, 0));

    ControlPaint.DrawReversibleFrame(new Rectangle(p, new Size(e.X, e.Y)), Color.Yellow, FrameStyle.Dashed);
  }

  protected override void OnMouseUp(MouseEventArgs e)
  {
    base.OnMouseUp(e);
    this.Invalidate();
  }
}

Использование этого элемента управления в WinForm и нажатие на него работает должным образом. Теперь измените «Изменить размер всех элементов» на «Увеличить» и снова запустите код — код больше не работает должным образом, метод PointToScreen возвращает ошибочное значение для (0, 0).

Кто-нибудь знает, как решить эту проблему? Большое спасибо.


person Shunyata Kharg    schedule 01.07.2014    source источник
comment
Рисование PaintEventArgs.ClipRectangle никогда не имеет смысла.   -  person Hans Passant    schedule 01.07.2014
comment
Я наблюдаю такое же поведение. Вы когда-нибудь находили решение?   -  person adv12    schedule 21.04.2016
comment
@HansPassant, интересно не Draw(e.ClipRectangle, e.Graphics), а результаты Control.PointToScreen на дисплее с разрешением не 100% DPI, как показано при вызове DrawReversibleFrame. Я наблюдал такое же поведение: PointToScreen кажется сломанным, когда действует автоматическое масштабирование. Есть ли опыт в этом/мысли?   -  person adv12    schedule 21.04.2016
comment
Поскольку Control.PointToScreen — это просто оболочка (источник ссылки) на MapWindowPoints API, вы фактически заявляете, что вся Windows сломана.   -  person Ivan Stoev    schedule 21.04.2016
comment
@IvanStoev, скопируйте приведенный выше код в проект WinForms и добавьте Class1 в основную форму. Измените настройки DPI дисплея на что-то выше 100%. Затем запустите проект и щелкните элемент управления Class1. Class1 создаст и нарисует прямоугольник, начиная с экранных координат, рассчитанных с помощью Control.PointToScreen из местоположения клиента (0, 0). Прямоугольник, нарисованный на экране, будет начинаться выше и левее элемента управления Class1. Мы утверждаем, что это происходит. Похоже на ошибку в Control.PointToScreen, но я был бы рад рассмотреть другие теории.   -  person adv12    schedule 21.04.2016
comment
Наблюдение: когда я устанавливаю масштаб дисплея на 125%, размеры, возвращаемые из Control.PointToScreen, необходимо масштабировать на 1,25, чтобы они отображались в нужном месте на экране. То же самое касается ширины и высоты прямоугольника экрана.   -  person adv12    schedule 21.04.2016
comment
@ adv12 adv12 Я говорю, что проблема не связана с Windows Forms. MapWindowPoints используется в каждом приложении Windows (управляемом, неуправляемом и т. д.).   -  person Ivan Stoev    schedule 21.04.2016
comment
@ adv12 - Сейчас не работает GetDCEx(). ControlPaint() использует эту функцию, чтобы получить контекст устройства для окна рабочего стола для рисования. Виртуализация DPI не применяется. Вероятно, это очень специфично для версии Windows, я предполагаю, что это пошло не так в Win8.1, и я вижу, что это пойдет не так в Windows 10 версии 10586. Непросто обойти это и рискованно. Если вы не хотите объявлять свое приложение dpiAware, тогда лучше позвонить в службу поддержки Microsoft.   -  person Hans Passant    schedule 21.04.2016
comment
@HansPassant, в моем собственном коде я не использую ControlPaint(), но использую Graphics.CopyFromScreen(). Могу ли я предположить, что также использует GetDCEx()? (Просто пытаюсь понять, как мы с ОП наткнулись на одно и то же поведение...)   -  person adv12    schedule 22.04.2016
comment
Вздох. Нет, виртуализация dpi никоим образом не даст вам правильный вид в окнах другого приложения. Вы должны объявить свое приложение dpiAware.   -  person Hans Passant    schedule 22.04.2016
comment
@HansPassant, на самом деле я пытался захватить свое собственное окно - что-то, нарисованное с помощью Direct3D, которое я не знал, как захватить иначе. Извините, что расстроил вас; Я рассмотрю объявление моего приложения dpiAware. Спасибо за помощь.   -  person adv12    schedule 22.04.2016


Ответы (1)


Похоже, вам нужно сделать его осведомленным о DPI. Вы можете сделать это так

[DllImport("user32.dll")]
private static extern bool SetProcessDPIAware();

static void Main()
{
    SetProcessDPIAware();

}
person Alex    schedule 03.06.2017