WPF:滚动 Itemcontrol 内容固定标题 [英] WPF: Scroll Itemcontrol Content Fixed Header

查看:21
本文介绍了WPF:滚动 Itemcontrol 内容固定标题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

是否可以使用 WPF 的 ItemsControl 执行类似操作:Demo

我正在尝试冻结 GroupedItems 而不是 GridView 列.

资源:

<CollectionViewSource x:Key="data" Source="{Binding}"><CollectionViewSource.GroupDescriptions><PropertyGroupDescription PropertyName="日期"/></CollectionViewSource.GroupDescriptions></CollectionViewSource></Window.Resources>

列表视图:

<ListView.View><网格视图><GridView.Columns><GridViewColumn Header="Col 1" DisplayMemberBinding="{Binding Col1}" Width="100"/><GridViewColumn Header="Col 2" DisplayMemberBinding="{Binding Col2}" Width="100"/><GridViewColumn Header="Col 3" DisplayMemberBinding="{Binding Col3}" Width="100"/></GridView.Columns></GridView></ListView.View><ListView.GroupStyle><GroupStyle><GroupStyle.ContainerStyle><Style TargetType="{x:Type GroupItem}"><Setter 属性="模板"><Setter.Value><ControlTemplate TargetType="{x:Type GroupItem}"><网格><Grid.RowDefinitions><RowDefinition Height="自动"/><RowDefinition Height="自动"/></Grid.RowDefinitions><网格 Grid.Row="0"><TextBlock Background="Beige" FontWeight="Bold" Text="{Binding Path=Name, StringFormat={}{0}}"/></网格><DockPanel Grid.Row="1"><ItemsPresenter Grid.Row="2"></ItemsPresenter></DockPanel></网格></控制模板></Setter.Value></Setter></风格></GroupStyle.ContainerStyle></GroupStyle></ListView.GroupStyle></ListView>

背后的代码:

public MainWindow(){初始化组件();列表<字符串>colList1 = new List() { "Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7" };列表<字符串>colList2 = new List() { "1", "2", "3", "4", "5", "6" };ObservableCollection<数据>dataCollection = new ObservableCollection();for (var a = 0; a <100; a++){随机 rnd = 新随机();int min = rnd.Next(5000);int rnd1 = rnd.Next(0, 6);int rnd2 = rnd.Next(0, 5);dataCollection.Add(新数据(){Date = DateTime.Now.AddMinutes(min).ToString("hh:MM tt"),Col1 = colList1[rnd2],Col2 = String.Format("Col2: {0}", "X"),Col3 = colList2[rnd2]});}this.DataContext = dataCollection;}公共类数据{公共字符串日期{获取;放;}公共字符串 Col1 { 获取;放;}公共字符串 Col2 { 获取;放;}公共字符串 Col3 { 获取;放;}}

解决方案

因为我刚刚遇到了类似的问题并且hack-ish"解决方案不适合我的需求,而且我通常不喜欢hack-ish"的东西生产环境,我为此开发了一个通用的解决方案,我想分享一下.附加的类具有以下主要功能:

  • MVVM 兼容
  • 无代码隐藏
  • 兼容ListView、GridView、ItemsControl,甚至静态xaml!- 应该使用 ScollViewer 处理任何事情......
  • 使用附加属性声明组项

xaml 用法(只是您内部的 ControlTemplate):

<网格><Grid.RowDefinitions><RowDefinition Height="自动"/><RowDefinition Height="自动"/></Grid.RowDefinitions><Grid Grid.Row="0" local:StickyScrollHeader.AttachToControl="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Grid}}"><TextBlock Background="Beige" FontWeight="Bold" Text="{Binding Path=Name, StringFormat={}{0}}"/><Grid.ColumnDefinitions><ColumnDefinition Width="*"/></Grid.ColumnDefinitions></网格><DockPanel Grid.Row="1"><ItemsPresenter Grid.Row="2"></ItemsPresenter></DockPanel></网格></控制模板>

类(放在任何地方,必要时添加 xaml 命名空间):

公共静态类 StickyScrollHeader{公共静态 FrameworkElement GetAttachToControl(FrameworkElement obj){返回(FrameworkElement)obj.GetValue(AttachToControlProperty);}公共静态无效 SetAttachToControl(FrameworkElement obj, FrameworkElement 值){obj.SetValue(AttachToControlProperty, value);}私有静态 ScrollViewer FindScrollViewer(FrameworkElement item){FrameworkElement treeItem = item;FrameworkElement directItem = item;while (treeItem != null){treeItem = VisualTreeHelper.GetParent(treeItem) as FrameworkElement;if (treeItem 是 ScrollViewer){将 treeItem 作为 ScrollViewer 返回;}else if (treeItem 是 ScrollContentPresenter){返回 (treeItem 作为 ScrollContentPresenter).ScrollOwner;}}while (directItem != null){directItem = directItem.Parent 作为 FrameworkElement;if (directItem 是 ScrollViewer){将 directItem 作为 ScrollViewer 返回;}else if (directItem 是 ScrollContentPresenter){return (directItem as ScrollContentPresenter).ScrollOwner;}}返回空;}私有静态 ScrollContentPresenter FindScrollContentPresenter(FrameworkElement sv){int childCount = VisualTreeHelper.GetChildrenCount(sv);for (int i = 0; i < childCount; i++){if (VisualTreeHelper.GetChild(sv, i) is FrameworkElement child && child is ScrollContentPresenter){将孩子作为 ScrollContentPresenter 返回;}}for (int i = 0; i < childCount; i++){if (FindScrollContentPresenter(VisualTreeHelper.GetChild(sv, i) as FrameworkElement) is FrameworkElement child && child is ScrollContentPresenter){将孩子作为 ScrollContentPresenter 返回;}}返回空;}public static readonly DependencyProperty AttachToControlProperty =DependencyProperty.RegisterAttached("AttachToControl", typeof(FrameworkElement), typeof(StickyScrollHeader), new PropertyMetadata(null, (s, e) =>{尝试{if (!(s is FrameworkElement targetControl)){ 返回;}Canvas.SetZIndex(targetControl, 999);滚动查看器 SV;FrameworkElement 父级;if (e.OldValue 是 FrameworkElement oldParentControl){ScrollViewer oldSv = FindScrollViewer(oldParentControl);父 = oldParentControl;oldSv.ScrollChanged -= Sv_ScrollChanged;}if (e.NewValue 是 FrameworkElement newParentControl){sv = FindScrollViewer(newParentControl);父 = 新父控件;sv.ScrollChanged += Sv_ScrollChanged;}void Sv_ScrollChanged(对象发送者,ScrollChangedEventArgs sce){如果(!parent.IsVisible){返回;}尝试{ScrollViewer isv = 发送者为 ScrollViewer;ScrollContentPresenter scp = FindScrollContentPresenter(isv);var relativeTransform = parent.TransformToAncestor(scp);Rect parentRenderRect = relativeTransform.TransformBounds(new Rect(new Point(0, 0), parent.RenderSize));Rect intersectingRect = Rect.Intersect(new Rect(new Point(0, 0), scp.RenderSize), parentRenderRect);if (intersectingRect != Rect.Empty){TranslateTransform targetTransform = new TranslateTransform();如果(parentRenderRect.Top <0){double tempTop = (parentRenderRect.Top * -1);if (tempTop + targetControl.RenderSize.Height < parent.RenderSize.Height){targetTransform.Y = tempTop;}else if (tempTop < parent.RenderSize.Height){targetTransform.Y = tempTop - (targetControl.RenderSize.Height - intersectingRect.Height);}}别的{targetTransform.Y = 0;}targetControl.RenderTransform = targetTransform;}}抓住 { }}}抓住 { }}));}

希望这也能帮助其他人遇到这个问题;)

Is it possible to do something like this with WPF's ItemsControl: Demo

I am trying to freeze the GroupedItems rather than the GridView Columns.

Resources:

<Window.Resources>
    <CollectionViewSource x:Key="data" Source="{Binding}">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="Date"/>
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
</Window.Resources>

ListView:

<ListView Grid.Column="0" ItemsSource="{Binding Source={StaticResource data}}">
    <ListView.View>
        <GridView>
            <GridView.Columns>
                <GridViewColumn Header="Col 1" DisplayMemberBinding="{Binding Col1}" Width="100"/>
                <GridViewColumn Header="Col 2" DisplayMemberBinding="{Binding Col2}" Width="100"/>
                <GridViewColumn Header="Col 3" DisplayMemberBinding="{Binding Col3}" Width="100"/>
            </GridView.Columns>
        </GridView>
    </ListView.View>
    <ListView.GroupStyle>
        <GroupStyle>
            <GroupStyle.ContainerStyle>
                <Style TargetType="{x:Type GroupItem}">
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type GroupItem}">
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="Auto"/>
                                        <RowDefinition Height="Auto"/>
                                    </Grid.RowDefinitions>
                                    <Grid Grid.Row="0">
                                        <TextBlock Background="Beige" FontWeight="Bold" Text="{Binding Path=Name, StringFormat={}{0}}"/>
                                    </Grid>
                                    <DockPanel Grid.Row="1">
                                        <ItemsPresenter Grid.Row="2"></ItemsPresenter>
                                    </DockPanel>
                                </Grid>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </GroupStyle.ContainerStyle>
        </GroupStyle>
    </ListView.GroupStyle>
</ListView>

Code behind:

public MainWindow()
{
    InitializeComponent();

    List<String> colList1 = new List<string>() { "Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7" };
    List<String> colList2 = new List<string>() { "1", "2", "3", "4", "5", "6" };

    ObservableCollection<Data> dataCollection = new ObservableCollection<Data>();

    for (var a = 0; a < 100; a++)
    {
        Random rnd = new Random();
        int min = rnd.Next(5000);
        int rnd1 = rnd.Next(0, 6);
        int rnd2 = rnd.Next(0, 5);

        dataCollection.Add(new Data()
        {
            Date = DateTime.Now.AddMinutes(min).ToString("hh:MM tt"),
            Col1 = colList1[rnd2],
            Col2 = String.Format("Col2: {0}", "X"),
            Col3 = colList2[rnd2]
        });
    }
    this.DataContext = dataCollection;
}

public class Data
{
    public string Date { get; set; }
    public string Col1 { get; set; }
    public string Col2 { get; set; }
    public string Col3 { get; set; }
}

解决方案

As i just ran into a similar issue and the 'hack-ish' solution did not fit my needs and i generally dont like 'hack-ish' stuff in production environments, i developed a generic solution to this which i'd like to share. The attached Class has following key-features:

  • MVVM compatible
  • no Code-Behind
  • compatible with ListView, GridView, ItemsControl, even static xaml! - should work with anything using a ScollViewer ...
  • Uses attached property to declare the group item

xaml usage (just your inner ControlTemplate):

<ControlTemplate TargetType="{x:Type GroupItem}">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition  Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0" local:StickyScrollHeader.AttachToControl="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Grid}}">
            <TextBlock Background="Beige" FontWeight="Bold" Text="{Binding Path=Name, StringFormat={}{0}}"/>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
        </Grid>
        <DockPanel Grid.Row="1">
            <ItemsPresenter Grid.Row="2"></ItemsPresenter>
        </DockPanel>
    </Grid>
</ControlTemplate>

The Class (put anywhere, add xaml-namespace if necessary):

public static class StickyScrollHeader
{
    public static FrameworkElement GetAttachToControl(FrameworkElement obj)
    {
        return (FrameworkElement)obj.GetValue(AttachToControlProperty);
    }

    public static void SetAttachToControl(FrameworkElement obj, FrameworkElement value)
    {
        obj.SetValue(AttachToControlProperty, value);
    }

    private static ScrollViewer FindScrollViewer(FrameworkElement item)
    {
        FrameworkElement treeItem = item;
        FrameworkElement directItem = item;

        while (treeItem != null)
        {
            treeItem = VisualTreeHelper.GetParent(treeItem) as FrameworkElement;
            if (treeItem is ScrollViewer)
            {
                return treeItem as ScrollViewer;
            }
            else if (treeItem is ScrollContentPresenter)
            {
                return (treeItem as ScrollContentPresenter).ScrollOwner;
            }
        }

        while (directItem != null)
        {
            directItem = directItem.Parent as FrameworkElement;

            if (directItem is ScrollViewer)
            {
                return directItem as ScrollViewer;
            }
            else if (directItem is ScrollContentPresenter)
            {
                return (directItem as ScrollContentPresenter).ScrollOwner;
            }
        }

        return null;
    }

    private static ScrollContentPresenter FindScrollContentPresenter(FrameworkElement sv)
    {
        int childCount = VisualTreeHelper.GetChildrenCount(sv);

        for (int i = 0; i < childCount; i++)
        {
            if (VisualTreeHelper.GetChild(sv, i) is FrameworkElement child && child is ScrollContentPresenter)
            {
                return child as ScrollContentPresenter;
            }
        }

        for (int i = 0; i < childCount; i++)
        {
            if (FindScrollContentPresenter(VisualTreeHelper.GetChild(sv, i) as FrameworkElement) is FrameworkElement child && child is ScrollContentPresenter)
            {
                return child as ScrollContentPresenter;
            }
        }

        return null;
    }

    public static readonly DependencyProperty AttachToControlProperty =
        DependencyProperty.RegisterAttached("AttachToControl", typeof(FrameworkElement), typeof(StickyScrollHeader), new PropertyMetadata(null, (s, e) =>
        {
            try
            {
                if (!(s is FrameworkElement targetControl))
                { return; }

                Canvas.SetZIndex(targetControl, 999);

                ScrollViewer sv;
                FrameworkElement parent;

                if (e.OldValue is FrameworkElement oldParentControl)
                {
                    ScrollViewer oldSv = FindScrollViewer(oldParentControl);
                    parent = oldParentControl;
                    oldSv.ScrollChanged -= Sv_ScrollChanged;
                }

                if (e.NewValue is FrameworkElement newParentControl)
                {
                    sv = FindScrollViewer(newParentControl);
                    parent = newParentControl;
                    sv.ScrollChanged += Sv_ScrollChanged;
                }

                void Sv_ScrollChanged(object sender, ScrollChangedEventArgs sce)
                {
                    if (!parent.IsVisible) { return; }

                    try
                    {

                        ScrollViewer isv = sender as ScrollViewer;
                        ScrollContentPresenter scp = FindScrollContentPresenter(isv);

                        var relativeTransform = parent.TransformToAncestor(scp);
                        Rect parentRenderRect = relativeTransform.TransformBounds(new Rect(new Point(0, 0), parent.RenderSize));
                        Rect intersectingRect = Rect.Intersect(new Rect(new Point(0, 0), scp.RenderSize), parentRenderRect);
                        if (intersectingRect != Rect.Empty)
                        {
                            TranslateTransform targetTransform = new TranslateTransform();

                            if (parentRenderRect.Top < 0)
                            {
                                double tempTop = (parentRenderRect.Top * -1);

                                if (tempTop + targetControl.RenderSize.Height < parent.RenderSize.Height)
                                {
                                    targetTransform.Y = tempTop;
                                }
                                else if (tempTop < parent.RenderSize.Height)
                                {
                                    targetTransform.Y = tempTop - (targetControl.RenderSize.Height - intersectingRect.Height);
                                }
                            }
                            else
                            {
                                targetTransform.Y = 0;
                            }
                            targetControl.RenderTransform = targetTransform;
                        }
                    }
                    catch { }
                }
            }
            catch { }
        }));
}

Hope this also helps others running into this issue ;)

这篇关于WPF:滚动 Itemcontrol 内容固定标题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆