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

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

问题描述

是否可以使用WPF的ItemsControl来完成这样的工作:描述的内容。





后面的代码:

  using System; 
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Media;

namespace WpfApplication1
{
public partial class FreezingGroupHeader:UserControl
{
private double _listviewHeaderHeight;
private double _listviewSideMargin;

public FreezingGroupHeader()
{
InitializeComponent();

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

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

随机rnd = new Random(); (var a = 0; a< 100; a ++)
{
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;

this.Loaded + = OnLoaded;


private void OnLoaded(object sender,RoutedEventArgs e)
{
//定位冻结头文件
GetListViewMargins(this.listview1);

厚度边距= this.frozenGroupHeader.Margin;
margin.Top = _listviewHeaderHeight;
margin.Right = SystemParameters.VerticalScrollBarWidth + _listviewSideMargin;
margin.Left = _listviewSideMargin;

this.frozenGroupHeader.Margin = margin;

UpdateFrozenGroupHeader();


private void listview1_ScrollChanged(object sender,ScrollChangedEventArgs e)
{
UpdateFrozenGroupHeader();
}

///< summary>
///设置冻结标题的文本和可见性
///< / summary>
private void UpdateFrozenGroupHeader()
{
if(listview1.HasItems)
{
// frozenGroupHeader
GroupItem group = GetFirstVisibleGroupItem(this.listview1 );
if(group!= null)
{
object data = group.Content;
this.frozenGroupHeader.Text = data.GetType()。GetProperty(Name)。GetValue(data,null)as string; //轻微破解
}
this.frozenGroupHeader.Visibility = Visibility.Visible;
}
else
this.frozenGroupHeader.Visibility = Visibility.Collapsed;
}

///< summary>
///设置将用于定位冻结标题的值
///< / summary>
private void GetListViewMargins(ListView listview)
{
if(listview.HasItems)
{
object o = listview.Items [0];
ListViewItem firstItem =(ListViewItem)listview.ItemContainerGenerator.ContainerFromItem(o);
if(firstItem!= null)
{
GroupItem group = FindUpVisualTree< GroupItem>(firstItem);
Point p = group.TranslatePoint(new Point(0,0),listview);
_listviewHeaderHeight = p.Y; //列标题的高度
_listviewSideMargin = p.X; // listview borders
}
}
}

///< summary>
///获取listview中第一个可见的GroupItem
///< / summary>
Private GroupItem GetFirstVisibleGroupItem(ListView listview)
{
HitTestResult hitTest = VisualTreeHelper.HitTest(listview,new Point(5,_listviewHeaderHeight + 5));
GroupItem group = FindUpVisualTree< GroupItem>(hitTest.VisualHit);
返回组;
}


///< summary>
///在可视化树上向上查找类型T的对象,从初始对象
/// //开始// http://www.codeproject.com/Tips/75816/Walk-up-the- Visual-Tree
///< / summary>
private static T FindUpVisualTree< T>(DependencyObject initial)其中T:DependencyObject
{
DependencyObject current = initial; (current!= null&& current.GetType()!= typeof(T))
{
current = VisualTreeHelper.GetParent(current);


}
返回当前值T;
}

public class Data
{
public string Date {get;组; }
公共字符串Col1 {get;组; }
公共字符串Col2 {get;组; }
公共字符串Col3 {get;组; }
}
}
}

Xaml:

 < UserControl x:Class =WpfApplication1.FreezingGroupHeader
xmlns =http://schemas.microsoft.com/
xmlns:x =http://schemas.microsoft.com/winfx/2006/xaml
xmlns:mc =http://schemas.openxmlformats。 org / markup-compatibility / 2006
xmlns:d =http://schemas.microsoft.com/expression/blend/2008
mc:Ignorable =d
d:DesignHeight =300d:DesignWidth =300
>

< UserControl.Resources>
< CollectionViewSource x:Key =dataSource ={Binding}>
< CollectionViewSource.GroupDescriptions>
< PropertyGroupDescription PropertyName =Date/>
< / CollectionViewSource>

< Style x:Key =GroupHeaderStyle1TargetType ={x:Type TextBlock}>
< Setter Property =BackgroundValue =Beige/>
< Setter Property =ForegroundValue =Black/>
< Setter Property =FontWeightValue =Bold/>
< / style>
< /UserControl.Resources>

<网格>
< ListView x:Name =listview1Grid.Column =0ItemsSource ={Binding Source = {StaticResource data}}ScrollViewer.ScrollChanged =listview1_ScrollChanged>
< ListView.View>
< GridView>
< GridView.Columns>
< GridViewColumn Header =Col 1DisplayMemberBinding ={Binding Col1}Width =100/>
< GridViewColumn Header =Col 2DisplayMemberBinding ={Binding Col2}Width =100/>
< GridViewColumn Header =Col 3DisplayMemberBinding ={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.RowDefinitions>
< RowDefinition Height =Auto/>
< RowDefinition Height =Auto/>
< /Grid.RowDefinitions>
< Grid Grid.Row =0>
< Grid.ColumnDefinitions>
< ColumnDefinition Width =*/>
< /Grid.ColumnDefinitions>
< TextBlock Style ={StaticResource GroupHeaderStyle1}Text ={Binding Name,StringFormat = {} {0}}/>
< / Grid>
< DockPanel Grid.Row =1>
< ItemsPresenter Grid.Row =2>< / ItemsPresenter>
< / DockPanel>
< / Grid>
< / ControlTemplate>
< / Setter>
< / style>
< / GroupStyle>
< /ListView.GroupStyle>
< / ListView>
< TextBlock x:Name =frozenGroupHeaderStyle ={StaticResource GroupHeaderStyle1}VerticalAlignment =Top/>
< / Grid>
< / UserControl>


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">
                                        <Grid.ColumnDefinitions>
                                            <ColumnDefinition Width="*"/>
                                        </Grid.ColumnDefinitions>
                                        <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; }
          }

解决方案

My solution uses a TextBlock overlay that shares the group header style. The positioning and correct HitTesting is the tricky part, but I'm quite confident this does not break for small changes in layout or logic.

I was not sure if you want to hide the ColumnHeader or not, but this is easy and doesn't need any other adjustments than what is depicted here.

Code behind:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace WpfApplication1
{
    public partial class FreezingGroupHeader : UserControl
    {
        private double _listviewHeaderHeight;
        private double _listviewSideMargin;

        public FreezingGroupHeader()
        {
            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>();

            Random rnd = new Random();

            for (var a = 0; a < 100; a++)
            {
                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;

            this.Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            // Position frozen header
            GetListViewMargins(this.listview1);

            Thickness margin = this.frozenGroupHeader.Margin;
            margin.Top = _listviewHeaderHeight;
            margin.Right = SystemParameters.VerticalScrollBarWidth + _listviewSideMargin;
            margin.Left = _listviewSideMargin;

            this.frozenGroupHeader.Margin = margin;

            UpdateFrozenGroupHeader();
        }

        private void listview1_ScrollChanged(object sender, ScrollChangedEventArgs e)
        {
            UpdateFrozenGroupHeader();
        }

        /// <summary>
        /// Sets text and visibility of frozen header
        /// </summary>
        private void UpdateFrozenGroupHeader()
        {
            if (listview1.HasItems)
            {
                // Text of frozenGroupHeader
                GroupItem group = GetFirstVisibleGroupItem(this.listview1);
                if (group != null)
                {
                    object data = group.Content;
                    this.frozenGroupHeader.Text = data.GetType().GetProperty("Name").GetValue(data, null) as string;  // slight hack
                }
                this.frozenGroupHeader.Visibility = Visibility.Visible;
            }
            else
                this.frozenGroupHeader.Visibility = Visibility.Collapsed;
        }

        /// <summary>
        /// Sets values that will be used in the positioning of the frozen header
        /// </summary>
        private void GetListViewMargins(ListView listview)
        {
            if (listview.HasItems)
            {
                object o = listview.Items[0];
                ListViewItem firstItem = (ListViewItem)listview.ItemContainerGenerator.ContainerFromItem(o);
                if (firstItem != null)
                {
                    GroupItem group = FindUpVisualTree<GroupItem>(firstItem);
                    Point p = group.TranslatePoint(new Point(0, 0), listview);
                    _listviewHeaderHeight = p.Y; // height of columnheader
                    _listviewSideMargin = p.X; // listview borders
                }
            }
        }

        /// <summary>
        /// Gets the first visible GroupItem in the listview
        /// </summary>
        private GroupItem GetFirstVisibleGroupItem(ListView listview)
        {
            HitTestResult hitTest = VisualTreeHelper.HitTest(listview, new Point(5, _listviewHeaderHeight + 5));
            GroupItem group = FindUpVisualTree<GroupItem>(hitTest.VisualHit);
            return group;
        }


        /// <summary>
        /// walk up the visual tree to find object of type T, starting from initial object
        /// http://www.codeproject.com/Tips/75816/Walk-up-the-Visual-Tree
        /// </summary>
        private static T FindUpVisualTree<T>(DependencyObject initial) where T : DependencyObject
        {
            DependencyObject current = initial;

            while (current != null && current.GetType() != typeof(T))
            {
                current = VisualTreeHelper.GetParent(current);
            }
            return current as T;
        }

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

Xaml:

<UserControl x:Class="WpfApplication1.FreezingGroupHeader"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
           >

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

        <Style x:Key="GroupHeaderStyle1" TargetType="{x:Type TextBlock}">
            <Setter Property="Background" Value="Beige" />
            <Setter Property="Foreground" Value="Black" />
            <Setter Property="FontWeight" Value="Bold" />
        </Style>
    </UserControl.Resources>

    <Grid>
        <ListView x:Name="listview1" Grid.Column="0" ItemsSource="{Binding Source={StaticResource data}}" ScrollViewer.ScrollChanged="listview1_ScrollChanged" >
            <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">
                                                <Grid.ColumnDefinitions>
                                                    <ColumnDefinition Width="*"/>
                                                </Grid.ColumnDefinitions>
                                                <TextBlock Style="{StaticResource GroupHeaderStyle1}" Text="{Binding 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>
        <TextBlock x:Name="frozenGroupHeader" Style="{StaticResource GroupHeaderStyle1}" VerticalAlignment="Top"/>
    </Grid>
</UserControl>

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

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