以下方法在自定义控件内部实现。它以DataTable table
作为参数,并用表中的值填充网格grdMain
。该表的每一列都应在鼠标悬停时更改其颜色。但是,当我尝试将样式附加到ColumnDefinition时,它将引发异常:
System.ArgumentException:'不允许样式对象影响 适用对象的样式属性。
private void DrawGrid(DataTable table)
{
// Prepare style to apply
var gridColumnStyle = GetColumnStyles();
foreach (var column in table.Columns)
{
var columnDefinition = new ColumnDefinition();
columnDefinition.OverridesDefaultStyle = true;
// System.ArgumentException: 'Style object is not allowed to affect the Style property of the object to which it applies.'
columnDefinition.Style = gridColumnStyle;
grdMain.ColumnDefinitions.Add(columnDefinition);
}
int rowNumber = 0;
foreach (DataRow row in table.Rows)
{
grdMain.RowDefinitions.Add(new RowDefinition());
for (int columnNumber = 0; columnNumber < table.Columns.Count; columnNumber++)
{
var cellText = new TextBlock()
{
Text = row[columnNumber].ToString(),
};
grdMain.Children.Add(cellText);
cellText.SetValue(Grid.RowProperty, rowNumber);
cellText.SetValue(Grid.ColumnProperty, columnNumber);
}
rowNumber++;
}
}
如何以编程方式将样式应用于列定义?
UPD::这是GetColumnStyles()
的实现:
private Style GetColumnStyles()
{
var columnStyle = new DataVisualizer.Desktop.Views.Styles.ColumnSelectionTableStyle();
var columnHoverBrush = columnStyle["ColumnHoverBrush"];
var columnBrush = columnStyle["ColumnBrush"];
DataTrigger columnMouseHoverTrigger = new DataTrigger()
{
Binding = new Binding("IsMouseOver"),
Value = true
};
columnMouseHoverTrigger.Setters.Add(new Setter()
{
Property = StyleProperty,
Value = columnHoverBrush
});
var gridColumnStyle = new Style();
gridColumnStyle.Triggers.Add(columnMouseHoverTrigger);
return gridColumnStyle;
}
ColumnSelectionTableStyle
在单独的文件中定义:
<ResourceDictionary
x:Class="DataVisualizer.Desktop.Views.Styles.ColumnSelectionTableStyle"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<SolidColorBrush x:Key="StandardSolidColorBrush" Color="Blue" />
<LinearGradientBrush x:Key="StandardLinearGradientBrush" StartPoint="0.0,0.0" EndPoint="1.0,1.0">
<LinearGradientBrush.GradientStops>
<GradientStop Color="White" Offset="0" />
<GradientStop Color="Black" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
<SolidColorBrush x:Key="ColumnHoverBrush" Color="BlueViolet" Opacity=".5"/>
<SolidColorBrush x:Key="ColumnBrush" Color="White" Opacity="1"/>
</ResourceDictionary>
答案 0 :(得分:1)
事实证明这是比我预期的还要复杂的问题。我们走了。
如果我理解正确,那么您将在Brush
的类ColumnSelectionTableStyle
中存储各种ResourceDictionary
。然后,您想使用那些Brush
来为Style
的列创建一个Grid
。
代码中直接导致异常的部分是Property = StyleProperty
中的行GetColumnStyles
。正如例外所言,您不能使用Style
来更改正在使用的Style
(这会产生一些奇怪的悖论)。在这里不是很重要的原因是,这实际上并不是您想要执行的操作。
ColumnDefinition
如果我确实了解,Trigger
仅需要设置列的 background ,而无需更改其整个Style
。通常,我建议您将Trigger
目标设为Background
属性,但这就是您遇到的真正问题的地方。 ColumnDefinition
没有“背景”属性。
ColumnDefinition
实际上并不“包含”该列中的元素,它不是Control
,甚至不是可见元素。 Grid
仅使用它来管理其子级的布局。如果希望Grid
的特定列具有颜色,则需要在该列中放置可见的内容。我建议使用Border
或Rectangle
。
要使您的列看上去有颜色,请在Rectangle
上添加Border
/ Grid
,分别设置Grid.Column
和Grid.RowSpan
,然后然后设置该Background
/ Rectangle
的{{1}}属性。我还要设置Border
,因为您希望背景元素的作用就好像它不存在一样。然后,您将在此背景元素上方(而不是内部)顶部的列中添加其他元素。
IsHitTestVisible = false
从技术上来说确实具有ColumnDefinition
属性(从基类继承),但是根据我的测试,它实际上不起作用。
IsMouseOver
和Rectangle
都具有有效的Border
属性,但是只有当鼠标直接放在元素(或其子元素之一)上时,这些属性才起作用)之间没有其他内容。由于您将要在其上放置附加元素,因此那些“较高”元素将“窃取” IsMouseOver
,因此背景元素将无法用作可靠的触发条件。
基本上,如果要在MouseOver上更改列背景的颜色,则必须变脏。以我的方式看,您可以:
A。在IsMouseOver
级别使用MouseMove
或PreviewMouseMove
事件来跟踪鼠标的位置,确定鼠标所在的列,然后手动更改相应背景的Grid
属性元素。
B。在每个单元格的根元素处侦听Background
的更改,然后检查该单元格所在的列并手动更改相应背景元素的IsMouseOver
属性。
创建自定义控件需要大量工作。
您不能使用样式来替换自身(但不必这样做)。
Background
没有背景,但是ColumnDefinition
和Rectangle
都有背景。
答案 1 :(得分:0)
正如Keith Stein所建议的那样,最简单(也是唯一可能的)解决方法是更改网格列是使用Rectangle
。就我而言,我以编程方式将它们添加到每列的上方,并将RowSpan
设置为行数:
private Rectangle GetColumnRectangle(int colNumber, int rowsNumber)
{
Rectangle rect = new Rectangle();
rect.Fill = _columnNormal;
rect.SetValue(Grid.ColumnProperty, colNumber);
rect.SetValue(Grid.RowSpanProperty, rowsNumber);
rect.SetValue(Grid.ZIndexProperty, 10);
//Subscribe to events
rect.MouseEnter += OnColumnMouseEnter;
rect.MouseLeave += OnColumnMouseLeave;
rect.MouseDown += OnColumnSelected;
return rect;
}
DrawGrid
方法相对于新方法签名已更改:
private void DrawGrid(DataTable table)
{
foreach (var column in table.Columns)
{
grdMain.ColumnDefinitions.Add(new ColumnDefinition());
}
int rowNumber = 0;
foreach (DataRow row in table.Rows)
{
grdMain.RowDefinitions.Add(new RowDefinition());
for (int columnNumber = 0; columnNumber < table.Columns.Count; columnNumber++)
{
var cellText = new TextBlock()
{
Text = row[columnNumber].ToString(),
};
grdMain.Children.Add(cellText);
cellText.SetValue(Grid.RowProperty, rowNumber);
cellText.SetValue(Grid.ColumnProperty, columnNumber);
}
rowNumber++;
}
for (int colNumber = 0; colNumber < grdMain.ColumnDefinitions.Count; colNumber++)
{
var rect = GetColumnRectangle(colNumber, rowNumber);
grdMain.Children.Add(rect);
//Dictionary; indicating whether the column is selected
_rectangles.Add(rect, false);
}
}
将列标记为选中状态的第一个想法肯定是扩展Rectangle
以添加一个Selected
属性,但是它是sealed
,因此最简单的解决方案是字典。
在我的情况下,在OnColumnMouseEnter
,OnColumnMouseLeave
和OnColumnSelected
中应用的样式包含不同的不透明度值,这使Rectangle
的行为就像在后台一样。>
如果您需要在列后面有矩形的解决方案,则应使用Keith Steins解决方案。