WPF DataGrid: изменение размера столбцов - PullRequest
3 голосов
/ 19 сентября 2011

У меня есть System.Windows.Controls.DataGrid со свойством CanUserResizeColumns, назначенным для True.Теперь я могу настроить ширину столбцов с помощью щелчка левой кнопкой мыши между двумя заголовками столбцов.

Но я также хочу иметь возможность изменять ширину столбцов в любой строке таблицы данных, не тольков заголовках столбцов.Возможно ли это?

Ответы [ 3 ]

5 голосов
/ 19 сентября 2011

В вашей dataGrid вы можете использовать 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, вышеупомянутый обработчик событий должен быть включен в Attached Behavior

2 голосов
/ 20 октября 2012

Вслед за 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, я настроилBorderThickness объекта DataGridCell равен 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"},
                       };
        }
    }
}
0 голосов
/ 12 декабря 2016

Вот альтернативное решение, которое не загрязняет содержимое вашей сетки данных. Слой Canvas поверх DataGrid, и внутри этого Canvas есть Линия, которую можно перетаскивать влево и вправо. При перетаскивании обновляет желаемую ширину столбца.

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 для поддержки поведения независимо от количества строк сетки данных, поэтому оно может быть более эффективным и масштабируемым для больших наборов данных.

...