我有一个带有行和列的WPF Grid
,例如
<Grid Name="myGrid" MouseMove="OnMouseMove">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
在.cs文件中使用MouseMove
处理程序,例如
private void OnMouseMove(object sender, MouseEventArgs e)
{
var position = e.GetPosition(myGrid);
// What row & col is the mouse over?
}
我希望能够找到鼠标结束的网格中的哪一行和哪一列,这可能吗?
[注意:这是问题的简化版本,因此以这种方式呈现它看起来有点奇怪 - 它是一些拖累和放大器的一部分。在网格功能之间删除]
答案 0 :(得分:16)
我希望你已经找到了解决方案。我遇到了同样的问题,我找到了这个无人接听的帖子。所以我认为下一个会很乐意找到这个解决方案:
private void OnMouseMove(object sender, MouseEventArgs e)
{
var element = (UIElement)e.Source;
int c = Grid.GetColumn(element);
int r = Grid.GetRow(element);
}
答案 1 :(得分:5)
以下可以完成(但不应在较大的网格上使用)。它仅适用于Rows,但第二个循环可以应用于列...
Nicolas的答案只有在网格包含一些UIElements(它们在网格中的位置被确定 - 我想如果一个元素跨越多行/列时也会造成麻烦)时才有效。
private void ItemsGrid_MouseMove(object sender, MouseEventArgs e)
{
double y = e.GetPosition(ItemsGrid).Y;
double start = 0.0;
int row = 0;
foreach(RowDefinition rd in ItemsGrid.RowDefinitions)
{
start += rd.ActualHeight;
if (y < start)
{
break;
}
row++;
}
System.Diagnostics.Debug.WriteLine("Row : " + row);
}
答案 2 :(得分:2)
public static class GridExtentions
{
public static T Parent<T>(this DependencyObject root) where T : class
{
if (root is T) { return root as T; }
DependencyObject parent = VisualTreeHelper.GetParent(root);
return parent != null ? parent.Parent<T>() : null;
}
public static Point GetColumnRow(this Grid obj, Point relativePoint) { return new Point(GetColumn(obj, relativePoint.X), GetRow(obj, relativePoint.Y)); }
private static int GetRow(Grid obj, double relativeY) { return GetData(obj.RowDefinitions, relativeY); }
private static int GetColumn(Grid obj, double relativeX) { return GetData(obj.ColumnDefinitions, relativeX); }
private static int GetData<T>(IEnumerable<T> list, double value) where T : DefinitionBase
{
var start = 0.0;
var result = 0;
var property = typeof(T).GetProperties().FirstOrDefault(p => p.Name.StartsWith("Actual"));
if (property == null) { return result; }
foreach (var definition in list)
{
start += (double)property.GetValue(definition);
if (value < start) { break; }
result++;
}
return result;
}
}
用法:
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
var hit = VisualTreeHelper.HitTest(this, e.GetPosition(this));
if (hit == null) { return; }
var grid = hit.VisualHit.Parent<Grid>();
if (grid == null) { return; }
var gridPosition = grid.GetColumnRow(e.GetPosition(grid));
MessageBox.Show(string.Format("Grid location Row: {1} Column: {0}", gridPosition.X, gridPosition.Y));
}
答案 3 :(得分:1)
希望可以提供帮助。它对我的应用程序运行良好
public class GridCell
{
public int GridRow { get; set; }
public int GridCol { get; set; }
}
private void Window_MouseMove(object sender, MouseEventArgs e)
{
Point point = e.GetPosition(this.myGrid);
Grid gridTarget = GetLastedGridContainPoint(point, this.myGrid);
Point pointTarget = this.myGrid.TranslatePoint(point, gridTarget);
GridCell cell = GetGridCellContainPoint(pointTarget, gridTarget);
}
private bool IsPointInGrid(Point relativePoint, Grid grid)
{
if (relativePoint.X < 0 || relativePoint.X > grid.ActualWidth ||
relativePoint.Y < 0 || relativePoint.Y > grid.ActualHeight)
{
return false;
}
return true;
}
private Grid GetLastedGridContainPoint(Point relativePoint, Grid gridParent)
{
Grid gridReturn = null;
if (gridParent.Children.Count > 0)
{
Point relativeChildPoint;
foreach (UIElement e in gridParent.Children)
{
if (e is Grid)
{
relativeChildPoint = gridParent.TranslatePoint(relativePoint, (e as Grid));
gridReturn = GetLastedGridContainPoint(relativeChildPoint, (e as Grid));
if (gridReturn != null)
{
break;
}
}
}
}
if (gridReturn == null)
{
if (IsPointInGrid(relativePoint, gridParent))
{
gridReturn = gridParent;
}
}
return gridReturn;
}
private GridCell GetGridCellContainPoint(Point relativePoint, Grid gridTarget)
{
if (!IsPointInGrid(relativePoint, gridTarget))
{
return null;
}
GridCell cell = new GridCell();
double dbStart = 0;
double dbEnd = 0;
if (gridTarget.ColumnDefinitions.Count > 0)
{
for (int i = 0; i < gridTarget.ColumnDefinitions.Count; i++)
{
dbStart = dbEnd;
dbEnd += gridTarget.ColumnDefinitions[i].ActualWidth;
if (relativePoint.X >= dbStart && relativePoint.X < dbEnd)
{
cell.GridCol = i;
break;
}
}
}
dbStart = 0;
dbEnd = 0;
if (gridTarget.RowDefinitions.Count > 0)
{
for (int i = 0; i < gridTarget.RowDefinitions.Count; i++)
{
dbStart = dbEnd;
dbEnd += gridTarget.RowDefinitions[i].ActualHeight;
if (relativePoint.Y >= dbStart && relativePoint.Y < dbEnd)
{
cell.GridRow = i;
break;
}
}
}
return cell;
}
答案 4 :(得分:1)
Andre的回答有一个小错误,因为获得的坐标不考虑DataGrid中的行和列标题。至少在我用Visual Basic实现解决方案时就是这种情况。
您还可以修改显示的示例以考虑大型DataGrid。在我看来,限制是基于滚动视图,所以我展示了这种修正的两个实现:
Private Sub myGrid_MouseMove(sender As Object, e As MouseEventArgs) Handles myGrid.MouseMove
Dim total As Double
Dim myScrollViewer As ScrollViewer = FindVisualChild(Of ScrollViewer)(myGrid)
Dim cursorPositionX = e.GetPosition(myGrid).X
Dim columnIndex As Integer = -1
total = 0
'Horizontal offset'
Dim rowHeaders As DataGridRowHeader = FindVisualChild(Of DataGridRowHeader)(myGrid)
cursorPositionX -= (rowHeaders.ActualWidth - myScrollViewer.HorizontalOffset)
For Each column As DataGridColumn In myGrid.Columns
If cursorPositionX < total Then Exit For
columnIndex += 1
total += column.Width.DisplayValue
Next
Dim cursorPositionY = e.GetPosition(myGrid).Y
Dim rowIndex As Integer = -1
total = 0
'Vertical offset'
Dim originalOffset As Double = myScrollViewer.VerticalOffset
Dim colHeadersPresenter As DataGridColumnHeadersPresenter = FindVisualChild(Of DataGridColumnHeadersPresenter)(myGrid)
cursorPositionY -= colHeadersPresenter.ActualHeight
For Each row As System.Data.DataRowView In myGrid.Items
If cursorPositionY < total Then Exit For
rowIndex += 1
Dim dgRow As DataGridRow = GetRowByIndex(myGrid, rowIndex)
total += dgRow.ActualHeight
'GetRowByIndex will scroll the view to bring the DataGridRow of interest into view, which throws off the counter. This adjusts for that'
myGrid.UpdateLayout()
If Not myScrollViewer.VerticalOffset = originalOffset Then myGrid.ScrollIntoView(myGrid.Items(CInt(myScrollViewer.ViewportHeight + originalOffset - 1)))
myGrid.UpdateLayout()
If myScrollViewer.VerticalOffset > rowIndex Then cursorPositionY += dgRow.ActualHeight
Next
End Sub
请注意,ScrollViewer.HorizontalOffset Property会返回设备无关像素中的值,因此我只是在循环遍历列之前将我的位置偏移一次。
请注意,如果CanContentScroll = True,ScrollViewer.VerticalOffset Property将返回项目数。所以在我的例子中,在每个循环中,我将计数器偏移一个项目的高度(DataGridRow)。如果CanContentScroll = False,则可以像列索引循环一样处理它。
我无法在Visual Basic中的.Net 4.0的DataGrid中找到行定义,但以下支持函数有助于获取DataGridRow:
Function GetRowByIndex(ByVal p_dataGrid As DataGrid,
ByVal p_index As Integer) As DataGridRow
Dim row As DataGridRow
row = CType(p_dataGrid.ItemContainerGenerator.ContainerFromIndex(p_index), DataGridRow)
If IsNothing(row) Then
'May be virtualized, bring into view and try again.'
p_dataGrid.UpdateLayout()
p_dataGrid.ScrollIntoView(p_dataGrid.Items(p_index))
row = CType(p_dataGrid.ItemContainerGenerator.ContainerFromIndex(p_index), DataGridRow)
End If
Return row
End Function
Visual Basic中的FindVisualChild函数:
Function FindVisualChild(Of childItem As DependencyObject)(ByVal p_obj As DependencyObject) As childItem
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(p_obj) - 1
Dim child As DependencyObject = VisualTreeHelper.GetChild(p_obj, i)
If child IsNot Nothing AndAlso TypeOf child Is childItem Then
Return CType(child, childItem)
Else
Dim childOfChild As childItem = FindVisualChild(Of childItem)(child)
If childOfChild IsNot Nothing Then
Return childOfChild
End If
End If
Next i
Return Nothing
End Function
答案 5 :(得分:1)
参考Grid组件
替换Grid.ColumnDefinitionsint GetColumn(double point)
{
int index = 0;
foreach(var column in Grid.ColumnDefinitions)
{
if(point > column.Offset && point < (column.Offset + column.ActualWidth))
return index;
index++;
}
return 0;
}
答案 6 :(得分:0)
我遇到了同样的问题,我也在使用FluidKit。我正在尝试构建一个表单设计器,您可以从工具箱拖动控件并将其拖放到网格单元格中。以下是我解决它的方法:
我在第一行创建了一个带有两个虚拟矩形的网格:
<Grid Name="myCanvas" ShowGridLines="True" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Rectangle DragDrop:DragDropManager.DropTargetAdvisor="{StaticResource targetAdvisor1}"
Grid.Row="0" Grid.Column="0" Width="200" Height="100" Fill="Blue" Stroke="Black" StrokeThickness="4" />
<Rectangle DragDrop:DragDropManager.DropTargetAdvisor="{StaticResource targetAdvisor2}"
Grid.Row="0" Grid.Column="1" Width="200" Height="100" Fill="Red" Stroke="Black" StrokeThickness="4" />
</Grid>
看起来如下:
请注意,我为每个矩形定义了DropTargetAdvisor,现在的逻辑如下:
OnDropCompleted
中的DropTargetAdvisor
方法将删除您放弃控件的矩形,并从Grid.Row
和Grid.Column
附加属性中获取其坐标。 这是我的DefaultDropTargetAdvisor.OnDropCompleted
方法:
public void OnDropCompleted(IDataObject obj, Point dropPoint)
{
UIElement dragged_control = ExtractElement(obj);
//Get the current Rectangle
FrameworkElement fe = TargetUI as FrameworkElement;
//Get parent Grid from this Rectangle
Grid g = fe.Parent as Grid;
//Get row and columns of this Rectangle
int row = Grid.GetRow(TargetUI);
int col = Grid.GetColumn(TargetUI);
//Remove Rectangle
g.Children.Remove(TargetUI);
//Set row and column for the dragged control
Grid.SetRow(dragged_control, row);
Grid.SetColumn(dragged_control, col);
//Add dragged control to Grid in that row/col
g.Children.Add(dragged_control);
}
答案 7 :(得分:0)
对于Telerik RadGridView,如果网格不包含UI元素,最好的方法是使用ChildrenOfType&lt;&gt;在带有IsMouseOver的Linq表达式中。
private void myGridView_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
MyCustomClass myClass = null;
var rows = this.myGridView.ChildrenOfType<GridViewRow>().Where(r => r.IsMouseOver == true);
foreach (var row in rows)
{
if (row is GridViewNewRow) break;
GridViewRow gvr = (GridViewRow)row;
myClass = (MyCustomClass)gvr.Item;
}
// do something with myClass here if we have found a row under mouse
}