2011-09-19 233 views
3

我有一个System.Windows.Controls.DataGrid属性CanUserResizeColumns分配给True。现在我可以通过在2列标题之间使用鼠标左键单击来调整列的宽度。WPF DataGrid:调整列大小

但我也希望能够更改dataGrid的任何行中列的宽度,而不仅仅是在列标题中。可能吗?

+1

戴安娜,你真的应该接受答案,否则社区将不会有动力去帮助你。 – MichaelS

+0

不是。这太神秘了。 。:( –

+0

@AngelWPF,我想她的意思 - 我可以通过拖动留下了细胞\右边界(如列标题的行为)调整列宽 有趣的问题,还有就是在Excel中没有这样的行为以及。 – MichaelS

回答

4

在你的数据网格,你可以在你的代码中使用DataGridTemplate列alogn与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那么上面的事件处理程序应该放在一个附加行为

+0

+1从我 - 。它的工作原理良好的乐趣,我决定用你的代码中附加的行为和使用张贴解答。 – RichardOD

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的设置为以下视图模型代码的实例:

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"}, 
         }; 
     } 
    } 
} 
0

这是另一种解决方案,不会污染您的数据网格内容。在DataGrid顶部画一个Canvas,在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元素支持的行为,无论你有多少数据网格行有,所以它可能是更有效的和可扩展的大型数据集。