質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

3回答

4064閲覧

GridSplitterを使用して変更したGridのサイズを取得したい

tuyudaku

総合スコア75

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

1グッド

1クリップ

投稿2021/08/19 07:13

現在、アプリケーション上で画面レイアウトの設定をGUIで変更する仕組みを実装しています。
ちょっと分かりにくいかもしれませんが。

![画面例

画像のようなパネルを表示して、パネルの境界線をドラッグすることで①と②のサイズを変更して、
それぞれのパネルのサイズをファイルに保存するというものを考えています。

このパネルはユーザーコントロールを定義して、GridとGridSplitterを使っています。
依存関係プロパティも定義しており、外部からパネルの幅と高さをBindできるようにはしてあります。

この外部からBindされた値(変数)をどのように使用したら考えているような動作になるか分からず困っています。
理想はGridSplitterを動かせばBindされた変数にサイズの変わったGridの幅がそのままBind元に行くというのをイメージしているのですが、
Bindした変数の値が変わらないため、おかしな見た目になってしまいます。
どのようにすればいいか教えていただけないでしょうか?

下記に現在のソースを書いておきます。
日本語がおかしな部分があるかもしれません、足りない情報があれば指摘していただければすぐに追加いたしますので、
よろしくお願いいたします。

XAML

1 <Grid> 2 <Grid.ColumnDefinitions> 3 <ColumnDefinition Width="*"/> 4 <ColumnDefinition Width="1"/> 5 <ColumnDefinition Width="*"/> 6 </Grid.ColumnDefinitions> 7 8 <Grid Grid.Column="0" Background="Red" HorizontalAlignment="Stretch" Width="{Binding Panel1Width, ElementName=root}" > 9 <TextBlock Text="①" TextAlignment="Center" VerticalAlignment="Center" FontSize="30" FontWeight="UltraBold"/> 10 </Grid> 11 12 <Grid Grid.Column="2" Background="Blue" HorizontalAlignment="Stretch" Width="{Binding Panel2Width, ElementName=root}"> 13 <TextBlock Text="②" TextAlignment="Center" VerticalAlignment="Center" FontSize="30" FontWeight="UltraBold"/> 14 </Grid> 15 16 <GridSplitter Grid.Column="1" ResizeDirection="Columns" HorizontalAlignment="Stretch"/> 17 </Grid>

C#

1 public partial class Layout2Panel : UserControl 2 { 3 public Layout2Panel() 4 { 5 InitializeComponent(); 6 } 7 8 public double Panel1Width 9 { 10 get => (double)GetValue(Panel1WidthProperty); 11 set => SetValue(Panel1WidthProperty, value); 12 } 13 public static readonly DependencyProperty Panel1WidthProperty = 14 DependencyProperty.Register( 15 "Panel1Width", 16 typeof(double), 17 typeof(Layout2Panel), 18 new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 19 20 public double Panel1Height 21 { 22 get => (double)GetValue(Panel1HeightProperty); 23 set => SetValue(Panel1HeightProperty, value); 24 } 25 public static readonly DependencyProperty Panel1HeightProperty = 26 DependencyProperty.Register( 27 "Panel1Height", 28 typeof(double), 29 typeof(Layout2Panel), 30 new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 31 32 public double Panel1X 33 { 34 get => (double)GetValue(Panel1XProperty); 35 set => SetValue(Panel1XProperty, value); 36 } 37 public static readonly DependencyProperty Panel1XProperty = 38 DependencyProperty.Register( 39 "Panel1X", 40 typeof(double), 41 typeof(Layout2Panel), 42 new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 43 44 public double Panel1Y 45 { 46 get => (double)GetValue(Panel1YProperty); 47 set => SetValue(Panel1YProperty, value); 48 } 49 public static readonly DependencyProperty Panel1YProperty = 50 DependencyProperty.Register( 51 "Panel1Y", 52 typeof(double), 53 typeof(Layout2Panel), 54 new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 55 56 public double Panel2Width 57 { 58 get => (double)GetValue(Panel2WidthProperty); 59 set => SetValue(Panel2WidthProperty, value); 60 } 61 public static readonly DependencyProperty Panel2WidthProperty = 62 DependencyProperty.Register( 63 "Panel2Width", 64 typeof(double), 65 typeof(Layout2Panel), 66 new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 67 68 public double Panel2Height 69 { 70 get => (double)GetValue(Panel2HeightProperty); 71 set => SetValue(Panel2HeightProperty, value); 72 } 73 public static readonly DependencyProperty Panel2HeightProperty = 74 DependencyProperty.Register( 75 "Panel2Height", 76 typeof(double), 77 typeof(Layout2Panel), 78 new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 79 80 public double Panel2X 81 { 82 get => (double)GetValue(Panel2XProperty); 83 set => SetValue(Panel2XProperty, value); 84 } 85 public static readonly DependencyProperty Panel2XProperty = 86 DependencyProperty.Register( 87 "Panel2X", 88 typeof(double), 89 typeof(Layout2Panel), 90 new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 91 92 public double Panel2Y 93 { 94 get => (double)GetValue(Panel2YProperty); 95 set => SetValue(Panel2YProperty, value); 96 } 97 public static readonly DependencyProperty Panel2YProperty = 98 DependencyProperty.Register( 99 "Panel2Y", 100 typeof(double), 101 typeof(Layout2Panel), 102 new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 103 } 104
TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

追記では収まらないので、別回答にさせていただきます。

やっていることは前の回答と同じなのですが、より具体的かつシンプルになっています。
そのためまったく汎用的ではありません。

xml:MainWindow.xaml

1<Window x:Class="Questions355107.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Questions355107" Width="{Binding Settings.Window.Width, Mode=TwoWay}" 2 Height="{Binding Settings.Window.Height, Mode=TwoWay}" Left="{Binding Settings.Window.Left, Mode=TwoWay}" Top="{Binding Settings.Window.Top, Mode=TwoWay}"> 3 <Window.DataContext> 4 <local:ViewModel /> 5 </Window.DataContext> 6 <Window.Resources> 7 <DataTemplate DataType="{x:Type local:ContentModel}"> 8 <Border x:Name="b" Background="{Binding Brush}" SizeChanged="Border_SizeChanged"> 9 <Grid> 10 <TextBlock> 11 <TextBlock.Text> 12 <MultiBinding StringFormat="{}{0:f2}, {1:f2}"> 13 <Binding ElementName="b" Path="ActualWidth" /> 14 <Binding ElementName="b" Path="ActualHeight" /> 15 </MultiBinding> 16 </TextBlock.Text> 17 </TextBlock> 18 <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="30" FontWeight="Bold" Text="{Binding Text}" /> 19 </Grid> 20 </Border> 21 </DataTemplate> 22 23 <DataTemplate x:Key="OnePane"> 24 <ContentControl Content="{Binding Contents[0]}" /> 25 </DataTemplate> 26 <DataTemplate x:Key="TwoPane"> 27 <local:TwoPane /> 28 </DataTemplate> 29 <DataTemplate x:Key="ThreePane"> 30 <local:ThreePane /> 31 </DataTemplate> 32 <DataTemplate x:Key="FourPane"> 33 <local:FourPane /> 34 </DataTemplate> 35 36 <Style TargetType="GridSplitter"> 37 <Setter Property="HorizontalAlignment" Value="Stretch" /> 38 </Style> 39 40 <Style x:Key="PaneStyle" TargetType="{x:Type ContentControl}"> 41 <Style.Triggers> 42 <DataTrigger Binding="{Binding SelPane}" Value="1"> 43 <Setter Property="ContentTemplate" Value="{StaticResource OnePane}" /> 44 </DataTrigger> 45 <DataTrigger Binding="{Binding SelPane}" Value="2"> 46 <Setter Property="ContentTemplate" Value="{StaticResource TwoPane}" /> 47 </DataTrigger> 48 <DataTrigger Binding="{Binding SelPane}" Value="3"> 49 <Setter Property="ContentTemplate" Value="{StaticResource ThreePane}" /> 50 </DataTrigger> 51 <DataTrigger Binding="{Binding SelPane}" Value="4"> 52 <Setter Property="ContentTemplate" Value="{StaticResource FourPane}" /> 53 </DataTrigger> 54 </Style.Triggers> 55 </Style> 56 57 <Style x:Key="RadioListBoxItem" TargetType="{x:Type ListBoxItem}"> 58 <Setter Property="Template"> 59 <Setter.Value> 60 <ControlTemplate TargetType="{x:Type ListBoxItem}"> 61 <RadioButton MinWidth="30" Content="{TemplateBinding Content}" IsChecked="{Binding IsSelected, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource {x:Type ToggleButton}}" /> 62 </ControlTemplate> 63 </Setter.Value> 64 </Setter> 65 </Style> 66 </Window.Resources> 67 <DockPanel> 68 <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> 69 <Button Command="{Binding Save}" Content="Save" /> 70 <Button Command="{Binding Load}" Content="Load" /> 71 <ListBox Padding="-1" BorderThickness="0" ItemContainerStyle="{StaticResource RadioListBoxItem}" ItemsSource="{Binding Panes}" SelectedItem="{Binding SelPane}"> 72 <ListBox.ItemsPanel> 73 <ItemsPanelTemplate> 74 <StackPanel Orientation="Horizontal" /> 75 </ItemsPanelTemplate> 76 </ListBox.ItemsPanel> 77 </ListBox> 78 <Button Command="{Binding View}" Content="View" /> 79 </StackPanel> 80 <ContentControl Content="{Binding}" Style="{StaticResource PaneStyle}" /> 81 </DockPanel> 82</Window>

cs

1using CommunityToolkit.Mvvm.ComponentModel; 2using CommunityToolkit.Mvvm.Input; 3using System.Collections.ObjectModel; 4using System.Diagnostics; 5using System.Text.Json; 6using System.Text.Json.Serialization; 7using System.Windows; 8using System.Windows.Controls; 9using System.Windows.Media; 10 11namespace Questions355107 12{ 13 public class WindowModel : ObservableObject 14 { 15 public double Top { get => top; set => SetProperty(ref top, value); } 16 private double top = double.NaN; 17 public double Left { get => left; set => SetProperty(ref left, value); } 18 private double left = double.NaN; 19 public double Width { get => width; set => SetProperty(ref width, value); } 20 private double width = 800; 21 public double Height { get => height; set => SetProperty(ref height, value); } 22 private double height = 450; 23 } 24 25 // 分割割合保存復元用 GridSplitter1本に対し文字列2つ 26 // 大元(Settings)から変わるので、SetPropertyしなくてよかった模様 27 // ついでに配列化してすっきり 28 public class SplitterModel 29 { 30 public string[] TwoPane { get; set; } = { "1*", "1*", }; 31 public string[] ThreePane { get; set; } = { "3*", "2*", "1*", "1*" }; 32 public string[] FourPane { get; set; } = { "1*", "1*", "1*", "1*", }; 33 } 34 35 public class SettingsModel 36 { 37 public WindowModel Window { get; set; } = new(); 38 public SplitterModel Splitter { get; set; } = new(); 39 } 40 41 public class ContentModel 42 { 43 public string Text { get; set; } 44 45 // 実際に欲しい数値はここ 46 // Viewから値が入るのみ コードから値を入れてもViewには反映しないので注意 47 public double Width { get; set; } 48 public double Height { get; set; } 49 public Brush Brush { get; set; } 50 } 51 52 public class ViewModel : ObservableObject 53 { 54 public SettingsModel Settings { get => settings; set => SetProperty(ref settings, value); } 55 private SettingsModel settings; 56 57 public int[] Panes { get; } = { 1, 2, 3, 4, }; 58 public int SelPane { get => selPane; set => SetProperty(ref selPane, value); } 59 private int selPane = 3; 60 public ObservableCollection<ContentModel> Contents { get; } 61 62 public RelayCommand Save { get; } 63 public RelayCommand Load { get; } 64 public RelayCommand View { get; } 65 66 private string settingsJson; 67 private readonly JsonSerializerOptions options = new() 68 { 69 NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals, 70 WriteIndented = true, 71 }; 72 73 public ViewModel() 74 { 75 Settings = new(); 76 Contents = new() 77 { 78 new() { Text = "①", Brush = Brushes.LightCoral, }, 79 new() { Text = "②", Brush = Brushes.LightGreen, }, 80 new() { Text = "③", Brush = Brushes.SkyBlue, }, 81 new() { Text = "④", Brush = Brushes.Pink, }, 82 }; 83 Save = new(() => 84 { 85 settingsJson = JsonSerializer.Serialize(Settings, options); 86 Debug.WriteLine(settingsJson); 87 }); 88 Load = new(() => 89 { 90 Settings = null; 91 Settings = JsonSerializer.Deserialize<SettingsModel>(settingsJson, options); 92 }); 93 View = new(() => 94 { 95 foreach (var c in Contents) 96 Debug.WriteLine($"{c.Text}: {c.Width:f2}, {c.Height:f2}"); 97 }); 98 Save.Execute(null); 99 } 100 } 101 102 public partial class MainWindow : Window 103 { 104 public MainWindow() => InitializeComponent(); 105 private void Border_SizeChanged(object sender, SizeChangedEventArgs e) 106 { 107 if (sender is Border b && b.DataContext is ContentModel c) 108 { 109 c.Width = b.ActualWidth; 110 c.Height = b.ActualHeight; 111 } 112 } 113 } 114}

xml:TwoPane.xaml

1<UserControl x:Class="Questions355107.TwoPane" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 2 <Grid> 3 <Grid.ColumnDefinitions> 4 <ColumnDefinition Width="{Binding Settings.Splitter.TwoPane[0], Mode=TwoWay}" /> 5 <ColumnDefinition Width="5" /> 6 <ColumnDefinition Width="{Binding Settings.Splitter.TwoPane[1], Mode=TwoWay}" /> 7 </Grid.ColumnDefinitions> 8 <ContentControl Content="{Binding Contents[0]}" /> 9 <GridSplitter Grid.Column="1" /> 10 <ContentControl Grid.Column="2" Content="{Binding Contents[1]}" /> 11 </Grid> 12</UserControl>

xml:ThreePane.xaml

1<UserControl x:Class="Questions355107.ThreePane" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 2 <Grid> 3 <Grid.ColumnDefinitions> 4 <ColumnDefinition Width="{Binding Settings.Splitter.ThreePane[0], Mode=TwoWay}" /> 5 <ColumnDefinition Width="5" /> 6 <ColumnDefinition Width="{Binding Settings.Splitter.ThreePane[1], Mode=TwoWay}" /> 7 </Grid.ColumnDefinitions> 8 <ContentControl Content="{Binding Contents[0]}" /> 9 <GridSplitter Grid.Column="1" /> 10 <Grid Grid.Column="2"> 11 <Grid.RowDefinitions> 12 <RowDefinition Height="{Binding Settings.Splitter.ThreePane[2], Mode=TwoWay}" /> 13 <RowDefinition Height="5" /> 14 <RowDefinition Height="{Binding Settings.Splitter.ThreePane[3], Mode=TwoWay}" /> 15 </Grid.RowDefinitions> 16 <ContentControl Content="{Binding Contents[1]}" /> 17 <GridSplitter Grid.Row="1" /> 18 <ContentControl Grid.Row="2" Content="{Binding Contents[2]}" /> 19 </Grid> 20 </Grid> 21</UserControl>

xml:FourPane.xaml

1<UserControl x:Class="Questions355107.FourPane" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 2 <Grid> 3 <Grid.ColumnDefinitions> 4 <ColumnDefinition Width="{Binding Settings.Splitter.FourPane[0], Mode=TwoWay}" /> 5 <ColumnDefinition Width="5" /> 6 <ColumnDefinition Width="{Binding Settings.Splitter.FourPane[1], Mode=TwoWay}" /> 7 </Grid.ColumnDefinitions> 8 <Grid.RowDefinitions> 9 <RowDefinition Height="{Binding Settings.Splitter.FourPane[2], Mode=TwoWay}" /> 10 <RowDefinition Height="5" /> 11 <RowDefinition Height="{Binding Settings.Splitter.FourPane[3], Mode=TwoWay}" /> 12 </Grid.RowDefinitions> 13 <ContentControl Content="{Binding Contents[0]}" /> 14 <ContentControl Grid.Row="2" Content="{Binding Contents[1]}" /> 15 <ContentControl Grid.Column="2" Content="{Binding Contents[2]}" /> 16 <ContentControl Grid.Row="2" Grid.Column="2" Content="{Binding Contents[3]}" /> 17 <GridSplitter Grid.RowSpan="3" Grid.Column="1" /> 18 <GridSplitter Grid.Row="1" Grid.ColumnSpan="3" /> 19 </Grid> 20</UserControl>

アプリ画像

投稿2021/08/20 16:01

編集2023/07/28 16:40
TN8001

総合スコア9321

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TN8001

2021/08/20 16:02

文字数制限がきつくて詰め詰めです。 読みにくくて申し訳ないです^^;
tuyudaku

2021/08/23 01:12

返事が遅れて申し訳ありません! 詳細なコード例をありがとうございます! まだ私の勉強が及んでいない部分が多く、とても勉強になります。 xamlについて分かって来ていたつもりですがまだまだですね... DataTemplateは存在は知っていたけど、どういうものか、どう活用するものか分かっていなかったので、 これを機に勉強させていただきます! もしかしたら分からない部分を質問させていただくかもしれませんが、 お手すきの時がございましたら、お教えいただければ幸いです。
guest

0

GridLengthを文字列でバインドすると、StarPixcel両対応でいい感じでした。

再利用性のないユーザーコントロールに、DependencyPropertyを生やすのは趣味でないのでベタ書きです(生やすなら汎用的に作りたいという意味です)
ユーザーコントロールにするなら文字列(かGridLength)を中継するDependencyPropertyを生やすだけです。

xml

1<Window 2 x:Class="Questions355107.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="clr-namespace:Questions355107" 6 Width="{Binding Settings.Window.Width, Mode=TwoWay}" 7 Height="{Binding Settings.Window.Height, Mode=TwoWay}" 8 FontSize="30" 9 FontWeight="Bold" 10 Left="{Binding Settings.Window.Left, Mode=TwoWay}" 11 Top="{Binding Settings.Window.Top, Mode=TwoWay}"> 12 <Window.DataContext> 13 <local:ViewModel /> 14 </Window.DataContext> 15 <Window.Resources> 16 <Style TargetType="TextBlock"> 17 <Setter Property="HorizontalAlignment" Value="Center" /> 18 <Setter Property="VerticalAlignment" Value="Center" /> 19 </Style> 20 <Style TargetType="GridSplitter"> 21 <Setter Property="HorizontalAlignment" Value="Stretch" /> 22 </Style> 23 </Window.Resources> 24 <DockPanel> 25 <StackPanel 26 HorizontalAlignment="Center" 27 DockPanel.Dock="Top" 28 Orientation="Horizontal"> 29 <Button Command="{Binding SaveCommand}" Content="Save" /> 30 <Button Command="{Binding LoadCommand}" Content="Load" /> 31 </StackPanel> 32 33 <Grid> 34 <Grid.ColumnDefinitions> 35 <ColumnDefinition Width="{Binding Settings.Splitter.Outer1, Mode=TwoWay}" /> 36 <ColumnDefinition Width="5" /> 37 <ColumnDefinition Width="{Binding Settings.Splitter.Outer2, Mode=TwoWay}" /> 38 <ColumnDefinition Width="5" /> 39 <ColumnDefinition Width="{Binding Settings.Splitter.Outer3, Mode=TwoWay}" /> 40 </Grid.ColumnDefinitions> 41 <Border Background="LightCoral"> 42 <!--<TextBlock Text="outer ①" />--> 43 <Grid Margin="10"> 44 <Grid.RowDefinitions> 45 <RowDefinition Height="{Binding Settings.Splitter.Inner1, Mode=TwoWay}" /> 46 <RowDefinition Height="5" /> 47 <RowDefinition Height="{Binding Settings.Splitter.Inner2, Mode=TwoWay}" /> 48 </Grid.RowDefinitions> 49 <Border Background="Pink"> 50 <TextBlock Text="inner ①" /> 51 </Border> 52 53 <GridSplitter Grid.Row="1" /> 54 55 <Border Grid.Row="2" Background="Orange"> 56 <TextBlock Text="inner ②" /> 57 </Border> 58 </Grid> 59 </Border> 60 61 <GridSplitter Grid.Column="1" /> 62 63 <Border Grid.Column="2" Background="LightGreen"> 64 <TextBlock Text="outer ②" /> 65 </Border> 66 67 <GridSplitter Grid.Column="3" /> 68 69 <Border Grid.Column="4" Background="SkyBlue"> 70 <TextBlock Text="outer ③" /> 71 </Border> 72 </Grid> 73 </DockPanel> 74</Window>

cs

1using CommunityToolkit.Mvvm.ComponentModel; 2using CommunityToolkit.Mvvm.Input; 3using System.Diagnostics; 4using System.Text.Json; 5using System.Text.Json.Serialization; 6using System.Windows; 7 8 9namespace Questions355107 10{ 11 public class WindowModel : ObservableObject 12 { 13 public double Top { get => _Top; set => SetProperty(ref _Top, value); } 14 private double _Top = double.NaN; // 動かさずにSaveすると位置を復元しないが、普通そういうことにはならんでしょう 15 public double Left { get => _Left; set => SetProperty(ref _Left, value); } 16 private double _Left = double.NaN; 17 public double Width { get => _Width; set => SetProperty(ref _Width, value); } 18 private double _Width = 800; 19 public double Height { get => _Height; set => SetProperty(ref _Height, value); } 20 private double _Height = 450; 21 } 22 23 public class SplitterModel : ObservableObject 24 { 25 public string Outer1 { get => _Outer1; set => SetProperty(ref _Outer1, value); } 26 private string _Outer1 = "200"; // Pixcel指定(何もしていないのではみ出ます^^; 27 public string Outer2 { get => _Outer2; set => SetProperty(ref _Outer2, value); } 28 private string _Outer2 = "1*"; // Star指定 29 public string Outer3 { get => _Outer3; set => SetProperty(ref _Outer3, value); } 30 private string _Outer3 = "2*"; 31 public string Inner1 { get => _Inner1; set => SetProperty(ref _Inner1, value); } 32 private string _Inner1 = "1*"; 33 public string Inner2 { get => _Inner2; set => SetProperty(ref _Inner2, value); } 34 private string _Inner2 = "1*"; 35 } 36 37 public class SettingsModel 38 { 39 public WindowModel Window { get; set; } = new(); 40 public SplitterModel Splitter { get; set; } = new(); 41 } 42 43 public class ViewModel : ObservableObject 44 { 45 public SettingsModel Settings { get => _Settings; set => SetProperty(ref _Settings, value); } 46 private SettingsModel _Settings; 47 public RelayCommand SaveCommand { get; } 48 public RelayCommand LoadCommand { get; } 49 50 private string settingsJson; 51 private readonly JsonSerializerOptions options = new() 52 { 53 NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals, 54 WriteIndented = true, 55 }; 56 57 58 public ViewModel() 59 { 60 Settings = new(); // 本来はファイルから 61 62 SaveCommand = new(() => 63 { 64 // ファイルにセーブしたつもり 65 settingsJson = JsonSerializer.Serialize(Settings, options); 66 Debug.WriteLine(settingsJson); 67 }); 68 69 LoadCommand = new(() => 70 { 71 // 私は普段起動時に読むだけなのでPropertyChangedすらしないが、 72 // 起動中に変更する場合nullでも入れてリセットしないとうまく反映されなかった(Topだけ入らない等 73 Settings = null; 74 // ファイルからロードしたつもり 75 Settings = JsonSerializer.Deserialize<SettingsModel>(settingsJson, options); 76 }); 77 78 SaveCommand.Execute(null); // ヌルリよけ 79 } 80 } 81 82 public partial class MainWindow : Window 83 { 84 public MainWindow() => InitializeComponent(); 85 } 86}

アプリ画像
INotifyPropertyChangedICommand実装は↓を使用しました。
NuGet Gallery | CommunityToolkit.Mvvm 7.0.3

.NET Core 3.1より前の場合は↓を入れてください。
NuGet Gallery | System.Text.Json 5.0.2

C#9で書いたのでnew()でエラーが出たら型を書いてください。
Target-typed new expressions - C# 9.0 specification proposals | Microsoft Docs

投稿2021/08/19 22:37

編集2023/07/28 16:41
TN8001

総合スコア9321

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tuyudaku

2021/08/19 23:48

回答ありがとうございます! ちなみにユーザーコントロールにしようとしているのは、 パネル2枚、パネル3枚、パネル4枚など、パネル枚数の違いによるパターンが存在してまして、 理想はパネル枚数を指定したら、それに対応したレイアウト設定を読み込んでパネルを生成して、 パネルのレイアウト設定ができる! という感じなのですが(この日本語伝わりますかね...) それは出来たとしても複雑で難しそうだな...と思ったので パネル枚数ごとのユーザーコントロールを定義して、対応したユーザーコントロールを生成しようと考えました。 上記の理想のやり方はTN8001さんから見て、出来そうか出来なさそうかだけでも教えていただきたいのですが、どう思いますか...?
TN8001

2021/08/20 03:52

> パネル2枚、パネル3枚、パネル4枚など、パネル枚数の違いによるパターンが存在してまして、 > 理想はパネル枚数を指定したら、それに対応したレイアウト設定を読み込んでパネルを生成して、 > パネルのレイアウト設定ができる! > という感じなのですが(この日本語伝わりますかね...) 簡易ドッキングウィンドウ?(フローティングはせず、レイアウトのパターンがいくつかある)的なものでしょうか? ↓こんなイメージ ┏━┳━━┓ ┏━┳━┳━┓ ┏━━━━━┓ ┃ ┃  ┃ ┃ ┃ ┃ ┃ ┃     ┃ ┃ ┣━━┫ ┃ ┃ ┃ ┃ ┣━━━━━┫ ┃ ┃  ┃ ┃ ┃ ┃ ┃ ┃     ┃ ┗━┻━━┛ ┗━┻━┻━┛ ┗━━━━━┛ 分割が縦横入り乱れるなら、レイアウトA・レイアウトBとコントロールごと切り替えたほうが楽そうな気はします。 汎用化するならStackPanel的な書き心地で、間に自動でGridSplitterが入る感じかなと思ったんですが、ちょっと用途が違いますかね? >上記の理想のやり方はTN8001さんから見て、出来そうか出来なさそうかだけでも教えていただきたいのですが、どう思いますか...? どの程度のカスタマイズ性を持たせるかにもよりそうですが、上記パターン程度の複雑度かつ各ペイン内容は固定(内容まで選択可だとそのUIも悩ましそう)なら、割と簡単そうな気がしますし私もちょっと欲しいですw
tuyudaku

2021/08/20 04:06

>↓こんなイメージ そうですね、イメージ的にはそんな感じです。 そこまでレイアウト配置に自由度を持たせるつもりはないので、 3パネルだったら左に一つ、右に上下二つは固定で、 各パネルのサイズをGridSplitterで変更できるというのをイメージしています。 詳しい話をすると、パネルのサイズをレイアウト変更して保存するというのは、 複数の動画をこのレイアウト通りに配置して合成するというのを想定しており、 ユーザーがこの動画はこれぐらい大きく表示するというのを設定できるようにしたいと思っております。 そのための画面(ユーザーコントロール)を実装していました。 >汎用化するならStackPanel的な書き心地で、間に自動でGridSplitterが入る感じかなと思ったんですが、ちょっと用途が違いますかね? 多分合っているのかな...? 私の用途はとにかくパネルのサイズが取得できればいいのでそれが実現できるなら合っていると思います! すみません、C#を本格的に始めたのがここ一年ぐらいなので、ちゃんと分かっていない部分が多くて、 あいまいな返答になってしまいます... >割と簡単そうな気がしますし私もちょっと欲しいですw そうなんですね! このユーザーコントロールが汎用化出来たら結構いろいろな使い道ができそうですよね! コメントありがとうございます!
guest

0

TN8001 さんから頂いたコメントで復元について考慮されていないことに気づいたので改めて手を加えました。
(以前のソースコードについては本文から削除しています。そこまで大きな変更がある訳ではありませんが、必要があれば変更履歴で追って頂ければと思います)


Grid にバインディングするのではなく、下記のように SizeChanged のイベントを拾ってコードビハインドで依存関係プロパティに値をセットしてあげればよいと思います。

Layout2Panel.xaml

xaml

1<UserControl x:Class="WpfApp4.Layout2Panel" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 6 xmlns:local="clr-namespace:WpfApp4" 7 mc:Ignorable="d" 8 d:DesignHeight="450" d:DesignWidth="800"> 9 <UserControl.Resources> 10 <local:GridLengthConverter x:Key="gridLengthConverter" /> 11 <local:MinusConverter x:Key="minusConverter" /> 12 </UserControl.Resources> 13 <Grid x:Name="LayoutRoot"> 14 <Grid.ColumnDefinitions> 15 <ColumnDefinition Width="{Binding Path=Panel1Width, Mode=TwoWay, Converter={StaticResource gridLengthConverter}}" 16 MaxWidth="{Binding ActualWidth, ElementName=LayoutRoot, Mode=OneWay, Converter={StaticResource minusConverter}}"/> 17 <ColumnDefinition Width="1"/> 18 <ColumnDefinition Width="*"/> 19 </Grid.ColumnDefinitions> 20 <Grid x:Name="Panel1" Grid.Column="0" Background="Red" HorizontalAlignment="Stretch" SizeChanged="PanelSizeChanged" > 21 <TextBlock Text="①" TextAlignment="Center" VerticalAlignment="Center" FontSize="30" FontWeight="UltraBold"/> 22 </Grid> 23 <GridSplitter Grid.Column="1" ResizeDirection="Columns" HorizontalAlignment="Stretch"/> 24 <Grid x:Name="Panel2" Grid.Column="2" Background="Blue" HorizontalAlignment="Stretch" SizeChanged="PanelSizeChanged"> 25 <TextBlock Text="②" TextAlignment="Center" VerticalAlignment="Center" FontSize="30" FontWeight="UltraBold"/> 26 </Grid> 27 </Grid> 28</UserControl>

Layout2Panel.xaml.cs

xaml

1using System; 2using System.Collections.Generic; 3using System.ComponentModel; 4using System.Linq; 5using System.Runtime.CompilerServices; 6using System.Text; 7using System.Threading.Tasks; 8using System.Windows; 9using System.Windows.Controls; 10using System.Windows.Data; 11using System.Windows.Documents; 12using System.Windows.Input; 13using System.Windows.Media; 14using System.Windows.Media.Imaging; 15using System.Windows.Navigation; 16using System.Windows.Shapes; 17 18namespace WpfApp4 19{ 20 /// <summary> 21 /// Panel.xaml の相互作用ロジック 22 /// </summary> 23 public partial class Layout2Panel : UserControl 24 { 25 public Layout2Panel() 26 { 27 InitializeComponent(); 28 } 29 30 public double Panel1Width 31 { 32 get => (double)GetValue(Panel1WidthProperty); 33 set => SetValue(Panel1WidthProperty, value); 34 } 35 public static readonly DependencyProperty Panel1WidthProperty = 36 DependencyProperty.Register( 37 "Panel1Width", 38 typeof(double), 39 typeof(Layout2Panel), 40 new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 41 42 public double Panel2Width 43 { 44 get => (double)GetValue(Panel2WidthProperty); 45 set => SetValue(Panel2WidthProperty, value); 46 } 47 public static readonly DependencyProperty Panel2WidthProperty = 48 DependencyProperty.Register( 49 "Panel2Width", 50 typeof(double), 51 typeof(Layout2Panel), 52 new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); 53 54 private void PanelSizeChanged(object sender, SizeChangedEventArgs e) 55 { 56 Panel1Width = Panel1.ActualWidth; 57 Panel2Width = Panel2.ActualWidth; 58 } 59 } 60}

MainWindow.xaml

xaml

1<Window x:Class="WpfApp4.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfApp4" 7 mc:Ignorable="d" 8 d:DataContext="{d:DesignInstance local:Model}" 9 Title="MainWindow" Height="450" Width="800"> 10 <Grid> 11 <Grid.RowDefinitions> 12 <RowDefinition Height="20"/> 13 <RowDefinition Height="*"/> 14 </Grid.RowDefinitions> 15 <StackPanel Grid.Row="0" Orientation="Horizontal"> 16 <TextBlock Text="{Binding Panel1Width}"/> 17 <TextBlock Text="{Binding Panel2Width}"/> 18 </StackPanel> 19 <local:Layout2Panel Grid.Row="1" Panel1Width="{Binding Panel1Width}" Panel2Width="{Binding Panel2Width}"/> 20 </Grid> 21</Window>

MainWindow.xaml.cs

using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfApp4 { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new Model() { Panel1Width = 300, Panel2Width = 500 }; } } public class BindableBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual bool SetProperty<T>(ref T field, T value, [CallerMemberName]string propertyName = null) { if (Equals(field, value)) { return false; } field = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); return true; } } public class Model : BindableBase { private double _panel1Width; public double Panel1Width { get => _panel1Width; set => SetProperty(ref _panel1Width, value); } private double _panel2Width; public double Panel2Width { get => _panel2Width; set => SetProperty(ref _panel2Width, value); } } }

GridLengthConveter.cs

cs

1using System; 2using System.Collections.Generic; 3using System.Globalization; 4using System.Linq; 5using System.Text; 6using System.Threading.Tasks; 7using System.Windows; 8using System.Windows.Data; 9 10namespace WpfApp4 11{ 12 public class GridLengthConverter : IValueConverter 13 { 14 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 15 { 16 double val = (double)value; 17 GridLength gridLength = new GridLength(val); 18 19 return gridLength; 20 } 21 22 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 23 { 24 GridLength val = (GridLength)value; 25 26 return val.Value; 27 } 28 } 29}

MinusConverter.cs

cs

1using System; 2using System.Collections.Generic; 3using System.Globalization; 4using System.Linq; 5using System.Text; 6using System.Threading.Tasks; 7using System.Windows.Data; 8 9namespace WpfApp4 10{ 11 public class MinusConverter : IValueConverter 12 { 13 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 14 { 15 return ((double)value) - 1.0; 16 } 17 18 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 19 { 20 throw new NotImplementedException(); 21 } 22 } 23} 24

片方の ColumnDefinition Width のみバインディングしています。

Panel1Width と Panel2Width は両方とも実際のピクセル値がほしい認識なので、double に統一しています。なので Panel1Width をバインディングする際は GridLength に変換するコンバータを挟んでいます。もちろん要件次第では GridLength をそのまま Binding しても問題ないかと思います。

Panel2 の ColumnDefinition Width はサイズを固定したくないのでスターにする必要がありますが、実際のピクセル値がほしい場合は Panel2 の Grid から ActualWidth を取得してこないと難しいのではないかと思います。

かといって Panel2 の ColumnDefinition Width に対して
ActualWidth="{Binding Panel2Width, Mode=OneWayToSource}"
のようなことは出来ませんから、SizeChanged イベント経由で変更しています。

また、GridSplitter は対策なしだと右側にはみ出せるため、Panel1 の ColumnDefinition には MaxWidth の設定が必要です。
Panel1 の ColumnDefinition Width から GridSplitter の Width を引くために適当な Converter を作っています。
WPFのGridSplitterでピクセル指定の時に画面外までサイズ変更できなくする

イメージ

ファイルIO周りに関しては本題ではない認識なので実装を割愛しています。実際は設計にもよると思いますが、MVVM で製作しているならば Model で保存、読み込みの処理を行い、掲示した自分のコードのようなケースであれば Model.Panel1Width に初期値を渡してあげれば良いかと思います。

投稿2021/08/19 09:41

編集2021/08/19 14:23
BluOxy

総合スコア2663

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TN8001

2021/08/19 11:10

> それぞれのパネルのサイズをファイルに保存する とおっしゃってるのでおそらく次回起動時に復元したいということだと思うのですが、これだと復元されませんよね。 というかLayout2Panelのサイズは親ウインドウ次第なので、それぞれの割合(*ster)かどちらか片方の幅だけを保存復元することになりますね。 その際は内部のGridのサイズではなく、ColumnDefinition Widthをどうこうするほうが真っ当に思います。
tuyudaku

2021/08/19 12:51 編集

そうです、復元をしたいのでBindで幅の初期値を与えつつ、変更後の値も取得したという感じです。 >その際は内部のGridのサイズではなく、ColumnDefinition Widthをどうこうするほうが真っ当に思います。 現在、ColumnDefinition WidthにBindして見た目的にはましになったのですが、 GridSplitterで幅を変更すると①のパネルはサイズが変わるのですが、 ②のパネルのサイズが固定のままになってしまっている状態です。 Bindで値を与えてしまっているので固定になってしまうのは当たり前といえば当たり前ですが... BlueOxyさんの案を採用して、サイズ変更時のイベントで②のパネルサイズを計算して直接代入するという手はあるのですが、ちょっと嫌だな...って感じなので、うまいことBindの仕方やコントロールの組み合わせでできないかと模索しているところです。 BlueOxyさん回答ありがとうございます! イベントを使ったやり方は考えていなかったので参考になりました!
BluOxy

2021/08/19 12:53

復元について考慮していませんでした。仰られている通り片方の幅 (ColumnDefinition.Width) をバインディングし、復元した値をそのプロパティにセットする必要がありますね。 追記します。(追記中です)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問