在 WPF 中需要自定义样式的帮助
Posted
技术标签:
【中文标题】在 WPF 中需要自定义样式的帮助【英文标题】:Need help with a custom Style in WPF 【发布时间】:2010-12-06 19:36:52 【问题描述】:我正在尝试为 WPF Toolkit DataGrid
控件构建一些简单的自定义样式。
我有一个整体DataGrid
的样式和一个DataGridColumnHeader
的样式。我没有设置任何控件模板,只有基本属性。
以下是应用了自定义样式后的示例 DataGrid:
alt text http://img86.imageshack.us/img86/43/datagridcustomstyle.jpg
标题有渐变蓝色背景、粗体文本、填充等。我想要,但有两件事消失了:列标题之间的分隔符和 ID 列的排序箭头(此列目前在它)。
如果我没有弄乱任何控件模板,为什么分隔符和排序箭头会消失?
我什至尝试将SeparatorBrush
明确设置为Black
并将SeparatorVisibility
设置为Visible
,但这没有任何效果。
如果我恢复为默认样式,我的示例 DataGrid 的外观如下:
alt text http://img42.imageshack.us/img42/6533/datagriddefaultstyle.jpg
分隔符和排序箭头又回来了,所以绝对是我的风格有所不同。
这是我的自定义 DataGridColumnHeader 样式
<Style
x:Key="DataGrid_ColumnHeaderStyle"
TargetType="wt:DataGridColumnHeader">
<Setter
Property="Padding"
Value="5,2,5,2" />
<Setter
Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter
Property="VerticalContentAlignment"
Value="Stretch" />
<Setter
Property="FontWeight"
Value="Bold" />
<Setter
Property="BorderBrush"
Value="StaticResource Media_RaisedBorderBrush" />
<Setter
Property="Background"
Value="StaticResource Media_RaisedBackgroundBrush" />
<Setter
Property="Foreground"
Value="StaticResource Media_RaisedForegroundBrush" />
<Setter
Property="SeparatorBrush"
Value="Black" />
<Setter
Property="SeparatorVisibility"
Value="Visible" />
</Style>
这是我的自定义 DataGrid 样式
<Style
x:Key="DataGrid_Style"
TargetType="wt:DataGrid">
<Setter
Property="ColumnHeaderStyle"
Value="StaticResource DataGrid_ColumnHeaderStyle" />
<Setter
Property="RowBackground"
Value="StaticResource Media_OddRowBackgroundBrush" />
<Setter
Property="AlternatingRowBackground"
Value="StaticResource Media_EvenRowBackgroundBrush" />
<Setter
Property="HorizontalGridLinesBrush"
Value="White" />
<Setter
Property="VerticalGridLinesBrush"
Value="LightGray" />
<Setter
Property="AutoGenerateColumns"
Value="False" />
<Setter
Property="CanUserAddRows"
Value="False" />
<Setter
Property="CanUserDeleteRows"
Value="False" />
<Setter
Property="CanUserReorderColumns"
Value="True" />
<Setter
Property="CanUserResizeColumns"
Value="True" />
<Setter
Property="CanUserResizeRows"
Value="False" />
<Setter
Property="CanUserSortColumns"
Value="True" />
<Setter
Property="IsReadOnly"
Value="True" />
</Style>
这是我的示例 DataGrid 的标记
<wt:DataGrid
Style="StaticResource DataGrid_Style"
Margin="0,5,0,0"
ItemsSource="Binding Source=StaticResource Main_ContactData, XPath=//Contacts/*">
<wt:DataGrid.Columns>
<wt:DataGridTextColumn
Binding="Binding XPath=@Letter"
Header="ID" />
<wt:DataGridTextColumn
Binding="Binding XPath=@Name"
Header="Name" />
<wt:DataGridTextColumn
Binding="Binding XPath=@IsSaved"
Header="Saved?" />
<wt:DataGridTextColumn
Binding="Binding XPath=@IsBackedUp"
Header="Backed Up?" />
</wt:DataGrid.Columns>
</wt:DataGrid>
这是一个错误吗?
如果没有,您能否告诉我如何修改我的样式,以免丢失分隔线和排序箭头?
编辑
我尝试按照@Aran 的建议(如下)添加BasedOn
属性,但这似乎没有任何效果。有人有其他想法吗?
【问题讨论】:
【参考方案1】:感谢@Aran 的回答以及我在 Codeplex 讨论中找到的几篇帖子(请参阅 http://wpf.codeplex.com/Thread/View.aspx?ThreadId=65069),我设法提出了一组样式,可以在不放弃的情况下设置标题行的背景功能(排序箭头、分隔符等)。
对此的一个例外是“单元格选择”功能。 DataGrid 有一个SelectionUnit
属性,可以设置为Cell、FullRow 或CellOrRowHeader,但它似乎不起作用。如果我将其设置为FullRow
(无论您在何处单击该行,它都应该仅选择整行),当您单击它们时,它仍会在视觉上选择单个单元格。下图显示了 SelectionUnit="FullRow"
的 DataGrid 示例(未应用样式)。如您所见,看起来确实有一个单元格被选中(名称列的 G 行)。由于我目前不需要单元格选择,因此我设计了我的样式来抑制单击单元格时出现在单元格周围的深黑色边框。
alt text http://img80.imageshack.us/img80/4757/datagriddefaultcellsele.jpg
这是一个按 ID 降序排序的样式化 DataGrid 示例。如您所见,向下箭头显示在 ID 标签旁边,并且该列已适当地自动调整大小以为排序箭头腾出空间。
alt text http://img377.imageshack.us/img377/3836/datagridcustomfixed.jpg
这是另一个样式化 DataGrid 的示例。这个按“保存?”升序排序。请注意,标题居中,但它仍然在两侧为排序箭头留出足够的空间。
alt text http://img203.imageshack.us/img203/5140/datagridcustomfixed2.jpg
以下是实现这种外观所需的样式。
画笔
这些样式中有许多画笔设置,格式均为Property="StaticResource Media_..."
。我现在将省略定义,因为编译它们需要一些时间,但如果有人有兴趣复制我使用的确切颜色,请发表评论。
排序箭头样式
(箭头本身包含在列标题样式中)
<Style
x:Key="DataGrid_ArrowStyle"
TargetType="Polygon">
<Setter
Property="Grid.Column"
Value="1" />
<Setter
Property="HorizontalAlignment"
Value="Right" />
<Setter
Property="VerticalAlignment"
Value="Bottom" />
<Setter
Property="StrokeThickness"
Value="1" />
<Setter
Property="Stroke"
Value="StaticResource Media_BrightGraphicBorderBrush" />
<Setter
Property="Fill"
Value="StaticResource Media_BrightGraphicBackgroundBrush" />
<Setter
Property="Visibility"
Value="Hidden" />
</Style>
列标题缩略图样式
(拇指本身包含在列标题样式中)
<Style
x:Key="DataGrid_ColumnHeaderThumbStyle"
TargetType="Thumb">
<Setter
Property="Width"
Value="8" />
<Setter
Property="Background"
Value="StaticResource Media_RaisedSeparatorBackgroundBrush" />
<Setter
Property="Cursor"
Value="SizeWE" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="Thumb">
<Border
Padding="TemplateBinding Padding"
Background="Transparent">
<Border
Padding="0,2,0,2">
<Rectangle
HorizontalAlignment="Center"
Width="2"
Fill="TemplateBinding Background" />
</Border>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
列标题样式
(指的是上面定义的排序箭头样式和列标题拇指样式)
<Style
x:Key="DataGrid_ColumnHeaderStyle"
TargetType="wt:DataGridColumnHeader">
<Setter
Property="Padding"
Value="5,2,3,3" />
<Setter
Property="HorizontalContentAlignment"
Value="Stretch" />
<Setter
Property="VerticalContentAlignment"
Value="Stretch" />
<Setter
Property="FontWeight"
Value="Bold" />
<Setter
Property="BorderBrush"
Value="StaticResource Media_RaisedBorderBrush" />
<Setter
Property="Background"
Value="StaticResource Media_RaisedBackgroundBrush" />
<Setter
Property="Foreground"
Value="StaticResource Media_RaisedForegroundBrush" />
<Setter
Property="SeparatorBrush"
Value="StaticResource Media_RaisedBorderBrush" />
<Setter
Property="SeparatorVisibility"
Value="Visible" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="wt:DataGridColumnHeader">
<Grid>
<wt:DataGridHeaderBorder
Name="HeaderBorder"
BorderThickness="TemplateBinding BorderThickness"
Padding="TemplateBinding Padding"
BorderBrush="TemplateBinding BorderBrush"
Background="TemplateBinding Background"
SortDirection="TemplateBinding SortDirection"
IsClickable="TemplateBinding CanUserSort"
IsHovered="TemplateBinding IsMouseOver"
IsPressed="TemplateBinding IsPressed"
SeparatorVisibility="TemplateBinding SeparatorVisibility"
SeparatorBrush="TemplateBinding SeparatorBrush">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition
Width="*" />
<ColumnDefinition
Width="12" />
</Grid.ColumnDefinitions>
<ContentPresenter
Name="HeaderContent"
Grid.Column="0"
HorizontalAlignment="TemplateBinding HorizontalContentAlignment"
VerticalAlignment="TemplateBinding VerticalContentAlignment"
SnapsToDevicePixels="TemplateBinding SnapsToDevicePixels"
ContentStringFormat="TemplateBinding ContentStringFormat"
ContentTemplate="TemplateBinding ContentTemplate"
Content="TemplateBinding Content" />
<Polygon
Name="UpArrow"
Style="StaticResource DataGrid_ArrowStyle"
Margin="0,0,0,2"
Points="0,0 8,0 4,-6 0,0" />
<Polygon
Name="DownArrow"
Margin="0,0,0,1"
Style="StaticResource DataGrid_ArrowStyle"
Points="0,0 8,0 4,5 0,0" />
</Grid>
</wt:DataGridHeaderBorder>
<Thumb
x:Name="PART_LeftHeaderGripper"
Style="StaticResource DataGrid_ColumnHeaderThumbStyle"
Margin="-4,0,0,0"
HorizontalAlignment="Left" />
<Thumb
x:Name="PART_RightHeaderGripper"
Style="StaticResource DataGrid_ColumnHeaderThumbStyle"
Margin="0,0,-4,0"
HorizontalAlignment="Right"></Thumb>
</Grid>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition
Property="CanUserSort"
Value="True" />
<Condition
Property="IsMouseOver"
Value="True" />
</MultiTrigger.Conditions>
<Setter
TargetName="HeaderBorder"
Property="TextBlock.Foreground"
Value="StaticResource Media_MousedOverForegroundBrush" />
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition
Property="CanUserSort"
Value="True" />
<Condition
Property="IsPressed"
Value="True" />
</MultiTrigger.Conditions>
<Setter
TargetName="HeaderBorder"
Property="BorderBrush"
Value="StaticResource Media_PressedBorderBrush" />
<Setter
TargetName="HeaderBorder"
Property="Background"
Value="StaticResource Media_PressedBackgroundBrush" />
<Setter
TargetName="HeaderBorder"
Property="TextBlock.Foreground"
Value="StaticResource Media_PressedForegroundBrush" />
</MultiTrigger>
<Trigger
Property="SortDirection"
Value="Ascending">
<Setter
TargetName="UpArrow"
Property="Visibility"
Value="Visible" />
</Trigger>
<Trigger
Property="SortDirection"
Value="Descending">
<Setter
TargetName="DownArrow"
Property="Visibility"
Value="Visible" />
</Trigger>
<Trigger
Property="CanUserSort"
Value="False">
<Setter
TargetName="HeaderContent"
Property="Grid.ColumnSpan"
Value="2" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition
Property="HorizontalContentAlignment"
Value="Center" />
<Condition
Property="CanUserSort"
Value="True" />
</MultiTrigger.Conditions>
<Setter
TargetName="HeaderContent"
Property="Grid.ColumnSpan"
Value="2" />
<Setter
TargetName="HeaderContent"
Property="Margin"
Value="12,0,12,0" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
居中的列标题样式
(使用它使标题居中;如果列是可排序的,将自动为排序箭头腾出空间)
<Style
x:Key="DataGrid_CenteredColumnHeaderStyle"
TargetType="wt:DataGridColumnHeader"
BasedOn="StaticResource DataGrid_ColumnHeaderStyle">
<Setter
Property="HorizontalContentAlignment"
Value="Center" />
</Style>
环绕列标题样式
(使用它允许标题的文本自动换行 - 仅当您手动设置宽度或用户手动缩小列时才有效)
<Style
x:Key="DataGrid_WrappingColumnHeaderStyle"
TargetType="wt:DataGridColumnHeader"
BasedOn="StaticResource DataGrid_ColumnHeaderStyle">
<Setter
Property="VerticalContentAlignment"
Value="Top" />
<Setter
Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock
TextWrapping="WrapWithOverflow"
Text="TemplateBinding Content" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
单元格样式
<Style
x:Key="DataGrid_CellStyle"
TargetType="wt:DataGridCell">
<Setter
Property="Padding"
Value="5,2,5,2" />
<Setter
Property="BorderThickness"
Value="1" />
<Setter
Property="BorderBrush"
Value="Transparent" />
<Setter
Property="Background"
Value="Transparent" />
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="wt:DataGridCell">
<Border
BorderThickness="TemplateBinding BorderThickness"
Background="TemplateBinding Background"
BorderBrush="TemplateBinding BorderBrush"
SnapsToDevicePixels="True"
Padding="TemplateBinding Padding">
<ContentPresenter
HorizontalAlignment="TemplateBinding HorizontalContentAlignment"
VerticalAlignment="TemplateBinding VerticalContentAlignment"
SnapsToDevicePixels="TemplateBinding SnapsToDevicePixels"
ContentTemplate="TemplateBinding ContentTemplate"
ContentStringFormat="TemplateBinding ContentStringFormat"
Content="TemplateBinding Content" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger
Property="IsSelected"
Value="True">
<Setter
Property="BorderBrush"
Value="DynamicResource x:Static SystemColors.HighlightBrushKey" />
<Setter
Property="Background"
Value="DynamicResource x:Static SystemColors.HighlightBrushKey" />
<Setter
Property="Foreground"
Value="Black" />
</Trigger>
<Trigger
Property="IsKeyboardFocusWithin"
Value="True">
<Setter
Property="BorderBrush"
Value="DynamicResource x:Static SystemColors.HighlightBrushKey" />
<Setter
Property="Background"
Value="DynamicResource x:Static SystemColors.HighlightBrushKey" />
<Setter
Property="Foreground"
Value="Black" />
<!--<Setter
Property="BorderBrush"
Value="DynamicResource ComponentResourceKey ResourceId=FocusBorderBrushKey, TypeInTargetAssembly=wt:DataGrid" />-->
</Trigger>
</Style.Triggers>
</Style>
居中单元格样式
(使用它使单元格的内容居中)
<Style
x:Key="DataGrid_CenteredCellStyle"
TargetType="wt:DataGridCell"
BasedOn="StaticResource DataGrid_CellStyle">
<Setter
Property="HorizontalContentAlignment"
Value="Center" />
</Style>
数据网格样式
(为许多属性建立一组默认值,包括上面定义的列标题样式和单元格样式)
<Style
x:Key="DataGrid_Style"
TargetType="wt:DataGrid">
<Setter
Property="ColumnHeaderStyle"
Value="StaticResource DataGrid_ColumnHeaderStyle" />
<Setter
Property="CellStyle"
Value="StaticResource DataGrid_CellStyle" />
<Setter
Property="RowBackground"
Value="StaticResource Media_OddRowBackgroundBrush" />
<Setter
Property="AlternatingRowBackground"
Value="StaticResource Media_EvenRowBackgroundBrush" />
<Setter
Property="HorizontalGridLinesBrush"
Value="LightGray" />
<Setter
Property="VerticalGridLinesBrush"
Value="LightGray" />
<Setter
Property="SelectionMode"
Value="Single" />
<Setter
Property="SelectionUnit"
Value="FullRow" />
<Setter
Property="AutoGenerateColumns"
Value="False" />
<Setter
Property="CanUserAddRows"
Value="False" />
<Setter
Property="CanUserDeleteRows"
Value="False" />
<Setter
Property="CanUserReorderColumns"
Value="True" />
<Setter
Property="CanUserResizeColumns"
Value="True" />
<Setter
Property="CanUserResizeRows"
Value="False" />
<Setter
Property="CanUserSortColumns"
Value="True" />
<Setter
Property="IsReadOnly"
Value="True" />
<Setter
Property="HeadersVisibility"
Value="Column" />
</Style>
使用这些样式的 DataGrid 示例
(注意:需要支持数据——一个 XML 文件——才能工作)
<wt:DataGrid
Style="StaticResource DataGrid_Style"
Margin="0,5,0,0"
ItemsSource="Binding Source=StaticResource Main_ContactData, XPath=//Contacts/*">
<wt:DataGrid.Columns>
<wt:DataGridTextColumn
Binding="Binding XPath=@Letter"
Header="ID" />
<wt:DataGridTextColumn
Binding="Binding XPath=@Name"
Header="Name" />
<wt:DataGridTextColumn
HeaderStyle="StaticResource DataGrid_CenteredColumnHeaderStyle"
CellStyle="StaticResource DataGrid_CenteredCellStyle"
Binding="Binding XPath=@IsSaved"
Header="Saved?" />
<wt:DataGridTextColumn
HeaderStyle="StaticResource DataGrid_CenteredColumnHeaderStyle"
CellStyle="StaticResource DataGrid_CenteredCellStyle"
Binding="Binding XPath=@IsBackedUp"
Header="Backed Up?" />
</wt:DataGrid.Columns>
</wt:DataGrid>
【讨论】:
【参考方案2】:试试看
<Style
x:Key="DataGrid_Style"
TargetType="wt:DataGrid"
BasedOn="StaticResource x:Type wt:DataGrid">
将您的样式基于当前的数据网格之一,对于标题也是如此
<Style
x:Key="DataGrid_ColumnHeaderStyle"
TargetType="wt:DataGridColumnHeader"
BasedOn="StaticResource x:Type wt:DataGridColumnHeader">
从头开始 - 我把你的风格拉到了有问题的元素
<Style
TargetType="wt:DataGridColumnHeader"
BasedOn="StaticResource x:Type wt:DataGridColumnHeader">
<Setter
Property="Background"
Value="Blue" />
</Style>
如果你设置了背景(或边框画笔)你就完蛋了。
我在代码项目中找到了this link,来支持一下 -
"通过DataGrid的ColumnHeaderStyle可以很方便的修改列头的样式。但是,如果修改列头的背景色,就会发现排序箭头消失了!这是因为箭头没有ColumnHeader 模板的一部分;相反,它们是以编程方式添加的。”
他有一种重新添加排序指示符的样式。
我查看了 DataGridHeaderBorder 的代码(它是 datagridrowheader 的边框),它没有自己的控件模板,它只是从边框派生的。除了以编程方式添加的分隔符(分隔符只是矩形,请参见 DataGridHeaderBorder.cs 的第 1199 行),排序指示符也是。简要看一下我的代码会建议他们仍然应该被绘制,但他们没有,代码的一步是有序的。
解决方案是覆盖我认为的控件模板,并自己添加它们,代码项目上的链接将帮助您入门。
【讨论】:
@Aran,感谢您的回答,但添加BasedOn
属性没有任何区别。
@Aran,谢谢。我会尝试一下。我只是在尝试在 codeplex 上找到的类似解决方案,它似乎也可以工作,只是箭头最终与文本重叠。也许您链接的代码项目示例会更好。我假设我需要为<dg:DataGridHeaderBorder .../>
和<Thumb ... />
填写默认模板标记。
是的,可能只是直接从工具包中复制一个。回答您的问题“这是一个错误吗?”可能是的(我从不喜欢完全投入自己:)。我很高兴你找到了这个,我只是希望我记得当我遇到它时。
根据我在 Codeplex (codeplex.com/wpf/Thread/View.aspx?ThreadId=37625) 上找到的线程,这绝对是一个错误(并且可能会在工具包实际实施到框架中时修复)。同时,我一直在使用您找到的示例。我不得不对其进行一些按摩,以使箭头显示在标题文本旁边并将分隔符放回那里,但我想我几乎有了一个可行的解决方案(我会用代码发布答案当我一切正常时)。感谢您的所有帮助。以上是关于在 WPF 中需要自定义样式的帮助的主要内容,如果未能解决你的问题,请参考以下文章
WPF自定义控件与样式(13)-自定义窗体Window & 自适应内容大小消息框MessageBox
WPF自定义控件与样式-ScrollViewer与ListBox自定义样式