WPF中RowDetailsTemplate自定义展开折叠图标(XAML替代±按钮)
我来帮你搞定这个WPF自定义RowDetails展开/折叠图标的需求,全程用XAML实现,不用写后台事件代码~你参考的Silverlight方案核心思路是替换行模板,我们把它适配成WPF的实现方式,逻辑一致但语法更贴合WPF的控件模板体系。
核心思路
我们需要重写DataGridRow的默认模板,替换掉自带的±按钮,用DataTrigger绑定DataGridRow的DetailsVisibility属性来切换展开/折叠图片,全程不需要后台事件处理。
完整可运行XAML示例
你可以直接把这段代码复制到项目里测试,记得替换自己的图片路径:
<Window x:Class="WpfDataGridCustomRowDetails.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfDataGridCustomRowDetails" Title="Custom RowDetails Icon" Height="450" Width="800"> <Window.Resources> <!-- Visibility和Bool值的转换器,用于绑定ToggleButton的IsChecked --> <local:VisibilityToBoolConverter x:Key="VisibilityToBoolConverter"/> </Window.Resources> <Grid> <DataGrid x:Name="MyDataGrid" AutoGenerateColumns="False" RowDetailsVisibilityMode="Collapsed"> <!-- 自定义列 --> <DataGrid.Columns> <DataGridTextColumn Header="名称" Binding="{Binding Name}" Width="150"/> <DataGridTextColumn Header="描述" Binding="{Binding Description}" Width="*"/> </DataGrid.Columns> <!-- RowDetails模板,这里是你自己的详情内容 --> <DataGrid.RowDetailsTemplate> <DataTemplate> <Border Margin="10" Padding="10" Background="#F0F0F0" CornerRadius="5"> <TextBlock Text="{Binding Details}" FontStyle="Italic" Foreground="#333"/> </Border> </DataTemplate> </DataGrid.RowDetailsTemplate> <!-- 重写DataGridRow的模板,替换默认展开按钮 --> <DataGrid.RowStyle> <Style TargetType="{x:Type DataGridRow}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type DataGridRow}"> <Border x:Name="DGR_Border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True"> <SelectiveScrollingGrid> <SelectiveScrollingGrid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="*"/> </SelectiveScrollingGrid.ColumnDefinitions> <SelectiveScrollingGrid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </SelectiveScrollingGrid.RowDefinitions> <!-- 自定义展开/折叠按钮 --> <ToggleButton x:Name="ExpanderButton" Margin="4,2" Width="20" Height="20" IsChecked="{Binding Path=DetailsVisibility, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource VisibilityToBoolConverter}}"> <ToggleButton.Style> <Style TargetType="{x:Type ToggleButton}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ToggleButton}"> <Grid> <!-- 折叠状态显示的图片 --> <Image x:Name="CollapsedImg" Source="/Images/expand_icon.png" Stretch="Uniform"/> <!-- 展开状态显示的图片 --> <Image x:Name="ExpandedImg" Source="/Images/collapse_icon.png" Stretch="Uniform" Visibility="Collapsed"/> </Grid> <!-- 根据IsChecked状态切换图片显示 --> <ControlTemplate.Triggers> <Trigger Property="IsChecked" Value="True"> <Setter TargetName="CollapsedImg" Property="Visibility" Value="Collapsed"/> <Setter TargetName="ExpandedImg" Property="Visibility" Value="Visible"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </ToggleButton.Style> </ToggleButton> <!-- 行内容区域 --> <DataGridCellsPresenter Grid.Column="1" ItemsPanel="{TemplateBinding ItemsPanel}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> <!-- RowDetails详情区域 --> <DataGridDetailsPresenter Grid.Column="1" Grid.Row="1" SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding AreRowDetailsFrozen, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" Visibility="{TemplateBinding DetailsVisibility}"/> </SelectiveScrollingGrid> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </DataGrid.RowStyle> </DataGrid> </Grid> </Window>
关键细节说明
1. 必加的Visibility转换器
因为ToggleButton的IsChecked是布尔值,而DataGridRow的DetailsVisibility是Visibility枚举,所以需要一个简单的转换器来做类型转换:
namespace WpfDataGridCustomRowDetails { public class VisibilityToBoolConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return ((Visibility)value) == Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return (bool)value ? Visibility.Visible : Visibility.Collapsed; } } }
这个转换器只是做类型映射,不属于事件处理,完全符合你“尽量不用后台代码”的需求。
2. 图片路径设置
把你的展开/折叠图片添加到项目中,右键图片文件→属性→设置生成操作为Resource,然后修改XAML里的Source路径,比如你的项目名为MyWpfApp,图片在Images文件夹下,路径就是/MyWpfApp;component/Images/expand.png。
3. 额外优化:点击整行展开/折叠
如果想要点击整行也能触发展开/折叠,可以在DataGridRow的Style里添加一个事件触发器:
<Style TargetType="{x:Type DataGridRow}"> <!-- 原有的Setter内容 --> <Style.Triggers> <EventTrigger RoutedEvent="MouseLeftButtonUp"> <BeginStoryboard> <Storyboard> <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="DetailsVisibility"> <DiscreteObjectKeyFrame KeyTime="0" Value="{Binding DetailsVisibility, RelativeSource={RelativeSource Self}, Converter={StaticResource InverseVisibilityConverter}}"/> </ObjectAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </EventTrigger> </Style.Triggers> </Style>
这时候需要再添加一个反向Visibility转换器,用来切换可见性状态:
public class InverseVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return ((Visibility)value) == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return ((Visibility)value) == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; } }
这样就完全实现了用自定义图片作为展开/折叠图标,而且全程用XAML控制,不需要后台事件来修改按钮外观,完美适配你的需求~
内容的提问来源于stack exchange,提问作者Bigeyes

