Как можно узнать размер клиентской области окна в приложении WPF?

Я пытался использовать свойство RenderSize у Grid, но получает значение NaN. Аналогичная проблема возникает и с ActualWidth/ActualHeight. Я устанавливаю размеры окна после его инициализации, возможно, именно это и является причиной проблемы. Существуют ли альтернативные способы определения размеров клиентской области?
  • 17 апреля 2025 г. 17:45
Ответы на вопрос 2
В WPF размер клиентской области окна можно получить несколькими способами, но действительно, свойства `RenderSize`, `ActualWidth` и `ActualHeight` могут возвращать `NaN` в момент инициализации, если элементы еще не были отрисованы. 

Вот несколько способов, как можно определить размер клиентской области:

1. **Использовать событие Loaded**: Вы можете подписаться на событие `Loaded` вашего окна или элемента, чтобы получить актуальные размеры после завершения инициализации.

   ```csharp
   public MainWindow()
   {
       InitializeComponent();
       this.Loaded += MainWindow_Loaded;
   }

   private void MainWindow_Loaded(object sender, RoutedEventArgs e)
   {
       double clientWidth = this.ActualWidth;
       double clientHeight = this.ActualHeight;
       // Здесь clientWidth и clientHeight будут иметь нужные значения.
   }
   ```

2. **Использовать Layout Updated**: Если вам нужно отслеживать изменения в размере окна в процессе работы приложения, вы можете использовать событие `LayoutUpdated`.

   ```csharp
   public MainWindow()
   {
       InitializeComponent();
       this.LayoutUpdated += MainWindow_LayoutUpdated;
   }

   private void MainWindow_LayoutUpdated(object sender, EventArgs e)
   {
       double clientWidth = this.ActualWidth;
       double clientHeight = this.ActualHeight;
       // Обработайте размеры окна здесь
   }
   ```

3. **Window.StateChanged**: Если состояние вашего окна может изменяться (например, нормальное, свернутое или развернутое), вы можете подписаться на событие `StateChanged`.

   ```csharp
   public MainWindow()
   {
       InitializeComponent();
       this.StateChanged += MainWindow_StateChanged;
   }

   private void MainWindow_StateChanged(object sender, EventArgs e)
   {
       double clientWidth = this.ActualWidth;
       double clientHeight = this.ActualHeight;
       // Обработайте размеры окна здесь
   }
   ```

4. **На момент создания окна**: Если размеры окна задаются после его создания, убедитесь, что вы делаете это до того, как окно будет отображено. Например, установите размеры в конструкторе или в методе `OnSourceInitialized`:

   ```csharp
   protected override void OnSourceInitialized(EventArgs e)
   {
       base.OnSourceInitialized(e);
       this.Width = 800; // установите нужные значения
       this.Height = 600;
       // Теперь значения ActualWidth и ActualHeight должны быть актуальными.
   }
   ```

Эти подходы позволяют получать размеры клиентской области окна после того, как она была инициализирована и отрисована, что помогает избежать значений `NaN`.
Если вам именно самого окна в нормальных координатах, то можно использовать WinApi: 
https://github.com/VoidVolker/LockScreen/blob/mast...
public static class Native
{
    [DllImport("user32.dll")]
    private static extern bool GetWindowRect(IntPtr hwnd, ref RECT rectangle);

    [DllImport("user32.dll")]
    private static extern bool GetClientRect(IntPtr hwnd, ref RECT rectangle);

    public RECT GetWindowPos(IntPtr hwnd)
    {
        var r = new RECT();
        GetWindowRect(hwnd, ref r);
        return r;
    }

    public RECT GetClientPos(IntPtr hwnd)
    {
        var r = new RECT();
        GetClientRect(hwnd, ref r);
        return r;
    }

    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct RECT(int left, int top, int right, int bottom)
    {
        public int Left = left;
        public int Top = top;
        public int Right = right;
        public int Bottom = bottom;
        public readonly int Width => Right - Left;
        public readonly int Height => Bottom - Top;
    }
}

Значения RenderSize и ActualWidth/ActualHeight будут после вычисления положения самого элемента. Скорее всего вы слишком рано пытаетесь получить к ним доступ. Запустите ваше приложение и в отладчике посмотрите в дереве окон значения.
Плюс учтите, что в WPF свои пиксели, которые надо конвертировать в нормальные и обратно с учётом DPI текущего монитора, на котором располагается окно (на SO есть готовый код). И из-за этого WPF окно невозможно 100% точно позиционировать в нужных координатах и нужного размера в многомониторной конфигурации.

Пример работы с визуалом элемента при его инициализации:
public class MyControl : Control
{
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        // Размеры и позиция элемента вычислены и он готов к работе
    }
}

https://github.com/VoidVolker/LockScreen/blob/mast... - пример из реального проекта.
Т.е., наследуете свой элемент от базового класса или любого другого элемента и вклиниваетесь в событие применения шаблона элемента. В разметке XAML добавляете ссылку на класс и спокойно используете свой элемент как обычно.
Похожие вопросы