トップ回答者
ViewBoxを横方向にのみ拡大・縮小させる方法

質問
-
等倍でウィンドウサイズを変更するように強制する。
http://qiita.com/yu_ka1984/items/b4a3ce9ed7750bd67b86
上記サイトとViewBoxを組み合わせて、縦横比率を保って
画面を縮小・拡大できるアプリ画面を作成しています。
しかし、ユーザーの要望から
縦にスクロールできるScrollViewerのある画面では
・横方向にサイズ変更した場合には、現在画面の比率で縮小・拡大
・縦方向にサイズ変更した場合には、縮小拡大せずに中のコンテンツを可能な限り表示
(画面を縦方向に拡大した場合は拡大した分、ScrollViewerと中の内容がが拡大され、
内容が全て表示されれば、ScrollBarが消えるようなイメージ)
という制御をおこなわなければいけなくなりました。
ViewBoXだけでは対応できないかと思われるのですが
上記の要望をWPFで対応できるのでしょうか
回答
-
何か勘違いしていたらすみません。
上部のグレーの領域の高さを固定するには、対応する
<RowDefinition Height=".5*" />
の部分を
<RowDefinition Height="100" />
とすると高さが100ピクセルに固定されると思います。
ウィンドウを横方向にサイズ変更した場合に、上部のグレーの領域をサイズ変更前の比率を保って拡縮するのであれば、コードで値を変更する必要がある(?)のかなと思います。
<Window x:Class="WpfApplication1.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:WpfApplication1" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Grid > <Grid.RowDefinitions> <RowDefinition x:Name="topBarDef" Height="100" /> <RowDefinition Height="5" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid Grid.Row="0" Background="DimGray"> <StackPanel Orientation="Horizontal" > <Button Width="100" Height="30" Margin="5" Content="新規"/> <Button Width="100" Height="30" Margin="5" Content="更新"/> <Button Width="100" Height="30" Margin="5" Content="終了" /> </StackPanel> </Grid> <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Grid.Row="2" VerticalAlignment="Stretch" > <Viewbox Stretch="Uniform" VerticalAlignment="Top"> <Grid Width=" 200" Height="200"> <Grid.RowDefinitions > <RowDefinition Height="*"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid Background="Red" > <Button Width="100" Height="20" Content="仮のボタン" HorizontalAlignment="Left" Margin="10"/> </Grid> <Grid Background="Orange" Grid.Row="1"/> <Grid Background="yellow" Grid.Row="2"/> <Grid Background="GreenYellow" Grid.Row="3"/> <Grid Background="Green" Grid.Row="4"/> <Grid Background="Blue" Grid.Row="5"/> </Grid> </Viewbox> </ScrollViewer> </Grid> </Window>
using System; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; namespace WpfApplication1 { public partial class MainWindow : Window { private double fixedRate = 1.0; private double topBarRate = 1.0; public MainWindow() { InitializeComponent(); } [StructLayout(LayoutKind.Sequential)] private struct RECT { public int left; public int top; public int right; public int bottom; } protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); IntPtr handle = (new WindowInteropHelper(this)).Handle; HwndSource hwndSource = (HwndSource)HwndSource.FromVisual(this); hwndSource.AddHook(WndHookProc); } const int WM_SIZING = 0x214; const int WM_ENTERSIZEMOVE = 0x0231; const int WMSZ_LEFT = 1; const int WMSZ_RIGHT = 2; [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); private IntPtr WndHookProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == WM_SIZING) { RECT rect = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT)); int width = rect.right - rect.left; int height = rect.bottom - rect.top; double dheight = (width / fixedRate) - height; switch (wParam.ToInt32()) { case WMSZ_LEFT: case WMSZ_RIGHT: topBarDef.Height = new System.Windows.GridLength(topBarDef.ActualHeight + dheight * topBarRate); rect.bottom += (int)dheight; break; } Marshal.StructureToPtr(rect, lParam, false); } else if (msg == WM_ENTERSIZEMOVE) { var rect = new RECT(); GetWindowRect(hwnd, ref rect); fixedRate = (double)(rect.right - rect.left) / (double)(rect.bottom - rect.top); // アスペクト比を保持 topBarRate = topBarDef.ActualHeight / (double)(rect.bottom - rect.top); // ウィンドウ高さに対するトップバーの比率を保持 } return IntPtr.Zero; } } }
- 編集済み kenjinoteMVP 2017年1月27日 2:03
- 回答としてマーク XAMLVB 2017年1月27日 6:09
すべての返信
-
下記のようなXAMLで目的の動作になりますでしょうか?
<Window x:Class="WpfApplication1.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:WpfApplication1" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"> <Viewbox Stretch="Uniform"> <Image Source="image.png" /> </Viewbox> </ScrollViewer> </Window>
using System; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; namespace WpfApplication1 { public partial class MainWindow : Window { private double fixedRate = 1.0; public MainWindow() { InitializeComponent(); } [StructLayout(LayoutKind.Sequential)] private struct RECT { public int left; public int top; public int right; public int bottom; } protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); IntPtr handle = (new WindowInteropHelper(this)).Handle; HwndSource hwndSource = (HwndSource)HwndSource.FromVisual(this); hwndSource.AddHook(WndHookProc); } const int WM_SIZING = 0x214; const int WM_ENTERSIZEMOVE = 0x0231; const int WMSZ_LEFT = 1; const int WMSZ_RIGHT = 2; [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); private IntPtr WndHookProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == WM_SIZING) { RECT rect = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT)); int width = rect.right - rect.left; int height = rect.bottom - rect.top; int dheight = (int)(width / fixedRate + 0.5) - height; switch (wParam.ToInt32()) { case WMSZ_LEFT: case WMSZ_RIGHT: rect.bottom += dheight; break; } Marshal.StructureToPtr(rect, lParam, false); } else if(msg == WM_ENTERSIZEMOVE) { // ウィンドウ幅のドラッグ開始時にアスペクト比を保持 var rect = new RECT(); GetWindowRect(hwnd, ref rect); fixedRate = (double)(rect.right - rect.left) / (double)(rect.bottom - rect.top); } return IntPtr.Zero; } } }
「横方向にサイズ変更した場合には、現在画面の比率で縮小・拡大」を行うためコード追記しました。- 編集済み kenjinoteMVP 2017年1月26日 9:38
- 回答の候補に設定 栗下 望Microsoft employee, Moderator 2017年1月27日 0:33
-
kenjinote 様
ご回答ありがとうございます。
早速、ソースを実行しました。XAMLがSCrollViewerだけなら、正に想定通りなのですが
画面のXAMLは概要ですが下記のようになります。
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height=".5*" />
<RowDefinition Height=".1*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="DimGray">
<StackPanel Orientation="Horizontal" >
<Button Width="100" Height="30" Margin="5" Content="新規"/>
<Button Width="100" Height="30" Margin="5" Content="更新"/>
<Button Width="100" Height="30" Margin="5" Content="終了" />
</StackPanel>
</Grid>
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Grid.Row="2" VerticalAlignment="Stretch" >
<Viewbox Stretch="Uniform">
<Grid Width=" 200" Height="200">
<Grid.RowDefinitions >
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Background="Red" >
<Button Width="100" Height="20" Content="仮のボタン" HorizontalAlignment="Left" Margin="10"/>
</Grid>
<Grid Background="Orange" Grid.Row="1"/>
<Grid Background="yellow" Grid.Row="2"/>
<Grid Background="GreenYellow" Grid.Row="3"/>
<Grid Background="Green" Grid.Row="4"/>
<Grid Background="Blue" Grid.Row="5"/>
</Grid>
</Viewbox>
</ScrollViewer>
</Grid>
</Window>上記のような画面構成でSceollViewerだけが縦にひっぱった際に広がればと考えています
Kenjinote様のソースを元にすれば、何とかできそうなのですが、もし良い方法があればご提示頂ければ
ありがたいです
-
何か勘違いしていたらすみません。
上部のグレーの領域の高さを固定するには、対応する
<RowDefinition Height=".5*" />
の部分を
<RowDefinition Height="100" />
とすると高さが100ピクセルに固定されると思います。
ウィンドウを横方向にサイズ変更した場合に、上部のグレーの領域をサイズ変更前の比率を保って拡縮するのであれば、コードで値を変更する必要がある(?)のかなと思います。
<Window x:Class="WpfApplication1.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:WpfApplication1" mc:Ignorable="d" Title="MainWindow" Height="350" Width="525"> <Grid > <Grid.RowDefinitions> <RowDefinition x:Name="topBarDef" Height="100" /> <RowDefinition Height="5" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Grid Grid.Row="0" Background="DimGray"> <StackPanel Orientation="Horizontal" > <Button Width="100" Height="30" Margin="5" Content="新規"/> <Button Width="100" Height="30" Margin="5" Content="更新"/> <Button Width="100" Height="30" Margin="5" Content="終了" /> </StackPanel> </Grid> <ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto" Grid.Row="2" VerticalAlignment="Stretch" > <Viewbox Stretch="Uniform" VerticalAlignment="Top"> <Grid Width=" 200" Height="200"> <Grid.RowDefinitions > <RowDefinition Height="*"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <Grid Background="Red" > <Button Width="100" Height="20" Content="仮のボタン" HorizontalAlignment="Left" Margin="10"/> </Grid> <Grid Background="Orange" Grid.Row="1"/> <Grid Background="yellow" Grid.Row="2"/> <Grid Background="GreenYellow" Grid.Row="3"/> <Grid Background="Green" Grid.Row="4"/> <Grid Background="Blue" Grid.Row="5"/> </Grid> </Viewbox> </ScrollViewer> </Grid> </Window>
using System; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; namespace WpfApplication1 { public partial class MainWindow : Window { private double fixedRate = 1.0; private double topBarRate = 1.0; public MainWindow() { InitializeComponent(); } [StructLayout(LayoutKind.Sequential)] private struct RECT { public int left; public int top; public int right; public int bottom; } protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); IntPtr handle = (new WindowInteropHelper(this)).Handle; HwndSource hwndSource = (HwndSource)HwndSource.FromVisual(this); hwndSource.AddHook(WndHookProc); } const int WM_SIZING = 0x214; const int WM_ENTERSIZEMOVE = 0x0231; const int WMSZ_LEFT = 1; const int WMSZ_RIGHT = 2; [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); private IntPtr WndHookProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == WM_SIZING) { RECT rect = (RECT)Marshal.PtrToStructure(lParam, typeof(RECT)); int width = rect.right - rect.left; int height = rect.bottom - rect.top; double dheight = (width / fixedRate) - height; switch (wParam.ToInt32()) { case WMSZ_LEFT: case WMSZ_RIGHT: topBarDef.Height = new System.Windows.GridLength(topBarDef.ActualHeight + dheight * topBarRate); rect.bottom += (int)dheight; break; } Marshal.StructureToPtr(rect, lParam, false); } else if (msg == WM_ENTERSIZEMOVE) { var rect = new RECT(); GetWindowRect(hwnd, ref rect); fixedRate = (double)(rect.right - rect.left) / (double)(rect.bottom - rect.top); // アスペクト比を保持 topBarRate = topBarDef.ActualHeight / (double)(rect.bottom - rect.top); // ウィンドウ高さに対するトップバーの比率を保持 } return IntPtr.Zero; } } }
- 編集済み kenjinoteMVP 2017年1月27日 2:03
- 回答としてマーク XAMLVB 2017年1月27日 6:09