WPF:Размер окна в зависимости от текущего монитора

добавлено: 25 апр 18
понравилось:0
просмотров: 1555
комментов: 3

теги:

Автор: gpu

Дано
WPF-Приложение и комп с 2-мя мониторами с разным разрешением (e.g. 2560х1440) и (1920х1080).

Задача
В зависимости от текущего монитора использовать максимально доступную область для главного окна приложения.

К сожалению получение информации о подключенных мониторах доступно для Windows Forms и не доступно для WPF.
При невозможности использования Windows Forms можно использовать p-invoke для доступа к системным функциям User32.dll.

Решение на VB.Net
Определяем необxодимые нам структуры (RectStructure, MonitorInfo)
<StructLayout(LayoutKind.Sequential)>
Public Structure RectStructure
    Public Left As Integer
    Public Top As Integer
    Public Right As Integer
    Public Bottom As Integer
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
Public Structure MonitorInfo
    Public Size As Integer
    Public Monitor As RectStructure
    Public WorkArea As RectStructure
    Public Flags As UInteger
End Structure

И внешние методы>
<DllImport("user32.dll")>
Private Shared Function GetWindowRect(ByVal windowHandle As IntPtr, ByRef rectangle As RectStructure) As Boolean
End Function

<DllImport("user32.dll")>
Private Shared Function MonitorFromRect(<[In]> ByRef rectPointer As RectStructure, ByVal flags As UInteger) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Private Shared Function GetMonitorInfo(ByVal hmon As IntPtr, ByRef mi As MonitorInfo) As Boolean
End Function

Пишем вспомогательный метод для получения хендл монитора из хендла окна>
Private Const MONITOR_DEFAULTTONEAREST As UInteger = 2
Private Shared Function GetMonitorHandleByWindow(ByVal windowHandle As IntPtr) As IntPtr
    Dim rect As New RectStructure()
    GetWindowRect(windowHandle, rect)
    Dim monitorHandle = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST)
    Return monitorHandle
End Function

И собственно функцию которая будет нами использована в приложении для установки размера и положения окна>
Public Shared Function SetWindowDimensions(window As Window) As Boolean
    Dim ret As Boolean = False
    If window.IsActive Then
        Dim wih As New WindowInteropHelper(window)
        Dim winHandle As IntPtr = wih.Handle
        If winHandle <> IntPtr.Zero Then
            Dim monitorHandle As IntPtr = GetMonitorHandleByWindow(winHandle)
            Dim mi = New MonitorInfo()
            mi.Size = CUInt(Marshal.SizeOf(mi))
            GetMonitorInfo(monitorHandle, mi)
            window.Height = mi.WorkArea.bottom - mi.WorkArea.Top
            window.Width = mi.WorkArea.Right - mi.WorkArea.Left
            window.Left = mi.WorkArea.Left
            window.Top = mi.WorkArea.Top
            ret = True
        End If
    End If
    Return ret
End Function

Как мы видим размер и положение окна будет производиться только при условии что окно активировано и возможно получить хендла окна из обькта window.
Опытным путем было выяснено что очень важно вызвать SetWindowDimensions в правильном месте в правильное время. В зависимости от настроек системы и того
на каком монитире мы стартуем наше приложение , мы можем получить не тот монитор на котором приложение окончательно будет отображено.
Например, мы стартуем приложение на втором мониторе и второй монитор будет корректно возвращаться нам как текущий монитор при>
- Инициализации проложения (Application_Startup)
- Главное окно приложения загружено (Loaded)
- Главное окно приложения активировано (OnActivated)

Но после этого при дальнейшей инициализации (загрузка подконтролов) внезапно получим первый монитор как текущий вместо ожидаемого второго.
Так что выбор места для вызова SetWindowDimensions очень важен, по крайней мере в моем случае ето было именно так.
Возможно ето не всегда так и SetWindowDimensions спокойно можно вызывать скажем когда главное окно приложения активировано.

Комментарии


  • 10 мая 2018, 22:34 Roman Mejtes

    Может просто через диспатчер с нужным приоритетом запустить это? А не городить код в событиях.
    На Render там или Input приоритет

  • я это точно не решу (

  • >Может просто через диспатчер с нужным приоритетом >запустить это?
    Определить место где ето нужно сделать не входило в задачу нужно было сделать быстро и вчера.



Необходимо войти на сайт, чтобы оставлять комментарии