下面的方法是在自定义控件中实现的。它以DataTable table作为参数,并使用表中的值填充网格grdMain。此表的每一列都应在鼠标悬停时更改其颜色。但是,当我试图将样式附加到ColumnDefinition时,它会抛出一个异常:
System.ArgumentException:'Style对象不允许影响其应用对象的样式属性。
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>发布于 2019-12-06 15:12:21
事实证明,这是一个比我预期的更复杂的问题。我们开始吧。
如果我正确理解了这一点,您将把各种Brushes存储在ColumnSelectionTableStyle类中,该类继承自ResourceDictionary。然后,您需要使用这些Brushes为Grid的列创建一个Style。
例外(不太重要)
代码中直接导致异常的部分是GetColumnStyles中的行GetColumnStyles。正如例外说的那样,您不能使用Style来更改正在使用的Style (这会产生一些奇怪的悖论)。这并不是很重要的原因,因为这并不是你想要做的。
ColumnDefinition的问题
如果我理解的话,Trigger只需要设置列的背景,而不需要更改它的整个Style。通常,我建议您将Trigger目标设为Background属性,但这正是您遇到真正问题的地方。ColumnDefinition没有“背景”属性。
ColumnDefinition实际上并不“包含”列中的元素,它不是Control,甚至不是可见的元素。Grid只使用它来管理其子级的布局。如果希望Grid的某一列具有颜色,则需要在该列中放置可见的内容。我建议使用Border或Rectangle。
给它一个背景
为了显示您的列有颜色,请将Rectangle/Border添加到Grid中,适当地设置Grid.Column和Grid.RowSpan,然后设置Rectangle/Border的Background属性。我还会设置IsHitTestVisible = false,因为您希望后台元素的作用就好像它甚至不在那里一样。然后,您将在列中添加此背景元素的顶部(而不是内部)的其他元素。
IsMouseOver触发器
ColumnDefinition在技术上确实有一个IsMouseOver属性(继承自基类),但根据我测试的结果,它实际上不起作用。
Rectangle和Border都具有工作的IsMouseOver属性,但这些属性只有在鼠标直接位于元素(或其子元素)之上而没有其他属性的情况下才能工作。由于要将加法元素放在它们上面,这些“较高”元素将“窃取”IsMouseOver,因此后台元素不会作为可靠的触发器工作。
基本上,如果你想让列的背景在MouseOver上改变颜色,你就得弄脏。在我看来,你可以:
使用MouseMove或PreviewMouseMove事件在Grid级别跟踪鼠标的位置,确定它在哪个列中,然后手动更改相应的背景元素的Background属性。
在每个单元格的根元素处侦听对IsMouseOver的更改,然后检查该单元格所在的列,并手动更改相应背景元素的Background属性。
TL;DR
创建自定义控件是一项很大的工作。
您不能使用样式来替换自身(但不需要)。
ColumnDefinition没有背景,但Rectangle和Border都有。
发布于 2019-12-06 20:49:26
正如Keith所建议的,最简单(也可能是唯一的)解决方法是更改网格列,即使用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解决方案。
https://stackoverflow.com/questions/59186743
复制相似问题