我有一个System.Windows.Controls.DataGrid,其属性CanUserResizeColumns分配给True。现在,我可以通过在两个列标题之间单击鼠标左键来调整列的宽度。
但我也希望能够在dataGrid的任何行中更改列的宽度,而不仅仅是在列标题中。有可能吗?
答案 0 :(得分:5)
在您的dataGrid中,您可以使用DataGridTemplate
列与GridSplitter
对齐来实现此目的。
<toolkit:DataGridTemplateColumn Header="Text" >
<toolkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Text}"/>
<GridSplitter Grid.Column="1" Width="3"
DragIncrement="1"
DragDelta="GridSplitter_DragDelta"
Tag="{Binding BindsDirectlyToSource=True,
RelativeSource={RelativeSource
AncestorType={x:Type toolkit:DataGridCell}}}"/>
</Grid>
</DataTemplate>
</toolkit:DataGridTemplateColumn.CellTemplate>
</toolkit:DataGridTemplateColumn>
然后在你的代码后面......做这个......
private void GridSplitter_DragDelta(
object sender,
System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
var gridSplitter = sender as GridSplitter;
if (gridSplitter != null)
{
((DataGridCell) gridSplitter.Tag).Column.Width
= ((DataGridCell) gridSplitter.Tag).Column.ActualWidth +
e.HorizontalChange;
}
}
这样,单个单元级别的GridSplitter可以调整其整个列的大小。
如果您使用MVVM,则应将上述事件处理程序置于附加行为
答案 1 :(得分:2)
继WPF之后 - 这是一个很好的答案,以下是如何在附加行为中实现相同的结果:
public static class SplitterOnGridCellBehaviour
{
public static readonly DependencyProperty ChangeGridCellSizeOnDragProperty =
DependencyProperty.RegisterAttached("ChangeGridCellSizeOnDrag", typeof (bool),
typeof (SplitterOnGridCellBehaviour),
new PropertyMetadata(false, OnChangeGridCellSizeOnDrag));
private static void OnChangeGridCellSizeOnDrag(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
GridSplitter splitter = dependencyObject as GridSplitter;
if(splitter == null)
{
throw new NotSupportedException("SplitterOnGridCellBehaviour can only be on a GridSplitter");
}
if((bool)args.NewValue)
{
splitter.DragDelta += SplitterOnDragDelta;
}
else
{
splitter.DragDelta -= SplitterOnDragDelta;
}
}
private static void SplitterOnDragDelta(object sender, DragDeltaEventArgs args)
{
GridSplitter splitter = (GridSplitter)sender;
var containerCell = splitter.FindParent<DataGridCell>();
containerCell.Column.Width = containerCell.Column.ActualWidth + args.HorizontalChange;
}
public static void SetChangeGridCellSizeOnDrag(UIElement element, bool value)
{
element.SetValue(ChangeGridCellSizeOnDragProperty, value);
}
public static bool GetChangeGridCellSizeOnDrag(UIElement element)
{
return (bool) element.GetValue(ChangeGridCellSizeOnDragProperty);
}
public static T FindParent<T>(this DependencyObject child)
where T : DependencyObject
{
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null) return null;
var parent = parentObject as T;
if (parent != null)
{
return parent;
}
return FindParent<T>(parentObject);
}
}
为了使所有网格分割器在DataGrid中显示为一个,我将DataGridCell的BorderThickness调整为0,否则所有网格分割器都显示为破折号(至少在Windows 8上)。
窗口的XAML如下所示:
<Window x:Class="DataGridWithSplitter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DataGridWithSplitter" Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="CellWithSplitterTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Column1}"/>
<GridSplitter Grid.Column="1" Width="3" Background="Black" local:SplitterOnGridCellBehaviour.ChangeGridCellSizeOnDrag="True" />
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding SampleData}" GridLinesVisibility="None" HeadersVisibility="None" AutoGenerateColumns="False">
<DataGrid.Resources>
<!-- Makes the GridSplitters Solid -->
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0" />
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="First Column" CellTemplate="{StaticResource CellWithSplitterTemplate}" />
<DataGridTextColumn Header="Other column" Binding="{Binding Column2}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
剩下的工作非常明显,但为了完整起见,Windows DataContext被设置为以下ViewModel代码的实例:
public class SampleData
{
public string Column1 { get; set; }
public string Column2 { get; set; }
}
public class MainWindowViewModel
{
public IEnumerable<SampleData> SampleData
{
get
{
return new List<SampleData>()
{
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
new SampleData() {Column1 = "Hello", Column2 = "World"},
};
}
}
}
答案 2 :(得分:0)
这是一种不会污染数据网格内容的替代解决方案。将Canvas层叠在DataGrid的顶部,并在该Canvas中有一条可以左右拖动的Line。拖动时,它会更新所需的列宽。
XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DataGrid x:Name="grid" Grid.Row="0" /> <!-- This is your data grid -->
<Canvas Grid.Row="0"> <!-- Canvas layerd over data grid -->
<Line StrokeThickness="4" Stroke="Transparent" Cursor="SizeWE"
X1="{Binding Columns[0].ActualWidth, ElementName=grid}"
X2="{Binding X1, RelativeSource={RelativeSource Self}}"
Y2="{Binding ActualHeight, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Canvas}}}"
MouseLeftButtonDown="OnSplitLineMouseLeftButtonDown"
MouseLeftButtonUp="OnSplitLineMouseLeftButtonUp"
MouseMove="OnSplitLineMouseMove"/>
</Canvas>
</Grid>
C#代码隐藏:
#region SplitBarHandling
bool splitBarDragging = false;
double splitBarMouseLastX = 0;
private void OnSplitLineMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
splitBarDragging = true;
splitBarMouseLastX = e.GetPosition(null).X;
((UIElement)sender).CaptureMouse();
}
private void OnSplitLineMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
splitBarDragging = false;
((UIElement)sender).ReleaseMouseCapture();
}
private void OnSplitLineMouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (splitBarDragging)
{
e.Handled = true;
double newX = e.GetPosition(null).X;
grid.Columns[0].Width = grid.Columns[0].ActualWidth + (newX - splitBarMouseLastX);
splitBarMouseLastX = newX;
}
}
#endregion
注意我选择使线条透明,以便最终用户不会真正看到它。这是因为我已经依赖数据网格本身来显示列之间的垂直网格线。 此外,您可以选择线条粗细到您发现的任何用户友好,而不会影响网格单元格的布局。我选择了4,因为即使数据网格将垂直网格线渲染为1像素宽,也可以轻松拾取。
示例代码来自我的自定义PropertyGrid代码库,它只有两列,因此是硬编码的列0.为了更加通用化,我将其转换为附加行为,支持所需的列数,或子类DataGrid本身。
与之前的解决方案相比,这个解决方案只添加了一些WPF元素来支持行为,无论您拥有多少数据网格行,因此它可能在大型数据集上更有效和可扩展。