2011-09-19 163 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元素支持的行爲,無論你有多少數據網格行有,所以它可能是更有效的和可擴展的大型數據集。