C# 以编程方式更改列定义样式
下面的方法是在自定义控件内实现的。它将DataTable作为参数,并用表中的值填充网格grdMain。鼠标悬停时,此表的每列都应更改其颜色。但当我尝试将样式附加到ColumnDefinition时,它会抛出一个异常: System.ArgumentException:'不允许样式对象影响 应用它的对象的样式属性 ColumnSelectionTableStyle在单独的文件中定义:C# 以编程方式更改列定义样式,c#,wpf,C#,Wpf,下面的方法是在自定义控件内实现的。它将DataTable作为参数,并用表中的值填充网格grdMain。鼠标悬停时,此表的每列都应更改其颜色。但当我尝试将样式附加到ColumnDefinition时,它会抛出一个异常: System.ArgumentException:'不允许样式对象影响 应用它的对象的样式属性 ColumnSelectionTableStyle在单独的文件中定义: <ResourceDictionary x:Class="DataVisualizer.Deskt
<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>
这是一个比我想象的更复杂的问题。我们开始吧 如果我理解正确,您将在继承自ResourceDictionary的ColumnSelectionTableStyle类中存储各种笔刷。然后,您需要使用这些笔刷为轴网的柱创建样式 例外情况并不十分重要 直接导致异常的代码部分是GetColumnStyles中的line Property=StyleProperty。正如例外情况所说,你不能用一种风格来改变正在使用的风格,这会产生一些奇怪的悖论。这里不太重要的原因是,这不是你真正想要做的 列定义的问题 如果我理解的话,触发器只需要设置列的背景,而不需要更改其整个样式。通常我会建议您将触发器目标设置为Background属性,但这正是您遇到真正问题的地方。ColumnDefinition没有背景属性 ColumnDefinition实际上并不包含该列中的元素,它不是控件,甚至不是可见元素。Grid仅使用它来管理其子级的布局。如果希望网格的特定列具有颜色,则需要在该列中放置可见的内容。我建议使用边框或矩形 给它一个背景 要使列看起来有颜色,请向网格添加矩形/边框,适当设置Grid.column和Grid.RowSpan,然后设置该矩形/边框的背景属性。我还将设置ishitestvisible=false,因为您希望background元素的行为就像它不在那里一样。然后将其他元素添加到列的顶部,而不是此背景元素的内部 扳机 从技术上讲,ColumnDefinition确实具有从基类继承的IsMouseOver属性,但从我测试的结果来看,它实际上不起作用 矩形和边框都有working IsMouseOver属性,但只有当鼠标直接位于元素或其子元素之一上且其间没有其他内容时,这些属性才起作用。由于您将在它们上面放置附加元素,这些较高的元素将窃取IsMouseOver,因此背景元素不能作为可靠的触发器工作 基本上,如果你想让鼠标上方的列背景改变颜色,你必须弄脏。在我看来,你可以: A.在网格级别使用MouseMove或PreviewMouseMove事件跟踪鼠标位置,确定鼠标所在列,然后手动更改相应背景元素的背景属性 B.在每个单元格的根元素上侦听对IsMouseOver的更改,然后检查该单元格所在的列,并手动更改相应背景元素的背景属性 TL;博士 创建自定义控件需要大量的工作。 您不能使用样式替换自身,但不需要。
列定义没有背景,但矩形和边框都有。正如Keith Stein所建议的,最简单且可能唯一的解决方法是更改网格列,即使用矩形。在本例中,我以编程方式将它们添加到每列的正上方,并将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);
}
}
将列标记为选中的第一个想法当然是扩展矩形以添加选中的属性,但它是密封的,所以最简单的解决方案是使用字典。
在我的例子中,OnColumnMouseEnter、OnColumnMouseLeave和OnColumnSelected contain中应用的样式具有不同的不透明度值,这使得矩形可以像在背景中一样工作
如果您需要列后面有矩形的解决方案,您应该使用Keith Steins解决方案。gridColumnStyle是数组还是样式本身?我可能错了,但您可能必须应用gridColumnStyle[0]。StyleIt是itslef的样式。在我运行代码之前,不会有任何错误甚至警告。@ArliChokoev您不能在样式的setter中设置Style属性。这就是错误消息告诉您的。@mm8那么我该如何应用样式?@ArliChokoev:您想将什么样式应用于什么元素?答案非常详细,谢谢。我已经找到了一种处理鼠标悬停的方法,使用矩形,我将分别发布它。但是,什么样的
如果将样式设置为ColumnDefinition的Style属性,会遇到什么矛盾?@ArliChokoev一旦从元素中删除样式,该样式设置的属性将恢复为以前的值。因此,首先不应用样式,然后应用样式A,但A表示将样式设置为B。因此WPF首先删除A以设置B,但一旦删除A,它也会将命令集样式删除为B,这意味着样式将恢复为以前的值,这是A。现在我们又回到了我们开始尝试再次应用A的地方。我认为如果控件中有一个交互元素,这是行不通的。例如,如果你有一个按钮,并试图点击它,你将无法点击,因为矩形会挡住你的去路。但是看起来只有文本块,所以应该没问题。是的,它只适用于在这个矩形后面不需要元素的情况
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;
}
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);
}
}