以编程方式更改ColumnDefinition样式

时间:2019-12-05 01:03:27

标签: c# wpf

以下方法在自定义控件内部实现。它以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>

2 个答案:

答案 0 :(得分:1)

事实证明这是比我预期的还要复杂的问题。我们走了。

如果我理解正确,那么您将在Brush的类ColumnSelectionTableStyle中存储各种ResourceDictionary。然后,您想使用那些Brush来为Style的列创建一个Grid

异常(不是很重要)

代码中直接导致异常的部分是Property = StyleProperty中的行GetColumnStyles。正如例外所言,您不能使用Style来更改正在使用的Style(这会产生一些奇怪的悖论)。在这里不是很重要的原因是,这实际上并不是您想要执行的操作。

ColumnDefinition

的问题

如果我确实了解,Trigger仅需要设置列的 background ,而无需更改其整个Style。通常,我建议您将Trigger目标设为Background属性,但这就是您遇到的真正问题的地方。 ColumnDefinition没有“背景”属性。

ColumnDefinition实际上并不“包含”该列中的元素,它不是Control,甚至不是可见元素。 Grid仅使用它来管理其子级的布局。如果希望Grid的特定列具有颜色,则需要在该列中放置可见的内容。我建议使用BorderRectangle

为其提供背景

要使您的列看上去有颜色,请在Rectangle上添加Border / Grid,分别设置Grid.ColumnGrid.RowSpan,然后然后设置该Background / Rectangle的{​​{1}}属性。我还要设置Border,因为您希望背景元素的作用就好像它不存在一样。然后,您将在此背景元素上方(而不是内部)顶部的列中添加其他元素。

IsMouseOver触发器

IsHitTestVisible = false从技术上来说确实具有ColumnDefinition属性(从基类继承),但是根据我的测试,它实际上不起作用。

IsMouseOverRectangle都具有有效的Border属性,但是只有当鼠标直接放在元素(或其子元素之一)上时,这些属性才起作用)之间没有其他内容。由于您将要在其上放置附加元素,因此那些“较高”元素将“窃取” IsMouseOver,因此背景元素将无法用作可靠的触发条件。

基本上,如果要在MouseOver上更改列背景的颜色,则必须变脏。以我的方式看,您可以:

A。在IsMouseOver级别使用MouseMovePreviewMouseMove事件来跟踪鼠标的位置,确定鼠标所在的列,然后手动更改相应背景的Grid属性元素。

B。在每个单元格的根元素处侦听Background的更改,然后检查该单元格所在的列并手动更改相应背景元素的IsMouseOver属性。

TL; DR

创建自定义控件需要大量工作。
您不能使用样式来替换自身(但不必这样做)。
Background没有背景,但是ColumnDefinitionRectangle都有背景。

答案 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,因此最简单的解决方案是字典。 在我的情况下,在OnColumnMouseEnterOnColumnMouseLeaveOnColumnSelected中应用的样式包含不同的不透明度值,这使Rectangle的行为就像在后台一样。

如果您需要在列后面有矩形的解决方案,则应使用Keith Steins解决方案。

相关问题