WPF dataGrid查找单个单元格的X和Y并设置背景颜色

发布于 2024-10-31 23:43:30 字数 5489 浏览 3 评论 0原文

我有一个绑定到数据网格(MVVM)的类型化数据集。我还有一个点列表(类型数据集中的 X 和 Y),表明哪些单元格有错误。检测这一点的逻辑很复杂并且在服务器端运行。

我的目标是如果每个单元格有错误,则将其背景绘制为不同的颜色(即点列表包含该单元格的 X 和 Y)。

dataGrid 的定义是:

 <DataGrid x:Name="gridData" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" 
              BorderThickness="0 0 0 0" Margin="0 0 0 0" AutoGenerateColumns="True" AlternationCount="1" AlternatingRowBackground="AliceBlue"
              ItemsSource="{Binding Path=EquisDataTable}" SelectionUnit="Cell" SelectedCellsChanged="gridData_SelectedCellsChanged"
              AutoGeneratedColumns="GridData_OnAutoGeneratedColumns" AutoGeneratingColumn="gridData_AutoGeneratingColumn" Height="350">
                        <DataGrid.CellStyle>
            <Style TargetType="{x:Type DataGridCell}">
                <Style.Setters>
                    <Setter Property="Background">
                        <Setter.Value>
                            <MultiBinding Converter="{StaticResource onErrorConverter}">
                                <Binding RelativeSource="{RelativeSource Self}" />
                                <Binding RelativeSource="{RelativeSource AncestorType=SampleTests:SampleTestUserControlBase}" Path="DataContext.Problems" />
                                <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}" />
                            </MultiBinding>
                        </Setter.Value>
                    </Setter> 
                </Style.Setters>
            </Style>
        </DataGrid.CellStyle>
    </DataGrid>

我相信我现在已经接近它了(经过大量测试和谷歌搜索才到达这里)。当填充所有内容后更改单元格选择时,我可以找到单元格的 X 和 Y,“SelectedCellsChanged”方法。这并不能让我到达每个单元格并在加载时检查其值。

    private void gridData_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
    {
        int x;
        int y;
        if (TryGetDataGridCellXandY(gridData.CurrentCell, gridData, out x, out y))               
        { }
    }      

    private bool TryGetDataGridCellXandY(DataGridCellInfo dgc, DataGrid dataGrid, out int x, out int y)
    {
        int columnIndex = dgc.Column.DisplayIndex;
        return TryGetDataGridCellXandY(columnIndex, dataGrid, out x, out y);
    }

    private bool TryGetDataGridCellXandY(int columnIndex, DataGrid dataGrid, out int x, out int y)
    {
        DataGridCellInfo currentCell = dataGrid.CurrentCell;

        int rowIndex = int.MinValue;
        DataRowView rowView = currentCell.Item as DataRowView;
        if (rowView != null)
        {
            DataRow dataRow = rowView.Row;
            FieldInfo fi = typeof(DataRow).GetField("_rowID", BindingFlags.NonPublic | BindingFlags.Instance);
            try
            {
                if (fi != null)
                {
                    rowIndex = System.Convert.ToInt32(fi.GetValue(dataRow));
                }
            }
            catch (InvalidCastException) { }

        }

        x = columnIndex;
        y = rowIndex;

        return x > 0 && y > 0;
    }

所以我创建了一个多值转换器来完成同样的事情。使用转换器时,当单元格的列为空时,就会出现问题,并且 currentCell.Item (DataGridCellInfo).Item 将具有 {DependencyProperty.UnsetValue}。

public class DataGridCellOnErrorConversion : IMultiValueConverter
{
    private const string DefaultColour = "White";
    private const string ErrorColour = "Red";
 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length != 3)
            return DefaultColour;

        DataGridCell dgc = values[0] as DataGridCell;
        if(dgc == null)
            return DefaultColour;

        IList<Point> problems = values[1] as IList<Point>;
        if(problems == null)
            return DefaultColour;

        DataGrid grid = values[2] as DataGrid;
        if (grid == null)
            return DefaultColour;


        int x;
        int y;
        if (TryGetDataGridCellXandY(grid.CurrentCell, grid, out x, out y))
        {
            if (problems.Any(problem => System.Convert.ToInt32(problem.X) == x && System.Convert.ToInt32(problem.Y) == y))
            {
                return ErrorColour;
            }
        }

        return DefaultColour;
  }
 private bool TryGetDataGridCellXandY(DataGridCellInfo dgc, DataGrid dataGrid, out int x, out int y)
    {
        int columnIndex = dgc.Column.DisplayIndex;
        return TryGetDataGridCellXandY(columnIndex, dataGrid, out x, out y);
    }

    private bool TryGetDataGridCellXandY(int columnIndex, DataGrid dataGrid, out int x, out int y)
    {
        DataGridCellInfo currentCell = dataGrid.CurrentCell;

        int rowIndex = int.MinValue;
        DataRowView rowView = currentCell.Item as DataRowView;
        if (rowView != null)
        {
            DataRow dataRow = rowView.Row;
            FieldInfo fi = typeof(DataRow).GetField("_rowID", BindingFlags.NonPublic | BindingFlags.Instance);
            try
            {
                if (fi != null)
                {
                    rowIndex = System.Convert.ToInt32(fi.GetValue(dataRow));
                }
            }
            catch (InvalidCastException) { }

        }

        x = columnIndex;
        y = rowIndex;

        return x > 0 && y > 0;
    }  
}

这是因为绑定/创建顺序吗?有办法解决这个问题吗?

谢谢

I have a typed dataset that is being bound to a datagrid (MVVM). I also have a list of Points (X and Y in the typed dataset) that say which cells have errors. The logic to detect this is complex and is run on the server end.

My goal is to paint each cell's background a different colour if they have an error, (i.e. the list of points contains that Cell's X and Y).

The dataGrid is defined as:

 <DataGrid x:Name="gridData" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" 
              BorderThickness="0 0 0 0" Margin="0 0 0 0" AutoGenerateColumns="True" AlternationCount="1" AlternatingRowBackground="AliceBlue"
              ItemsSource="{Binding Path=EquisDataTable}" SelectionUnit="Cell" SelectedCellsChanged="gridData_SelectedCellsChanged"
              AutoGeneratedColumns="GridData_OnAutoGeneratedColumns" AutoGeneratingColumn="gridData_AutoGeneratingColumn" Height="350">
                        <DataGrid.CellStyle>
            <Style TargetType="{x:Type DataGridCell}">
                <Style.Setters>
                    <Setter Property="Background">
                        <Setter.Value>
                            <MultiBinding Converter="{StaticResource onErrorConverter}">
                                <Binding RelativeSource="{RelativeSource Self}" />
                                <Binding RelativeSource="{RelativeSource AncestorType=SampleTests:SampleTestUserControlBase}" Path="DataContext.Problems" />
                                <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}" />
                            </MultiBinding>
                        </Setter.Value>
                    </Setter> 
                </Style.Setters>
            </Style>
        </DataGrid.CellStyle>
    </DataGrid>

I believe I am close to it now (taken a lot of testing and Googling to get here). I can find a Cell's X and Y when a cell selection is changed after everything has been populated, "SelectedCellsChanged" method. This does not let me get to every cell and check its value though on load.

    private void gridData_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
    {
        int x;
        int y;
        if (TryGetDataGridCellXandY(gridData.CurrentCell, gridData, out x, out y))               
        { }
    }      

    private bool TryGetDataGridCellXandY(DataGridCellInfo dgc, DataGrid dataGrid, out int x, out int y)
    {
        int columnIndex = dgc.Column.DisplayIndex;
        return TryGetDataGridCellXandY(columnIndex, dataGrid, out x, out y);
    }

    private bool TryGetDataGridCellXandY(int columnIndex, DataGrid dataGrid, out int x, out int y)
    {
        DataGridCellInfo currentCell = dataGrid.CurrentCell;

        int rowIndex = int.MinValue;
        DataRowView rowView = currentCell.Item as DataRowView;
        if (rowView != null)
        {
            DataRow dataRow = rowView.Row;
            FieldInfo fi = typeof(DataRow).GetField("_rowID", BindingFlags.NonPublic | BindingFlags.Instance);
            try
            {
                if (fi != null)
                {
                    rowIndex = System.Convert.ToInt32(fi.GetValue(dataRow));
                }
            }
            catch (InvalidCastException) { }

        }

        x = columnIndex;
        y = rowIndex;

        return x > 0 && y > 0;
    }

So I created a multi value converter to do the same thing. The problem arises when a Cell's Column is null when using the converter, also the currentCell.Item (DataGridCellInfo).Item will have {DependencyProperty.UnsetValue}.

public class DataGridCellOnErrorConversion : IMultiValueConverter
{
    private const string DefaultColour = "White";
    private const string ErrorColour = "Red";
 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length != 3)
            return DefaultColour;

        DataGridCell dgc = values[0] as DataGridCell;
        if(dgc == null)
            return DefaultColour;

        IList<Point> problems = values[1] as IList<Point>;
        if(problems == null)
            return DefaultColour;

        DataGrid grid = values[2] as DataGrid;
        if (grid == null)
            return DefaultColour;


        int x;
        int y;
        if (TryGetDataGridCellXandY(grid.CurrentCell, grid, out x, out y))
        {
            if (problems.Any(problem => System.Convert.ToInt32(problem.X) == x && System.Convert.ToInt32(problem.Y) == y))
            {
                return ErrorColour;
            }
        }

        return DefaultColour;
  }
 private bool TryGetDataGridCellXandY(DataGridCellInfo dgc, DataGrid dataGrid, out int x, out int y)
    {
        int columnIndex = dgc.Column.DisplayIndex;
        return TryGetDataGridCellXandY(columnIndex, dataGrid, out x, out y);
    }

    private bool TryGetDataGridCellXandY(int columnIndex, DataGrid dataGrid, out int x, out int y)
    {
        DataGridCellInfo currentCell = dataGrid.CurrentCell;

        int rowIndex = int.MinValue;
        DataRowView rowView = currentCell.Item as DataRowView;
        if (rowView != null)
        {
            DataRow dataRow = rowView.Row;
            FieldInfo fi = typeof(DataRow).GetField("_rowID", BindingFlags.NonPublic | BindingFlags.Instance);
            try
            {
                if (fi != null)
                {
                    rowIndex = System.Convert.ToInt32(fi.GetValue(dataRow));
                }
            }
            catch (InvalidCastException) { }

        }

        x = columnIndex;
        y = rowIndex;

        return x > 0 && y > 0;
    }  
}

Is this because of a binding / creation order? Is there a way around this?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

千纸鹤 2024-11-07 23:43:30

这就是我解决问题的方法,(不确定它是否是最佳的,但似乎有效,(到目前为止):

我在数据网格单元格的样式上有一个多值转换器:

      <Style TargetType="{x:Type DataGridCell}">
           <Style.Setters>
               <Setter Property="Background">
                   <Setter.Value>
                       <MultiBinding Converter="{StaticResource onErrorConverter}">
                           <Binding RelativeSource="{RelativeSource Self}" />
                           <Binding RelativeSource="{RelativeSource AncestorType=SampleTests:SampleTestUserControlBase}" Path="DataContext.Problems" />
                           <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}" />
                       </MultiBinding>
                   </Setter.Value>
               </Setter> 
           </Style.Setters>
      </Style>

并且转换器:

public class DataGridCellOnErrorConversion : IMultiValueConverter
{
    private readonly SolidColorBrush DefaultColour = new SolidColorBrush(Colors.White);
    private readonly SolidColorBrush ErrorColour = new SolidColorBrush(Colors.Red);
    private readonly SolidColorBrush AlternatingColour = new SolidColorBrush(Colors.AliceBlue);


    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length < 3)
            return DefaultColour;

        DataGridCell dgc = values[0] as DataGridCell;
        if(dgc == null)
            return DefaultColour;

        IList<Point> problems = values[1] as IList<Point>;
        if(problems == null)
            return DefaultColour;

        DataGrid grid = values[2] as DataGrid;
        if (grid == null)
            return DefaultColour;
        int x;
        int y = -1;

        ItemCollection itemCollection = grid.Items;

        for (int i = 0; i < itemCollection.Count; i++)
        {
            if (itemCollection.CurrentItem == itemCollection[i])
                y = i;
        }

        x = dgc.Column.DisplayIndex;

        DataRowView currentRowView = null;
        FieldInfo fi = dgc.GetType().GetField("_owner", BindingFlags.NonPublic | BindingFlags.Instance);
        try
        {
            if (fi != null)
            {
                DataGridRow dataGridRow = fi.GetValue(dgc) as DataGridRow;
                if(dataGridRow != null)
                    currentRowView = dataGridRow.Item as DataRowView;
            }
        }
        catch (InvalidCastException) { }

        if(currentRowView != null)
        {
            for (int i = 0; i < itemCollection.Count; i++)
            {
                if (currentRowView == itemCollection[i])
                    y = i;
            }
        }

        if (problems.Any(problem => System.Convert.ToInt32(problem.X) == x && System.Convert.ToInt32(problem.Y) == y))
        {
            return ErrorColour;
        }

        return y % 2 == 0 ? AlternatingColour : DefaultColour;
    }
}

This is how I solved my problem, (not sure if it is optimal but seems to work, (so far):

I have a multi value converter on the Style for the datagrid cell:

      <Style TargetType="{x:Type DataGridCell}">
           <Style.Setters>
               <Setter Property="Background">
                   <Setter.Value>
                       <MultiBinding Converter="{StaticResource onErrorConverter}">
                           <Binding RelativeSource="{RelativeSource Self}" />
                           <Binding RelativeSource="{RelativeSource AncestorType=SampleTests:SampleTestUserControlBase}" Path="DataContext.Problems" />
                           <Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}" />
                       </MultiBinding>
                   </Setter.Value>
               </Setter> 
           </Style.Setters>
      </Style>

And the converter:

public class DataGridCellOnErrorConversion : IMultiValueConverter
{
    private readonly SolidColorBrush DefaultColour = new SolidColorBrush(Colors.White);
    private readonly SolidColorBrush ErrorColour = new SolidColorBrush(Colors.Red);
    private readonly SolidColorBrush AlternatingColour = new SolidColorBrush(Colors.AliceBlue);


    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length < 3)
            return DefaultColour;

        DataGridCell dgc = values[0] as DataGridCell;
        if(dgc == null)
            return DefaultColour;

        IList<Point> problems = values[1] as IList<Point>;
        if(problems == null)
            return DefaultColour;

        DataGrid grid = values[2] as DataGrid;
        if (grid == null)
            return DefaultColour;
        int x;
        int y = -1;

        ItemCollection itemCollection = grid.Items;

        for (int i = 0; i < itemCollection.Count; i++)
        {
            if (itemCollection.CurrentItem == itemCollection[i])
                y = i;
        }

        x = dgc.Column.DisplayIndex;

        DataRowView currentRowView = null;
        FieldInfo fi = dgc.GetType().GetField("_owner", BindingFlags.NonPublic | BindingFlags.Instance);
        try
        {
            if (fi != null)
            {
                DataGridRow dataGridRow = fi.GetValue(dgc) as DataGridRow;
                if(dataGridRow != null)
                    currentRowView = dataGridRow.Item as DataRowView;
            }
        }
        catch (InvalidCastException) { }

        if(currentRowView != null)
        {
            for (int i = 0; i < itemCollection.Count; i++)
            {
                if (currentRowView == itemCollection[i])
                    y = i;
            }
        }

        if (problems.Any(problem => System.Convert.ToInt32(problem.X) == x && System.Convert.ToInt32(problem.Y) == y))
        {
            return ErrorColour;
        }

        return y % 2 == 0 ? AlternatingColour : DefaultColour;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文