WPF DataGrid冻结右侧列

我们知道,wpf冻结左侧列是很简单的,微软提供给我们了FrozenColumnCount属性,它的值代表你要从左往右冻结几列,如下图所示:在这里插入图片描述
但是 这往往不是我们想要的,我们习惯于冻结右边的列,来实现固定操作列,删除,修改等等,遗憾的是,官方并没有提供支持,于是我们只能通过修改样式来实现.
先上效果:
在这里插入图片描述
我们实现了和网页一样丝滑的右侧固定列,并且使用起来及为方便,接下来我们看看代码是如何实现的.

.xaml

<controls:DataGrid  RightFrozenCount="2" FrozenColumnCount="1"  x:Name="listView" Margin="0 10 0 0" LoadingRow="listView_LoadingRow" Style="{StaticResource DesignDataGrid2}">
                        <DataGrid.Columns>
                            <DataGridTextColumn Header="很长很长的标题" Binding="{Binding Index1}"/>
                            <DataGridTextColumn Header="第3"  Binding="{Binding Index2}"/>
                            <DataGridTextColumn Header="第四"  Binding="{Binding Index3}"/>
                            <DataGridTextColumn Header="第五"  Binding="{Binding Index4}"/>
                            <DataGridTextColumn Header="第六"  Binding="{Binding Index2}"/>
                            <DataGridTextColumn Header="第七"  Binding="{Binding Index3}"/>
                            <DataGridTextColumn Header="第八"  Binding="{Binding Index6}"/>
                            <DataGridTextColumn Header="第九"  Binding="{Binding Index6}"/>
                            <DataGridTextColumn Header="第十"  Binding="{Binding Index1}"/>
                            <DataGridTextColumn Header="第十一"  Binding="{Binding Index6}"/>
                            <DataGridTextColumn Header="第十二"  Binding="{Binding Index1}"/>
                            <DataGridTemplateColumn Header="操作">
                                <DataGridTemplateColumn.CellTemplate>
                                    <DataTemplate>
                                        <StackPanel Orientation="Horizontal" Margin="6 0" VerticalAlignment="Center" HorizontalAlignment="Center">
                                            <Button Content="修改" Margin="0" FontSize="15" Padding="0" BorderThickness="0" Background="Transparent"/>
                                            <Button Content="删除" Padding="0" FontSize="15" Margin="20 0 0 0" BorderThickness="0" Background="Transparent"/>
                                        </StackPanel>
                                    </DataTemplate>
                                </DataGridTemplateColumn.CellTemplate>
                            </DataGridTemplateColumn>
                        </DataGrid.Columns>
                    </controls:DataGrid>

我们通过自定义的依赖属性RightFrozenCount来冻结右侧的列

资源样式

        <Style x:Key="DesignDataGridRow2" TargetType="{x:Type DataGridRow}">
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="SnapsToDevicePixels" Value="true" />
            <Setter Property="Foreground" Value="#332E2E"/>
            <Setter Property="Validation.ErrorTemplate" Value="{x:Null}" />

            <Setter Property="ValidationErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <TextBlock
                        Margin="2,0,0,0"
                        VerticalAlignment="Center"
                        Foreground="#d50000"
                        Text="!" />
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type DataGridRow}">
                        <Border
                        x:Name="DGR_Border"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"                    
                        SnapsToDevicePixels="True" >
                            <SelectiveScrollingGrid>
                                <SelectiveScrollingGrid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="*" />
                                </SelectiveScrollingGrid.ColumnDefinitions>
                                <SelectiveScrollingGrid.RowDefinitions>
                                    <RowDefinition Height="*" />
                                    <RowDefinition Height="Auto" />
                                    <RowDefinition Height="Auto" />
                                </SelectiveScrollingGrid.RowDefinitions>
                                <DataGridCellsPresenter
                                Grid.Column="1"
                                ItemsPanel="{TemplateBinding ItemsPanel}"
                                SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                                <DataGridDetailsPresenter
                                Grid.Row="1"
                                Grid.Column="1"
                                SelectiveScrollingGrid.SelectiveScrollingOrientation="{Binding AreRowDetailsFrozen, ConverterParameter={x:Static SelectiveScrollingOrientation.Vertical}, Converter={x:Static DataGrid.RowDetailsScrollingConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
                                Visibility="{TemplateBinding DetailsVisibility}" />
                                <DataGridRowHeader
                                Grid.Row="0"
                                Grid.RowSpan="2"
                                Grid.Column="0"
                                SelectiveScrollingGrid.SelectiveScrollingOrientation="Vertical"
                                Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Row}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />

                            </SelectiveScrollingGrid>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>

                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="#b8dcff" />
                    <Setter Property="assist2:DataGridAssist.MouseOverItem" Value="{Binding RelativeSource={RelativeSource  Mode=FindAncestor,AncestorType={x:Type controls:DataGrid}}}"/>
                </Trigger>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="Background" Value="#b8dcff" />
                </Trigger>
                <!--<Trigger Property="IsNewItem" Value="True">
                    <Setter Property="Margin" Value="{Binding NewItemMargin, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
                </Trigger>-->
            </Style.Triggers>
        </Style>
        <Style x:Key="DesignDataGrid2" TargetType="{x:Type controls:DataGrid}">
            <Setter Property="IsReadOnly" Value="True"/>
            <Setter Property="Background" Value="White" />
            <Setter Property="Foreground" Value="#DD000000" />
            <Setter Property="BorderBrush" Value="#1F000000" />
            <Setter Property="BorderThickness" Value="1" />
            <Setter Property="AutoGenerateColumns" Value="False" />
            <Setter Property="CanUserAddRows" Value="False" />
            <Setter Property="FontSize" Value="18" />
            <Setter Property="GridLinesVisibility" Value="Horizontal" />
            <Setter Property="HorizontalGridLinesBrush">
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource RemoveAlphaBrushConverter}">
                        <Binding Path="BorderBrush" RelativeSource="{RelativeSource Self}" />
                        <Binding Path="Background" RelativeSource="{RelativeSource Self}" />
                    </MultiBinding>
                </Setter.Value>
            </Setter>
            <Setter Property="VerticalGridLinesBrush" Value="{Binding HorizontalGridLinesBrush, RelativeSource={RelativeSource Self}}" />
            <Setter Property="RowDetailsVisibilityMode" Value="VisibleWhenSelected" />
            <Setter Property="HeadersVisibility" Value="Column" />
            <Setter Property="ScrollViewer.CanContentScroll" Value="true" />
            <Setter Property="ScrollViewer.PanningMode" Value="Both" />
            <Setter Property="Stylus.IsFlicksEnabled" Value="False" />
            <Setter Property="RowStyle" Value="{StaticResource DesignDataGridRow2}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type controls:DataGrid}">
                        <Border
                        Padding="{TemplateBinding Padding}"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        CornerRadius="{TemplateBinding assist:DataGridAssist.CornerRadius}"
                        SnapsToDevicePixels="True">
                            <ScrollViewer x:Name="DG_ScrollViewer" Focusable="false">
                                <ScrollViewer.Template>
                                    <ControlTemplate TargetType="{x:Type ScrollViewer}">
                                        <Grid>
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="Auto" />
                                                <ColumnDefinition Width="*" />
                                                <ColumnDefinition Width="Auto" />
                                                <ColumnDefinition Width="Auto" />
                                            </Grid.ColumnDefinitions>
                                            <Grid.RowDefinitions>
                                                <RowDefinition Height="Auto" />
                                                <RowDefinition Height="*" />
                                                <RowDefinition Height="Auto" />
                                            </Grid.RowDefinitions>
                      
                                            <Border BorderThickness="0 0 0 0" Grid.Column="2" Grid.RowSpan="2" BorderBrush="#1F000000" >
                                                <ContentControl Content="{Binding RightFrozenDataGrid,RelativeSource={RelativeSource AncestorType={x:Type controls:DataGrid}}}"/>
                                            </Border>
                                           
                                            <Border
                                            Grid.Row="0"
                                            Grid.Column="1"
                                            Visibility="{Binding HeadersVisibility, ConverterParameter={x:Static DataGridHeadersVisibility.Column}, Converter={x:Static DataGrid.HeadersVisibilityConverter}, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}">
                                                <DataGridColumnHeadersPresenter x:Name="PART_ColumnHeadersPresenter" />
                                            </Border>
                                            <ScrollContentPresenter
                                            x:Name="PART_ScrollContentPresenter"
                                            Grid.Row="1"
                                            Grid.Column="0"
                                            Grid.ColumnSpan="2"
                                            CanContentScroll="{TemplateBinding CanContentScroll}" />
                                            <ScrollBar
                                            x:Name="PART_VerticalScrollBar"
                                            Grid.Row="1"
                                            Style="{StaticResource ScrollBarStyle}"
                                            Grid.Column="3"
                                            Maximum="{TemplateBinding ScrollableHeight}"
                                            Orientation="Vertical"
                                            ViewportSize="{TemplateBinding ViewportHeight}"
                                            Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"
                                            Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />

                                            <Grid Grid.Row="3" Grid.Column="1">
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="{Binding NonFrozenColumnsViewportHorizontalOffset, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
                                                    <ColumnDefinition Width="*" />
                                                </Grid.ColumnDefinitions>
                                                <ScrollBar
                                                x:Name="PART_HorizontalScrollBar"
                                                Grid.Column="1"
                                                Maximum="{TemplateBinding ScrollableWidth}"
                                                Orientation="Horizontal"
                                                Style="{StaticResource ScrollBarStyle}"
                                                ViewportSize="{TemplateBinding ViewportWidth}"
                                                Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"
                                                Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" />
                                            </Grid>
                                        </Grid>
                                    </ControlTemplate>
                                </ScrollViewer.Template>
                                <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
                            </ScrollViewer>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <MultiTrigger>
                    <MultiTrigger.Conditions>
                        <Condition Property="IsGrouping" Value="true" />
                    </MultiTrigger.Conditions>
                    <Setter Property="ScrollViewer.CanContentScroll" Value="false" />
                </MultiTrigger>
            </Style.Triggers>
        </Style>

DataGrid.cs

    public class DataGrid: System.Windows.Controls.DataGrid
    {
        private ScrollViewer mianScrollViewer;
        private ItemsControl itemsControl;


        private DataGrid rightDataGrid;


        public object RightFrozenDataGrid
        {
            get { return (object)GetValue(RightFrozenDataGridProperty); }
            set { SetValue(RightFrozenDataGridProperty, value); }
        }

        // Using a DependencyProperty as the backing store for RightFrozenDataGrid.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RightFrozenDataGridProperty =
            DependencyProperty.Register("RightFrozenDataGrid", typeof(object), typeof(DataGrid), new PropertyMetadata(null));




        public object MouseOverItem
        {
            get { return (object)GetValue(MouseOverItemProperty); }
            set { SetValue(MouseOverItemProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MouseOverItem.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MouseOverItemProperty =
            DependencyProperty.Register("MouseOverItem", typeof(object), typeof(DataGrid), new PropertyMetadata(null, OnMouseOverItemChanged));

        private static void OnMouseOverItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d != null && d is DataGrid grid)
            {
                // 获取当前可见行的可视元素
                if(grid.rightDataGrid != null)
                {
                    foreach (var item in grid.Items)
                    {
                        var mainDataGridRow = grid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
                        if (mainDataGridRow != null) {
                            var row = grid.rightDataGrid?.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
                            if (row != null) {
                                row.Background = mainDataGridRow.Background;
                            }                           
                        }
                    }                  
                }
            }
        }

        public int RightFrozenCount
        {
            get { return (int)GetValue(RightFrozenCountProperty); }
            set { SetValue(RightFrozenCountProperty, value); }
        }

        // Using a DependencyProperty as the backing store for RightFrozenCount.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RightFrozenCountProperty =
            DependencyProperty.Register("RightFrozenCount", typeof(int), typeof(DataGrid), new PropertyMetadata(0, OnRightFrozenCountChanged));

        private static void OnRightFrozenCountChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
           
        }

        public DataGrid()
        {
            Loaded += DataGrid_Loaded;
            SelectionChanged += (e, s)=> BackgroundChanged();
            MouseLeave += (e, s) => BackgroundChanged();
            MouseEnter += (e, s) => BackgroundChanged();


        }

        private void DataGrid_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            if (Items != null&& Columns.Count>0&& RightFrozenCount>0) {
                rightDataGrid = new DataGrid();
               
                rightDataGrid.Background = Brushes.Transparent;
                rightDataGrid.BorderThickness = new Thickness(0);
                rightDataGrid.Style = Style;
                rightDataGrid.Columns.Clear();
                rightDataGrid.AutoGenerateColumns = false;
                

                for (int i = 0; i < RightFrozenCount; i++)
                {
                    var last = Columns[Columns.Count - 1];
                    Columns.Remove(last);
                    rightDataGrid.Columns.Insert(0,last);
                    
                }


                rightDataGrid.ItemsSource = ItemsSource;

                rightDataGrid.HeadersVisibility = DataGridHeadersVisibility.Column;
                rightDataGrid.IsReadOnly = true;
                rightDataGrid.CanUserAddRows = false; 
                rightDataGrid.SelectionChanged += (e, s) => {
                    SelectedItem = rightDataGrid.SelectedItem;
                    
                };
                RightFrozenDataGrid = rightDataGrid;

                
            }
            if (mianScrollViewer != null && rightDataGrid != null)
            {
               

                mianScrollViewer.ScrollChanged += (e, s) =>
                {
                    if (rightDataGrid.GetTemplateChild("DG_ScrollViewer") is ScrollViewer scroll)
                    {
                        scroll.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
                        scroll.ScrollToVerticalOffset(mianScrollViewer.VerticalOffset);
                    }
                };
            }
        }

        private void BackgroundChanged() {
            if (rightDataGrid != null)
            {
                foreach (var item in Items)
                {
                    var mainDataGridRow = ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
                    if (mainDataGridRow != null)
                    {
                        var row = rightDataGrid?.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
                        if (row != null)
                        {
                            row.Background = mainDataGridRow.Background;
                        }
                    }
                }

            }
        }

        public override void OnApplyTemplate()
        {

            base.OnApplyTemplate();
            mianScrollViewer = GetTemplateChild("DG_ScrollViewer") as ScrollViewer;
            if (mianScrollViewer != null) {

            }

        }
    }

附加属性DataGridAssist.cs

 public class DataGridAssist
    {
        public static readonly DependencyProperty MouseOverItemProperty
            = DependencyProperty.RegisterAttached("MouseOverItem", typeof(object), typeof(DataGridAssist),
                new PropertyMetadata(null, OnMouseOverItemChanged));

        private static void OnMouseOverItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if ( e.NewValue is Cloud.Test.Wpf.Controls.DataGrid dataGrid) {
                dataGrid.MouseOverItem = d;
            }
        }

        public static object GetMouseOverItem(DataGridRow element)
            => (object)element.GetValue(MouseOverItemProperty);
        public static void SetMouseOverItem(DataGridRow element, object value)
            => element.SetValue(MouseOverItemProperty, value);
    }

源码

整合中,和其他UI界面编辑完成一起发出来

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值