我正在用 C# 构建一个 WPF 应用程序。 我正在尝试使用
Grid
布局实现两列布局,类似于浏览器侧边栏。
Grid
实现为 3 列布局,因为我使用 GridSplitter
来轻松更改侧边栏的宽度。
侧边栏是可以折叠的,所以参考下面的Style进行设置,这样使用后就可以毫无问题地折叠了
GridSplitter
。默认情况下,侧边栏位于右侧,但我想在代码中动态颠倒顺序。
MainContent | GridSplitter | Sidebar
<---->
Sidebar | GridSplitter | MainContent
主窗口.xaml
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
d:DataContext="{d:DesignInstance local:MainWindow}">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<!--Main Column-->
<ColumnDefinition Width="*" />
<!--GridSplitter -->
<ColumnDefinition Width="Auto" />
<!--Collapsable Sidebar Column -->
<!--Can collapse entirely even if GridSplitter is used -->
<!--ref: https://stackoverflow.com/questions/12483017/wpf-collapse-gridsplitter-->
<ColumnDefinition>
<ColumnDefinition.Style>
<Style TargetType="{x:Type ColumnDefinition}">
<Style.Setters>
<Setter Property="Width" Value="200"/>
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowSidebar}" Value="False">
<DataTrigger.Setters>
<Setter Property="Width" Value="0"/>
<Setter Property="MaxWidth" Value="0"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</ColumnDefinition.Style>
</ColumnDefinition>
</Grid.ColumnDefinitions>
<!--Main Column-->
<Grid Grid.Column="0" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Button Content="Toggle sidebar" Click="ToggleSidebarButton_OnClick" />
<Button Content="Swap sidebar" Click="SwapSidebarButton_OnClick" />
</StackPanel>
<TextBlock
Grid.Row="1"
Text="Main Content"
FontSize="32"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
<GridSplitter
Grid.Column="1"
Visibility="{Binding ShowSidebar, Converter={StaticResource BooleanToVisibilityConverter}}"
Width="5"
ShowsPreview="True"
ResizeBehavior="PreviousAndNext"
ResizeDirection="Columns" />
<!--Sidebar Column-->
<Grid
Grid.Column="2"
Visibility="{Binding ShowSidebar, Converter={StaticResource BooleanToVisibilityConverter}}"
Background="Gray">
<TextBlock
Text="Sidebar"
FontSize="32"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</Grid>
</Window>
MainWindow.xaml.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApp1
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
private bool _showSidebar = true;
public bool ShowSidebar
{
get => _showSidebar;
set => SetField(ref _showSidebar, value);
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void ToggleSidebarButton_OnClick(object sender, RoutedEventArgs e)
{
ShowSidebar = !ShowSidebar;
}
private void SwapSidebarButton_OnClick(object sender, RoutedEventArgs e)
{
// TODO: How to swap the sidebar programmatically?
throw new NotImplementedException();
}
#region IPC
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
#endregion
}
}
TODO 部分是用代码隐藏编写的,但我实际上想使用 MVVM 在 ViewModel 中实现它。
我已经确认可以使用如下所示的 5 列布局来实现这一点,通过创建两个变量
ShowSidebarRight
和 ShowSidebarLeft
,分别放置两个 SidebarView 和 GridSplitter,并用 Visibility
控制显示来实现。
但我想只用一个 View 实例来实现它,因为它会创建一个额外的 SidebarView 和 ViewModel 实例,这会导致性能问题和代码冗余。
<Grid.ColumnDefinitions>
<!--Left Sidebar -->
<ColumnDefinition>
<ColumnDefinition.Style>
<Style TargetType="{x:Type ColumnDefinition}">
<Style.Setters>
<Setter Property="Width" Value="200"/>
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowSidebarLeft}" Value="False">
<DataTrigger.Setters>
<Setter Property="Width" Value="0"/>
<Setter Property="MaxWidth" Value="0"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</ColumnDefinition.Style>
</ColumnDefinition>
<!--Left GridSplitter -->
<ColumnDefinition Width="Auto" />
<!--Main Column-->
<ColumnDefinition Width="*" />
<!--Right GridSplitter -->
<ColumnDefinition Width="Auto" />
<!--Right Sidebar -->
<ColumnDefinition>
<ColumnDefinition.Style>
<Style TargetType="{x:Type ColumnDefinition}">
<Style.Setters>
<Setter Property="Width" Value="200"/>
</Style.Setters>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowSidebarRight}" Value="False">
<DataTrigger.Setters>
<Setter Property="Width" Value="0"/>
<Setter Property="MaxWidth" Value="0"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</ColumnDefinition.Style>
</ColumnDefinition>
</Grid.ColumnDefinitions>
您可以通过设置
FlowDirection
属性来反转水平布局顺序:
<Grid FlowDirection="RightToLeft">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" FlowDirection="LeftToRight">
<TextBlock Text="Main Content"/>
</Grid>
<Grid Grid.Column="2" FlowDirection="LeftToRight">
<TextBlock Text="Sidebar"/>
</Grid>
<GridSplitter
Grid.Column="1"
Width="5"
ShowsPreview="True"
ResizeBehavior="PreviousAndNext"
ResizeDirection="Columns" />
</Grid>