如何设置WPF窗口的启动ClientSize?

发布于 2024-07-26 07:12:33 字数 602 浏览 5 评论 0原文

我想设置 WPF 窗口的初始客户端大小。 我没有看到一个简单的方法来做到这一点。

具体来说,当我的窗口打开时,我希望它的大小足以容纳其内容,而不需要滚动条。 但在显示之后,我希望窗口可以自由调整大小(更大或更小)。

如果我在 Window 元素上设置 Width 和 Height 属性,则会设置非客户端(外部)大小,这是没有用的。 一旦标题栏和调整边框占用了该空间,客户端区域将不再足够大以容纳其内容,并且我将拥有滚动条。 我可以通过选择更大的尺寸来进行补偿,但是标题栏高度和边框厚度都是用户可自定义的(默认值也因操作系统版本而异),并且在不同的计算机上不一定相同。

我可以在窗口的内容元素(本例中为 )上设置宽度和高度,然后将窗口的 SizeToContent 属性设置为 WidthAndHeight。 这使得窗口的初始大小恰好达到我想要的位置。 但是之后事情就不再调整大小了——我可以调整窗口的大小,但它的内容不会随之调整大小,因为我指定了固定的大小。

有没有办法设置窗口的初始客户端大小,最好没有代码隐藏? (如果这是唯一的方法,我将采用代码隐藏,但如果有人有的话,我更喜欢仅使用 XAML 的方法。)

I want to set my WPF window's initial client size. I'm not seeing a straightforward way to do this.

Specifically, when my window opens, I want it to be sized just big enough for its contents to fit without needing scrollbars. But after it's shown, I want the window to be freely resizable (either larger or smaller).

If I set Width and Height attributes on my Window element, that sets the non-client (outer) size, which isn't useful. Once the titlebar and resize borders eat into that space, the client area will no longer be big enough for its content, and I'll have scrollbars. I could compensate by picking a larger size, but both titlebar height and border thickness are user-customizable (as well as the defaults varying by OS version) and won't necessarily be the same on a different machine.

I can set Width and Height on the window's content element (a <Grid> in this case), and then set the Window's SizeToContent attribute to WidthAndHeight. That gets the window's initial size exactly where I want it. But then things don't resize anymore -- I can resize the window, but its content doesn't resize with it, because I specified a fixed size.

Is there any way to set a Window's initial client size, preferably without code-behind? (I'll take code-behind if that's the only way, but I'd prefer a XAML-only approach if anyone has one.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

七色彩虹 2024-08-02 07:12:33

您可以通过以下两种方式之一在 Load 事件处理程序的代码隐藏中执行此操作:

注意:两个示例中 LayoutRoot 网格的内容相同,但 LayoutRoot 上的宽度和高度仅在示例 A 中指定

。 ) 窗口的 SizeToContent 以及内容的宽度和高度上的 ClearValue:

using System.Windows;

namespace WpfWindowBorderTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ClearValue(SizeToContentProperty);
            LayoutRoot.ClearValue(WidthProperty);
            LayoutRoot.ClearValue(HeightProperty);
        }
    }
}

假设页面布局如下(注意窗口上的 SizeToContent 设置和 Loaded 事件处理程序以及 LayoutRoot 上指定的宽度和高度):

<Window x:Class="WpfWindowBorderTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" SizeToContent="WidthAndHeight" Loaded="Window_Loaded">
    <Grid x:Name="LayoutRoot" Width="300" Height="300" Background="Green">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" />
        <Rectangle Grid.Row="0" Grid.Column="0" Width="75" Height="75" Fill="Red" />
        <Rectangle Grid.Row="1" Grid.Column="1" Fill="Yellow" />
        <Rectangle Grid.Row="1" Grid.Column="1" Width="225" Height="225" Fill="Red" />
    </Grid>
</Window>

B) 设置窗口的宽度考虑系统特定​​的客户端窗口框架尺寸的高度:

使用 System.Windows;

namespace WpfWindowBorderTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            const int snugContentWidth = 300;
            const int snugContentHeight = 300;

            var horizontalBorderHeight = SystemParameters.ResizeFrameHorizontalBorderHeight;
            var verticalBorderWidth = SystemParameters.ResizeFrameVerticalBorderWidth;
            var captionHeight = SystemParameters.CaptionHeight;

            Width = snugContentWidth + 2 * verticalBorderWidth;
            Height = snugContentHeight + captionHeight + 2 * horizontalBorderHeight;
        }
    }
}

假设像这样的比例页面布局(注意 Window 上没有 SizeToContent 设置或 Loaded 事件处理程序或 LayoutRoot 上指定的宽度和高度):

<Window x:Class="WpfWindowBorderTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1">
    <Grid x:Name="LayoutRoot" Background="Green">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" />
        <Rectangle Grid.Row="0" Grid.Column="0" Width="75" Height="75" Fill="Red" />
        <Rectangle Grid.Row="1" Grid.Column="1" Fill="Yellow" />
        <Rectangle Grid.Row="1" Grid.Column="1" Width="225" Height="225" Fill="Red" />
    </Grid>
</Window>

我还无法想出一种在 XAML 中以声明方式执行此操作的方法。

You can do it in code-behind on the Load event handler in one of two ways:

NOTE: The content of the LayoutRoot Grid is the same in both examples, but the Width and Height on the LayoutRoot are only specified in example A.

A) ClearValue on the the Window's SizeToContent and on the content's Width and Height:

using System.Windows;

namespace WpfWindowBorderTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ClearValue(SizeToContentProperty);
            LayoutRoot.ClearValue(WidthProperty);
            LayoutRoot.ClearValue(HeightProperty);
        }
    }
}

assuming a page layout like (note the SizeToContent setting and Loaded event handler on the Window and the Width and Height specified on the LayoutRoot):

<Window x:Class="WpfWindowBorderTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" SizeToContent="WidthAndHeight" Loaded="Window_Loaded">
    <Grid x:Name="LayoutRoot" Width="300" Height="300" Background="Green">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" />
        <Rectangle Grid.Row="0" Grid.Column="0" Width="75" Height="75" Fill="Red" />
        <Rectangle Grid.Row="1" Grid.Column="1" Fill="Yellow" />
        <Rectangle Grid.Row="1" Grid.Column="1" Width="225" Height="225" Fill="Red" />
    </Grid>
</Window>

or

B) setting the Window's Width and Height accounting for the System-specific client window frame sizes:

using System.Windows;

namespace WpfWindowBorderTest
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            const int snugContentWidth = 300;
            const int snugContentHeight = 300;

            var horizontalBorderHeight = SystemParameters.ResizeFrameHorizontalBorderHeight;
            var verticalBorderWidth = SystemParameters.ResizeFrameVerticalBorderWidth;
            var captionHeight = SystemParameters.CaptionHeight;

            Width = snugContentWidth + 2 * verticalBorderWidth;
            Height = snugContentHeight + captionHeight + 2 * horizontalBorderHeight;
        }
    }
}

assuming a proportional page layout like (note no SizeToContent setting or Loaded event handler on the Window or Width and Height specified on the LayoutRoot):

<Window x:Class="WpfWindowBorderTest.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1">
    <Grid x:Name="LayoutRoot" Background="Green">
        <Grid.RowDefinitions>
            <RowDefinition Height="1*"/>
            <RowDefinition Height="3*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"/>
            <ColumnDefinition Width="3*"/>
        </Grid.ColumnDefinitions>
        <Rectangle Grid.Row="0" Grid.Column="0" Fill="Black" />
        <Rectangle Grid.Row="0" Grid.Column="0" Width="75" Height="75" Fill="Red" />
        <Rectangle Grid.Row="1" Grid.Column="1" Fill="Yellow" />
        <Rectangle Grid.Row="1" Grid.Column="1" Width="225" Height="225" Fill="Red" />
    </Grid>
</Window>

I haven't been able to come up with a way to do it declaratively in XAML as yet.

来世叙缘 2024-08-02 07:12:33

您可以删除 XAML 中的窗口宽度和高度属性,并添加 SizeToContent="WidthAndHeight"。 这会将窗口的初始尺寸设置为其内容,但仍然允许您调整其大小。

<Window x:Class="WpfApplication2.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" SizeToContent="WidthAndHeight">
    <Grid>
        <TextBlock Text="How to set WPF window’s startup ClientSize?"/>
    </Grid>
</Window>

启动后,它看起来像这样:

在此处输入图像描述

但您仍然可以用鼠标拉伸它:

在此处输入图像描述

You can remove the window Width and Height attributes in XAML, and add SizeToContent="WidthAndHeight". This sets the initial dimensions of the window to its content, yet still lets you resize it.

<Window x:Class="WpfApplication2.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" SizeToContent="WidthAndHeight">
    <Grid>
        <TextBlock Text="How to set WPF window’s startup ClientSize?"/>
    </Grid>
</Window>

When started, it looks like this:

enter image description here

Yet you can still stretch it with the mouse:

enter image description here

你是我的挚爱i 2024-08-02 07:12:33

我也花了相当多的时间来弄清楚整个故事。 在网上找到这个问题的纯 XAML(零代码隐藏)答案非常困难,所以这是我的。

当我们在 Visual Studio WPF 设计器中设计窗口时,标准(默认)方法是在 Window 上定义 Width 和 Height 属性>,就像在 XAML 中一样:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="75" Width="190">
    <Grid>
        <Button Content="Right" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75" />
        <Button Content="Left" HorizontalAlignment="Left" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="75"/>
    </Grid>
</Window>

设计器预览如下所示:

在此处输入图像描述

一切看起来都很酷,但是当我们运行时对于应用程序,根据当前的 Windows 版本、主题和所有显示设置 jazz,运行时窗口有 99% 的可能性与设计的窗口不同。 这是它在我的 Windows 8.1 上的外观:

在此处输入图像描述

最初的解决方案是,就像 Oren 的答案中使用SizeTocontent 属性。 它基本上告诉 WPF 根据其内容(也称为“客户端大小”)定义窗口大小,而不是窗口本身(又称为“客户端大小 + 所有非客户端/chrome/边框完全无法控制的东西”)。

因此,我们可以将内容定义为固定大小,如下所示:

<Window x:Class="WpfApplication1.MainWindowSizeToContentCentered"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <Grid Height="40" Width="180">
        <Button Content="Right" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75" />
        <Button Content="Left" HorizontalAlignment="Left" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="75"/>
    </Grid>
</Window>

现在,运行时窗口看起来正是我们想要的样子(请注意,网格的高度和宽度与原始窗口一的值不完全相同 - 40/ 180 vs 75/190 - 这很好,就像在设计模式下一样,您现在可以永远忘记窗口高度和宽度属性):

在此处输入图像描述

调整窗口大小时有何表现? 像这样,网格居中,如果您想要这种行为,这很好:

在此处输入图像描述

但是,如果我们想要问题中的行为(“我可以调整窗口大小,但其内容不会随之调整大小,因为我指定了固定大小。”),这也是原始行为,我们可以不指定宽度和高度使用 MinWidthMinHeight,如下所示:

<Window x:Class="WpfApplication1.MainWindowSizeToContentResizable"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <Grid MinHeight="40" MinWidth="180">
        <Button Content="Right" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75" />
        <Button Content="Left" HorizontalAlignment="Left" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="75"/>
    </Grid>
</Window>

运行时窗口看起来相同,但调整大小体验现在与原始默认窗口布局相当:

在此处输入图像描述

这应该是默认的 WPF 设计器布局恕我直言。

I spend quite a time to figure that whole story too. It's surprisingly difficult to find a pure XAML (zero-code behind) answer to this question on the net, so here's mine.

When we design a Window in the Visual Studio WPF designer, the standard (and by default) way is to define Width and Height properties on the Window, like this in XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="75" Width="190">
    <Grid>
        <Button Content="Right" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75" />
        <Button Content="Left" HorizontalAlignment="Left" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="75"/>
    </Grid>
</Window>

The designer preview looks like this:

enter image description here

Everything looks cool, but when we run the application, depending on the current Windows version, theme and all display settings jazz, there are 99% chances that the runtime window will not look like the designed one. Here is what it looks on my Windows 8.1:

enter image description here

The initial solution is, like in Oren's answer to use the SizeTocontent property. It basically tells WPF to define the window size from it's content (aka "client size"), instead of the window itself (aka "client size + all that non-client/chrome/border totally incontrollable stuff").

So, we can define the Content to be of fixed size, like this:

<Window x:Class="WpfApplication1.MainWindowSizeToContentCentered"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <Grid Height="40" Width="180">
        <Button Content="Right" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75" />
        <Button Content="Left" HorizontalAlignment="Left" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="75"/>
    </Grid>
</Window>

And now, the runtime window looks exactly how we want (Note the Grid's height and width don't have exactly the same values as the original Window one - 40/180 vs 75/190 -, and that's fine, as in design mode, you can now just forget window Height and Width property forever):

enter image description here

How does that behave when the window is resized? like this, the grid is centered, which is fine if you want that behavior:

enter image description here

But, if we want the behavior in the question, ("I can resize the window, but its content doesn't resize with it, because I specified a fixed size."), which was the original behavior too, instead of specifying Width and Height, we can use MinWidth and MinHeight, like this:

<Window x:Class="WpfApplication1.MainWindowSizeToContentResizable"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <Grid MinHeight="40" MinWidth="180">
        <Button Content="Right" HorizontalAlignment="Right" Margin="0,0,10,10" VerticalAlignment="Bottom" Width="75" />
        <Button Content="Left" HorizontalAlignment="Left" Margin="10,0,0,10" VerticalAlignment="Bottom" Width="75"/>
    </Grid>
</Window>

The runtime window looks the same, but the resize experience is now comparable to the original default Window layout:

enter image description here

That should be the default WPF designer layout IMHO.

对你再特殊 2024-08-02 07:12:33

我在构造函数中执行以下操作,并在 xaml 中添加 ResizeMode="CanResizeWithGrip",但这取决于内容在启动时占用的空间大小

public Window1()
{
    this.Height = SystemParameters.WorkArea.Height;
    this.Width = SystemParameters.WorkArea.Width;
}

I do the following in the constructor and add ResizeMode="CanResizeWithGrip" in the xaml, but it kind of depends on how much space your content occupies on startup

public Window1()
{
    this.Height = SystemParameters.WorkArea.Height;
    this.Width = SystemParameters.WorkArea.Width;
}
舂唻埖巳落 2024-08-02 07:12:33

Simon Mourier 发布了一个很好的答案,但我需要一个控件的大小来遵循其他控件的大小。 因此,使用 SizeToContent 属性反转窗口大小调整行为并不是我所需要的。 我最终关注了 [Tim's] [计算非客户区大小的方法]从主窗口的动态宽度和高度中减去非客户区; 在 XAML 元素中。 这是通过读取 SystemParameters.WindowCaptionHeight 和 SystemParameters.ResizeFramVerticalBorderWidth 属性来完成的(请参阅下面的 IMultiValueConverter 代码)。

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition x:Name="_mwR0" Height="Auto"/>
    <RowDefinition x:Name="_mwR1" Height="4*"/>
    <RowDefinition x:Name="_mwR2" Height="Auto"/>
    <RowDefinition x:Name="_mwR3">
      <RowDefinition.Height>
        <MultiBinding Converter="{StaticResource SizeToRemainderConverter}" Mode="TwoWay">
          <Binding ElementName="_LogWindow" Path="Height" Mode="TwoWay"/>
          <Binding ElementName="_MainWindow" Path="Height" Mode="OneWay"/>
          <Binding ElementName="_mwR0" Path="Height" Mode="OneWay"/>
          <Binding ElementName="_mwR1" Path="Height" Mode="OneWay"/>
          <Binding ElementName="_mwR2" Path="Height" Mode="OneWay"/>
        </MultiBinding>
      </RowDefinition.Height>
    </RowDefinition>
  </Grid.RowDefinitions>
  <Menu IsMainMenu="True" Grid.Row="0">...</Menu>
  <ListView Grid.Row="1">...</ListView>
  <GridSplitter Grid.Row="2" ShowsPreview="True" HorizontalAlignment="Stretch" Height="6" VerticalAlignment="Center" Margin="0"/>
  <RichTextBox x:Name="_LogWindow" Grid.Row="3"/>
</Grid>

_LogWindow 是网格最后一行的内部控件。 当主窗口调整大小时,我需要缩小此控件以适应其他控件。

该转换器由于必须处理 System.Double 和 System.Windows.GridLength 对象类型而变得复杂。 我还保留了应用程序会话之间的布局,因此我需要转换器是双向的(对密集的代码表示歉意)。

public class SizeToRemainderConverter : IMultiValueConverter
{
  public object Convert(object[] values, Type targetType, object parm, System.Globalization.CultureInfo culture)
  {
    double ret = 0.0;
    if (values != null && values.Length > 0)
    {
      if (values[0].GetType().Name.Equals("String")) double.TryParse((values[0] as string), out ret);
      else if (values[0].GetType().Name.Equals("GridLength")) ret = ((GridLength)values[0]).Value;
      else ret = (double)System.Convert.ChangeType(values[0], typeof(double));
    }

    double available = 0.0;
    if (values != null && values.Length > 1)
    {
      if (values[1].GetType().Name.Equals("String")) double.TryParse((values[1] as string), out available);
      else if (values[1].GetType().Name.Equals("GridLength")) available = ((GridLength)values[1]).Value;
      else available = (double)System.Convert.ChangeType(values[1], typeof(double));

      available -= SystemParameters.WindowCaptionHeight;
      available -= SystemParameters.ResizeFrameVerticalBorderWidth;
      available -= SystemParameters.ResizeFrameVerticalBorderWidth;
    }

    for (int i = 2; i < (values?.Length ?? 0); ++i)
    {
      double delta = 0.0;

      if (values[i].GetType().Name.Equals("String")) double.TryParse((values[i] as string), out delta);
      else if (values[i].GetType().Name.Equals("GridLength")) delta = ((GridLength)values[i]).Value;
      else delta = (double)System.Convert.ChangeType(values[i], typeof(double));
      available -= delta;
    }

    if (available < ret) ret = 0.0;

    if (targetType.Name.Equals("GridLength")) return new GridLength(ret);
    return System.Convert.ChangeType(ret, targetType);
  }

  public object[] ConvertBack(object v, Type[] t, object p, System.Globalization.CultureInfo c)
  {
    object[] ret = new object[t.Length];
    switch (v.GetType().Name)
    {
      case "Double":
        for (int i = 0; i < t.Length; ++i)
        {
          if (t[i].Name.Equals("String")) ret[i] = v.ToString();
          else if (t[i].Name.Equals("GridLength")) ret[i] = new GridLength((v as double?) ?? 0.0);
          else ret[i] = System.Convert.ChangeType(v, t[i]);
        }
        break;

      case "GridLength":
        GridLength gl = (v as GridLength?) ?? new GridLength();
        for (int i = 0; i < t.Length; ++i)
        {
          if (t[i].Name.Equals("String")) ret[i] = gl.Value.ToString();
          else if (t[i].Name.Equals("GridLength")) ret[i] = gl;
          else ret[i] = System.Convert.ChangeType(gl.Value, t[i]);
        }
        break;

      case "String":
      default:
        double d = 0.0;
        double.TryParse(v as string, out d);
        for (int i = 0; i < t.Length; ++i)
        {
          if (t[i].Name.Equals("String")) ret[i] = v.ToString();
          else if (t[i].Name.Equals("GridLength")) ret[i] = new GridLength(d);
          else ret[i] = System.Convert.ChangeType(v, t[i]);
        }
        break;
    }

    return ret;
  }
}

Simon Mourier posts an excellent answer, but I needed the size of one control to defer to the others. So inverting the window sizing behavior with the SizeToContent attribute was not what I needed. I ended up following [Tim's] [method to compute the non-client size] to subtract out the non-client area from the MainWindow's dynamic Width and Height; in a XAML <MultiBinding> element. This was accomplished through reading the SystemParameters.WindowCaptionHeight and SystemParameters.ResizeFramVerticalBorderWidth properties (see the IMultiValueConverter code below).

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition x:Name="_mwR0" Height="Auto"/>
    <RowDefinition x:Name="_mwR1" Height="4*"/>
    <RowDefinition x:Name="_mwR2" Height="Auto"/>
    <RowDefinition x:Name="_mwR3">
      <RowDefinition.Height>
        <MultiBinding Converter="{StaticResource SizeToRemainderConverter}" Mode="TwoWay">
          <Binding ElementName="_LogWindow" Path="Height" Mode="TwoWay"/>
          <Binding ElementName="_MainWindow" Path="Height" Mode="OneWay"/>
          <Binding ElementName="_mwR0" Path="Height" Mode="OneWay"/>
          <Binding ElementName="_mwR1" Path="Height" Mode="OneWay"/>
          <Binding ElementName="_mwR2" Path="Height" Mode="OneWay"/>
        </MultiBinding>
      </RowDefinition.Height>
    </RowDefinition>
  </Grid.RowDefinitions>
  <Menu IsMainMenu="True" Grid.Row="0">...</Menu>
  <ListView Grid.Row="1">...</ListView>
  <GridSplitter Grid.Row="2" ShowsPreview="True" HorizontalAlignment="Stretch" Height="6" VerticalAlignment="Center" Margin="0"/>
  <RichTextBox x:Name="_LogWindow" Grid.Row="3"/>
</Grid>

_LogWindow is the inner control to the last row of the grid. As the MainWindow is resized, I need to shrink this control in deference to the others.

The converter is complicated by having to handle both System.Double and System.Windows.GridLength object types. I also preserve the layout between application sessions, so I needed the converter to be bi-directional (apologies for the dense code).

public class SizeToRemainderConverter : IMultiValueConverter
{
  public object Convert(object[] values, Type targetType, object parm, System.Globalization.CultureInfo culture)
  {
    double ret = 0.0;
    if (values != null && values.Length > 0)
    {
      if (values[0].GetType().Name.Equals("String")) double.TryParse((values[0] as string), out ret);
      else if (values[0].GetType().Name.Equals("GridLength")) ret = ((GridLength)values[0]).Value;
      else ret = (double)System.Convert.ChangeType(values[0], typeof(double));
    }

    double available = 0.0;
    if (values != null && values.Length > 1)
    {
      if (values[1].GetType().Name.Equals("String")) double.TryParse((values[1] as string), out available);
      else if (values[1].GetType().Name.Equals("GridLength")) available = ((GridLength)values[1]).Value;
      else available = (double)System.Convert.ChangeType(values[1], typeof(double));

      available -= SystemParameters.WindowCaptionHeight;
      available -= SystemParameters.ResizeFrameVerticalBorderWidth;
      available -= SystemParameters.ResizeFrameVerticalBorderWidth;
    }

    for (int i = 2; i < (values?.Length ?? 0); ++i)
    {
      double delta = 0.0;

      if (values[i].GetType().Name.Equals("String")) double.TryParse((values[i] as string), out delta);
      else if (values[i].GetType().Name.Equals("GridLength")) delta = ((GridLength)values[i]).Value;
      else delta = (double)System.Convert.ChangeType(values[i], typeof(double));
      available -= delta;
    }

    if (available < ret) ret = 0.0;

    if (targetType.Name.Equals("GridLength")) return new GridLength(ret);
    return System.Convert.ChangeType(ret, targetType);
  }

  public object[] ConvertBack(object v, Type[] t, object p, System.Globalization.CultureInfo c)
  {
    object[] ret = new object[t.Length];
    switch (v.GetType().Name)
    {
      case "Double":
        for (int i = 0; i < t.Length; ++i)
        {
          if (t[i].Name.Equals("String")) ret[i] = v.ToString();
          else if (t[i].Name.Equals("GridLength")) ret[i] = new GridLength((v as double?) ?? 0.0);
          else ret[i] = System.Convert.ChangeType(v, t[i]);
        }
        break;

      case "GridLength":
        GridLength gl = (v as GridLength?) ?? new GridLength();
        for (int i = 0; i < t.Length; ++i)
        {
          if (t[i].Name.Equals("String")) ret[i] = gl.Value.ToString();
          else if (t[i].Name.Equals("GridLength")) ret[i] = gl;
          else ret[i] = System.Convert.ChangeType(gl.Value, t[i]);
        }
        break;

      case "String":
      default:
        double d = 0.0;
        double.TryParse(v as string, out d);
        for (int i = 0; i < t.Length; ++i)
        {
          if (t[i].Name.Equals("String")) ret[i] = v.ToString();
          else if (t[i].Name.Equals("GridLength")) ret[i] = new GridLength(d);
          else ret[i] = System.Convert.ChangeType(v, t[i]);
        }
        break;
    }

    return ret;
  }
}
窗影残 2024-08-02 07:12:33

我不知道为什么你需要复杂的代码。 这对我来说一直很好:

<Window x:Class="Loadsheet_2._0.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Loadsheet_2._0"
    mc:Ignorable="d"
    Title="MainWindow" MinHeight="635" MinWidth="1200" SizeToContent="WidthAndHeight" ResizeMode="CanResize">

I don't know, why you would need complicated code behind for that. This always worked fine for me:

<Window x:Class="Loadsheet_2._0.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Loadsheet_2._0"
    mc:Ignorable="d"
    Title="MainWindow" MinHeight="635" MinWidth="1200" SizeToContent="WidthAndHeight" ResizeMode="CanResize">
给我一枪 2024-08-02 07:12:33

这是一个完全本机的方法,不需要任何 XAML,并且在显示窗口之前工作。

public unsafe override void SetSize(int width, int height, WindowSizeType type)
        {
            if (type == WindowSizeType.WorkingArea)// aka client area
            {
                // get native HWND handle
                IntPtr handle = new WindowInteropHelper(window).EnsureHandle();

                // get window rect and size
                RECT rect = new RECT();
                int result = GetWindowRect(handle, ref rect);
                if (result == 0) throw new Exception("GetWindowRect failed");
                int rectWidth = rect.right - rect.left;
                int rectHeight = rect.bottom - rect.top;

                // get client rect and size
                RECT clientRect = new RECT();
                result = GetClientRect(handle, ref clientRect);
                if (result == 0) throw new Exception("GetClientRect failed");
                int clientRectWidth = clientRect.right - clientRect.left;
                int clientRectHeight = clientRect.bottom - clientRect.top;

                // increase size based on client side decoration delta
                width = width + (rectWidth - clientRectWidth);
                height = height + (rectHeight - clientRectHeight);

                // apply new adjusted window size
                result = SetWindowPos(handle, IntPtr.Zero, 0, 0, width, height, SWP_NOMOVE);
                if (result == 0) throw new Exception("SetWindowPos failed");
            }
            else
            {
                window.Width = width;
                window.Height = height;
            }
        }

        #region SetSize native Helpers
        [StructLayout(LayoutKind.Sequential)]
        struct RECT
        {
            public int left, top, right, bottom;
        }

        private const string lib = "User32.dll";

        [DllImport(lib, EntryPoint = "GetWindowRect")]
        private extern static int GetWindowRect(IntPtr hWnd, ref RECT lpRect);

        [DllImport(lib, EntryPoint = "GetClientRect")]
        private extern static int GetClientRect(IntPtr hWnd, ref RECT lpRect);

        [DllImport(lib, EntryPoint = "SetWindowPos")]
        private extern static int SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
        private const int SWP_NOMOVE = 0x0002;
        #endregion

Here is a fully native approch that doesn't require any XAML and works before the Window is shown.

public unsafe override void SetSize(int width, int height, WindowSizeType type)
        {
            if (type == WindowSizeType.WorkingArea)// aka client area
            {
                // get native HWND handle
                IntPtr handle = new WindowInteropHelper(window).EnsureHandle();

                // get window rect and size
                RECT rect = new RECT();
                int result = GetWindowRect(handle, ref rect);
                if (result == 0) throw new Exception("GetWindowRect failed");
                int rectWidth = rect.right - rect.left;
                int rectHeight = rect.bottom - rect.top;

                // get client rect and size
                RECT clientRect = new RECT();
                result = GetClientRect(handle, ref clientRect);
                if (result == 0) throw new Exception("GetClientRect failed");
                int clientRectWidth = clientRect.right - clientRect.left;
                int clientRectHeight = clientRect.bottom - clientRect.top;

                // increase size based on client side decoration delta
                width = width + (rectWidth - clientRectWidth);
                height = height + (rectHeight - clientRectHeight);

                // apply new adjusted window size
                result = SetWindowPos(handle, IntPtr.Zero, 0, 0, width, height, SWP_NOMOVE);
                if (result == 0) throw new Exception("SetWindowPos failed");
            }
            else
            {
                window.Width = width;
                window.Height = height;
            }
        }

        #region SetSize native Helpers
        [StructLayout(LayoutKind.Sequential)]
        struct RECT
        {
            public int left, top, right, bottom;
        }

        private const string lib = "User32.dll";

        [DllImport(lib, EntryPoint = "GetWindowRect")]
        private extern static int GetWindowRect(IntPtr hWnd, ref RECT lpRect);

        [DllImport(lib, EntryPoint = "GetClientRect")]
        private extern static int GetClientRect(IntPtr hWnd, ref RECT lpRect);

        [DllImport(lib, EntryPoint = "SetWindowPos")]
        private extern static int SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
        private const int SWP_NOMOVE = 0x0002;
        #endregion
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文