想法是这样的:构建一个有 4 行的网格,每行有 1 个图表。将 3 个矩形作为拆分器按钮添加到网格中,并将它们的 Grid.RowSpan 设置为 4。
MainWindow.xaml:
<Window x:Class="testwpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
MouseMove="Window_MouseMove_1" MouseUp="Window_MouseUp_1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Name="row1" Height="40"/>
<RowDefinition Name="row2" Height="40"/>
<RowDefinition Name="row3" Height="40"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Fill="Green"/>
<Rectangle Grid.Row="1" Fill="Blue"/>
<Rectangle Grid.Row="2" Fill="Red"/>
<Rectangle Grid.Row="3" Fill="Yellow"/>
<Rectangle Fill="#5000" VerticalAlignment="Top" Height="10" Name="splitter1" MouseDown="splitter_MouseDown_1" Grid.RowSpan="4" Margin="0,40,0,0"/>
<Rectangle Fill="#5000" VerticalAlignment="Top" Height="10" Name="splitter2" MouseDown="splitter_MouseDown_1" Grid.RowSpan="4" Margin="0,80,0,0"/>
<Rectangle Fill="#5000" VerticalAlignment="Top" Height="10" Name="splitter3" MouseDown="splitter_MouseDown_1" Grid.RowSpan="4" Margin="0,120,0,0"/>
</Grid>
</Window>
MainWindow.xaml.cs:
FrameworkElement dragginSplitter = null;
private void splitter_MouseDown_1(object sender, MouseButtonEventArgs e)
{
dragginSplitter = sender as FrameworkElement;
}
private void Window_MouseMove_1(object sender, MouseEventArgs e)
{
if (dragginSplitter != null)
dragginSplitter.Margin = new Thickness(0, e.GetPosition(this).Y, 0, 0);
if (splitter2.Margin.Top < splitter1.Margin.Top + 10) splitter2.Margin = new Thickness(0, splitter1.Margin.Top + 10, 0, 0);
if (splitter3.Margin.Top < splitter2.Margin.Top + 10) splitter3.Margin = new Thickness(0, splitter2.Margin.Top + 10, 0, 0);
row1.Height = new GridLength(splitter1.Margin.Top);
row2.Height = new GridLength(splitter2.Margin.Top - splitter1.Margin.Top);
row3.Height = new GridLength(splitter3.Margin.Top - splitter2.Margin.Top);
}
private void Window_MouseUp_1(object sender, MouseButtonEventArgs e)
{
dragginSplitter = null;
}
Window_MouseMove_1 的重要说明:
- 每当拖动拆分器时,draggingSplitter 的上边距等于鼠标的 Y 位置。
- 拆分器不应相互通过,因此每当拖动拆分器时,我们都会检查它们的上边距。
- 每当拖动拆分器时,根据拆分器的上边距计算网格行的高度(不是网格的内容)。
编辑:
我修改了代码并添加了各种条件以满足隐藏/显示的需要。
<Window x:Class="testwpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
MouseMove="Window_MouseMove_1" MouseUp="Window_MouseUp_1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Name="row1" Height="40"/>
<RowDefinition Name="row2" Height="40"/>
<RowDefinition Name="row3" Height="40"/>
<RowDefinition Name="row4" Height="*"/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Fill="Green"/>
<Rectangle Grid.Row="1" Fill="Blue"/>
<Rectangle Grid.Row="2" Fill="Red"/>
<Rectangle Grid.Row="3" Fill="Yellow"/>
<Rectangle Fill="#5000" VerticalAlignment="Top" Height="10" Name="splitter1" MouseDown="splitter_MouseDown_1" Grid.RowSpan="4" Margin="0,40,0,0"/>
<Rectangle Fill="#5000" VerticalAlignment="Top" Height="10" Name="splitter2" MouseDown="splitter_MouseDown_1" Grid.RowSpan="4" Margin="0,80,0,0"/>
<Rectangle Fill="#5000" VerticalAlignment="Top" Height="10" Name="splitter3" MouseDown="splitter_MouseDown_1" Grid.RowSpan="4" Margin="0,120,0,0"/>
<WrapPanel Grid.RowSpan="4">
<CheckBox Name="check1" Content="graph1" IsChecked="True" Checked="CheckBox_Checked_1" Unchecked="CheckBox_Checked_1"/>
<CheckBox Name="check2" Content="graph2" IsChecked="True" Checked="CheckBox_Checked_1" Unchecked="CheckBox_Checked_1"/>
<CheckBox Name="check3" Content="graph3" IsChecked="True" Checked="CheckBox_Checked_1" Unchecked="CheckBox_Checked_1"/>
<CheckBox Name="check4" Content="graph4" IsChecked="True" Checked="CheckBox_Checked_1" Unchecked="CheckBox_Checked_1"/>
</WrapPanel>
</Grid>
</Window>
xaml 更改:添加了 4 个复选框并命名了第 4 行。
FrameworkElement dragginSplitter = null;
private void splitter_MouseDown_1(object sender, MouseButtonEventArgs e)
{
dragginSplitter = sender as FrameworkElement;
}
private void Window_MouseMove_1(object sender, MouseEventArgs e)
{
if (dragginSplitter != null)
{
dragginSplitter.Margin = new Thickness(0, e.GetPosition(this).Y, 0, 0);
updateSplitters();
}
}
private void updateSplitters()
{
if (splitter2.Margin.Top < splitter1.Margin.Top + 10) splitter2.Margin = new Thickness(0, splitter1.Margin.Top + 10, 0, 0);
if (splitter3.Margin.Top < splitter2.Margin.Top + 10) splitter3.Margin = new Thickness(0, splitter2.Margin.Top + 10, 0, 0);
if (check1.IsChecked.Value)
{
if (!check2.IsChecked.Value && !check3.IsChecked.Value && !check4.IsChecked.Value)
row1.Height = new GridLength(1, GridUnitType.Star);
else
row1.Height = new GridLength(splitter1.Margin.Top);
}
else
row1.Height = new GridLength(0);
if (check2.IsChecked.Value)
{
if (!check3.IsChecked.Value && !check4.IsChecked.Value)
row2.Height = new GridLength(1, GridUnitType.Star);
else if(check1.IsChecked.Value)
row2.Height = new GridLength(splitter2.Margin.Top - splitter1.Margin.Top);
else
row2.Height = new GridLength(splitter2.Margin.Top);
}
else
row2.Height = new GridLength(0);
if (check3.IsChecked.Value)
{
if (!check4.IsChecked.Value)
row3.Height = new GridLength(1, GridUnitType.Star);
else if (check2.IsChecked.Value)
row3.Height = new GridLength(splitter3.Margin.Top - splitter2.Margin.Top);
else if (check1.IsChecked.Value)
row3.Height = new GridLength(splitter3.Margin.Top - splitter1.Margin.Top);
else
row3.Height = new GridLength(splitter3.Margin.Top);
}
else
row3.Height = new GridLength(0);
row4.Height = check4.IsChecked.Value ? new GridLength(1, GridUnitType.Star) : new GridLength(0);
}
private void Window_MouseUp_1(object sender, MouseButtonEventArgs e)
{
dragginSplitter = null;
}
private void CheckBox_Checked_1(object sender, RoutedEventArgs e)
{
if (check4 == null) return;//for when not yet completely loaded
if (!check1.IsChecked.Value)
splitter1.Visibility = System.Windows.Visibility.Collapsed;
if (!check2.IsChecked.Value)
splitter2.Visibility = System.Windows.Visibility.Collapsed;
if (!check3.IsChecked.Value)
splitter3.Visibility = System.Windows.Visibility.Collapsed;
if (!check4.IsChecked.Value)
{
splitter3.Visibility = System.Windows.Visibility.Collapsed;
if (!check3.IsChecked.Value)
splitter2.Visibility = System.Windows.Visibility.Collapsed;
if (!check2.IsChecked.Value)
splitter1.Visibility = System.Windows.Visibility.Collapsed;
}
if (check1.IsChecked.Value && (check2.IsChecked.Value || check3.IsChecked.Value || check4.IsChecked.Value))
splitter1.Visibility = System.Windows.Visibility.Visible;
if (check2.IsChecked.Value && (check3.IsChecked.Value || check4.IsChecked.Value))
splitter2.Visibility = System.Windows.Visibility.Visible;
if (check3.IsChecked.Value && check4.IsChecked.Value)
splitter3.Visibility = System.Windows.Visibility.Visible;
updateSplitters();
}
cs的变化:
- updateSplitters 函数被替换为之前用于更新拆分器边距的逻辑。
- 检查或未选中任何复选框时,调用分离器更改和更新更新的可见性。
- updateSplitters 完成所有其余的工作。没什么好说的,除了最后一个可见行总是有 Star 的 GridUnitType 来填充网格的剩余空间。
我发现的唯一问题是,当窗口调整大小或将上面的行之一调整为较大的高度值时,网格的下部内容可能会从屏幕上掉下来。