我已经开始在 WPF 中使用 dataGrid,但来自 WinForm,它与我习惯的有点不同..
我现在的问题是我需要获取特定单元格的矩形(以便我可以访问其大小和位置)。
在 WinForm 的数据网格中,有一个方法称为:
cellRectangle = dataGridView.GetCellDisplayRectangle(columnIndex, rowIndex, cutOverflow)
但是,我似乎在 WPF 中找不到类似的东西,而且与我以前在 WinForm 中能做的相比,我在网上能找到的所有内容都非常复杂..
如果可能的话,我想避免任何 XAML 代码,并全部用 C# 代码编写
希望你们中有人能解决我的问题。
WPF 不使用坐标,而是使用容器。如果您想将某些内容放入 DataGridCell,请找到它并将新内容放入 DataGridCell.Content 中。
在我的示例中,您可以输入行号和列号,然后按“执行代码”按钮。这会将按钮放置到网格中所需的位置。单击“Click Me”后,该按钮消失并显示旧的单元格内容。
<Window x:Class="DataGridCell.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataGridCell"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Background="LightGray">
<Label Content="Row:"/>
<TextBox Name="RowTextBox" Text="2"/>
<Label Content="Column:"/>
<TextBox Name="ColumnTextBox" Text="1"/>
<Button DockPanel.Dock="Bottom" Content="Execute Code" Name="ExecuteButton"/>
</StackPanel>
<DataGrid Name="MainDataGrid" ItemsSource="{Binding}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Col0" Binding="{Binding Col0}"/>
<DataGridTextColumn Header="Col1" Binding="{Binding Col1}" />
<DataGridTextColumn Header="Col2" Binding="{Binding Col2}" />
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Window>
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
namespace DataGridCell {
public class RowData {
public string Col0 { get; set; }
public string Col1 { get; set; }
public string Col2 { get; set; }
}
public partial class MainWindow: Window {
public MainWindow() {
InitializeComponent();
var dataList = new List<RowData>();
dataList.Add(new RowData {Col0="0.0", Col1="0.1", Col2="0.2"});
dataList.Add(new RowData {Col0="1.0", Col1="1.1", Col2="1.2"});
dataList.Add(new RowData {Col0="2.0", Col1="2.1", Col2="2.2"});
dataList.Add(new RowData {Col0="3.0", Col1="3.1", Col2="3.2"});
ObservableCollection<RowData> custdata = new ObservableCollection<RowData>(dataList);
MainDataGrid.DataContext = custdata;
ExecuteButton.Click+=ExecuteButton_Click;
}
ContentControl cell;
object oldContent;
private void ExecuteButton_Click(object sender, RoutedEventArgs e) {
var row = int.Parse(RowTextBox.Text);
var col = int.Parse(ColumnTextBox.Text);
var cellContent = (FrameworkElement)MainDataGrid.Columns[col].GetCellContent(MainDataGrid.Items[row]);
cell = (ContentControl)cellContent.Parent;
var button = new Button {Content="Click me" };
button.Click += Button_Click;
oldContent = cell.Content;
cell.Content = button;
ExecuteButton.IsEnabled = false;
}
private void Button_Click(object sender, RoutedEventArgs e) {
cell.Content = oldContent;
ExecuteButton.IsEnabled = true;
}
}
}
虽然这可行,但有点麻烦。如果数据网格重新创建其内容,您的按钮将丢失。例如,如果用户单击列标题对网格进行排序,就会发生这种情况。
如果您确实需要单元格的尺寸,您可以使用 cell.ActualWidth 和 cell.ActualHeight 获取它。但这些值可能随时更改,例如当用户更改列的宽度时。 WPF 中不使用坐标。如果您需要定义控件的确切位置,请使用它的边距来定位它。
获取单元格的中心: (微软真丢脸,这个解决方案与 winforms 相比复杂得离谱)
public Point GetCenterPointOfCell(int rowId_0Based, int colId_0Based)
{
Point ret = Point.Empty;
_grid.Dispatcher.Invoke(new Action(() =>
{
var item = _grid.Items[rowId_0Based];
int currentRow = _grid.SelectedIndex == -1 ? 0 : _grid.SelectedIndex;
System.Windows.DependencyObject dep = _grid.ItemContainerGenerator.ContainerFromIndex(currentRow);
var dataGridRow = dep as DataGridRow;
if (dataGridRow is null)
{
string msg = $"Cannot acces row[{currentRow}] of datagrid";
_log.Error(msg);
throw new AccessViolationException(msg);
}
int visibleRowCount = (int)(_grid.ActualHeight / dataGridRow.ActualHeight * 0.8);
if (rowId_0Based > _grid.SelectedIndex + visibleRowCount || rowId_0Based < _grid.SelectedIndex)
{
object targetScroll;
if (rowId_0Based + visibleRowCount<_grid.Items.Count)
{
targetScroll = _grid.Items[rowId_0Based + visibleRowCount];
}
else
{
targetScroll = item;
}
_grid.ScrollIntoView(targetScroll);
_grid.UpdateLayout();
}
var cellContentElement = _grid.Columns[colId_0Based].GetCellContent(item);
if (cellContentElement == null)
throw new Exception("cannot get cell content");
System.Windows.Rect cellBounds = cellContentElement.TransformToAncestor(_grid).TransformBounds(new System.Windows.Rect(0, 0, cellContentElement.ActualWidth, cellContentElement.ActualHeight));
var centerPoint = new System.Windows.Point((int)(cellBounds.X + cellBounds.Width / 2), (int)(cellBounds.Y + cellBounds.Height / 2));
System.Windows.Point winPoint = _grid.PointToScreen(centerPoint);
ret = new Point((int)winPoint.X, (int)winPoint.Y);
}));
return ret;
}