web-dev-qa-db-ja.com

WPFウィンドウのスタートアップClientSizeを設定するにはどうすればよいですか?

WPFウィンドウの初期clientサイズを設定したい。私はこれを行う簡単な方法を見ていません。

具体的には、ウィンドウが開いたときに、スクロールバーを必要とせずにコンテンツが収まるだけの大きさにする必要があります。しかし、それが表示された後、私はウィンドウを自由にサイズ変更できるようにしたい(大きいか小さいか)。

Window要素にWidth属性とHeight属性を設定すると、非クライアント(外側)サイズが設定されますが、これは役に立ちません。タイトルバーとサイズ変更の境界線がそのスペースに食い込むと、クライアント領域はそのコンテンツに対して十分な大きさではなくなり、スクロールバーが表示されます。より大きなサイズを選択することで補正できますが、タイトルバーの高さと境界線の太さはどちらもユーザーがカスタマイズでき(デフォルトはOSのバージョンによって異なります)、必ずしも別のマシンで同じになるとは限りません。

ウィンドウのコンテンツ要素に幅と高さを設定できます(<Grid>この場合)、ウィンドウのSizeToContent属性をWidthAndHeightに設定します。これにより、ウィンドウの初期サイズが希望どおりに正確に取得されます。しかし、その後はサイズが変更されなくなります。ウィンドウのサイズを変更することはできますが、固定サイズを指定したため、コンテンツのサイズは変更されません。

ウィンドウの初期クライアントサイズを、できればコードビハインドなしで設定する方法はありますか? (それが唯一の方法である場合はコードビハインドを採用しますが、誰かが持っている場合はXAMLのみのアプローチをお勧めします。)

17
Joe White

次の2つの方法のいずれかで、Loadイベントハンドラーのコードビハインドでこれを行うことができます。

注:LayoutRootグリッドの内容は両方の例で同じですが、LayoutRootの幅と高さは例Aでのみ指定されています。

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

次のような比例ページレイアウトを想定しています(ウィンドウに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で宣言的にそれを行う方法を思い付くことができませんでした。

20
Tim Erickson

XAMLでウィンドウのWidth属性とHeight属性を削除し、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>

開始すると、次のようになります。

代替テキストhttp://img7.imageshack.us/img7/1285/onstart.png

それでも、マウスで伸ばすことができます。

代替テキストhttp://img16.imageshack.us/img16/6687/stretched.png

13
Oren Trutner

私もその全体の話を理解するためにかなりの時間を費やしています。ネット上でこの質問に対する純粋なXAML(ゼロコードビハインド)の答えを見つけるのは驚くほど難しいので、ここに私のものがあります。

Visual Studio WPFデザイナーでウィンドウを設計する場合、標準の(そしてデフォルトで)方法は、XAMLのように、WidthHeightプロパティとWindowプロパティを定義することです。 :

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

デザイナーのプレビューは次のようになります。

enter image description here

すべてがかっこいいように見えますが、アプリケーションを実行すると、現在のWindowsのバージョン、テーマ、およびすべての表示設定のジャズに応じて、ランタイムウィンドウが見えない可能性が99%あります。設計されたもの。これが私のWindows8.1での表示です。

enter image description here

最初の解決策は、Orenの回答のように、SizeTocontentプロパティを使用することです。これは基本的に、ウィンドウ自体(別名「クライアントサイズ+クライアント/クローム/ボーダー以外のすべての完全に制御できないもの」)ではなく、コンテンツからウィンドウサイズ(別名「クライアントサイズ」)を定義するようにWPFに指示します。

したがって、次のように、コンテンツを固定サイズに定義できます。

<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対75/190-そしてそれはデザインモードのように問題ありません、これで、ウィンドウの高さと幅のプロパティを忘れることができます永久):

enter image description here

ウィンドウのサイズが変更されたとき、それはどのように動作しますか?このように、グリッドは中央に配置されます。これは、その動作が必要な場合は問題ありません。

enter image description here

ただし、質問の動作が必要な場合は(「ウィンドウのサイズを変更できますが、固定サイズを指定したため、コンテンツのサイズは変更されません。」)、これも幅を指定する代わりに元の動作でした。と高さ、次のように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>

ランタイムウィンドウは同じように見えますが、サイズ変更のエクスペリエンスは元のデフォルトのウィンドウレイアウトと同等になりました。

enter image description here

これは、デフォルトのWPFデザイナーレイアウトIMHOである必要があります。

6
Simon Mourier

コンストラクターで次のことを行い、xamlにResizeMode = "CanResizeWithGrip"を追加しますが、起動時にコンテンツが占めるスペースの量によって異なります。

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

Simon Mourier 優れた回答を投稿しますが、他のコントロールに合わせるには、1つのコントロールのサイズが必要でした。したがって、SizeToContent属性を使用してウィンドウのサイズ変更動作を反転することは、私が必要としていたことではありませんでした。私は最終的に [Tim's][非クライアントサイズを計算する方法] メインウィンドウの動的な幅と高さから非クライアント領域を差し引くことになりました。 XAML <MultiBinding>要素内。これは、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は、グリッドの最後の行の内部コントロールです。 MainWindowのサイズが変更されると、他のコントロールとは異なり、このコントロールを縮小する必要があります。

コンバーターは、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;
  }
}
0
ergohack

これは、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
0
zezba9000