10

WPF の ItemsControl: Demoでこのようなことを行うことは可能ですか?

GridView 列ではなく GroupedItems をフリーズしようとしています。

ここに画像の説明を入力

資力:

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

リストビュー:

<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>

コードビハインド:

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; }
}
4

3 に答える 3

5

私のソリューションでは、グループ ヘッダー スタイルを共有する TextBlock オーバーレイを使用しています。ポジショニングと正しい HitTesting はトリッキーな部分ですが、レイアウトやロジックの小さな変更で問題が発生することはないと確信しています。

ColumnHeader を非表示にするかどうかはわかりませんでしたが、これは簡単で、ここに示されている以外の調整は必要ありません。

ここに画像の説明を入力

コードビハインド:

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>
于 2013-03-10T11:04:50.647 に答える
1

このソリューションは素晴らしいものではなく、ハックっぽいですが、基本的にはあなたが望むことをします。リストビューヘッダーを非表示にし、リストビューの上にテキストブロックを配置し、テキスト値をリストボックスの最初の表示アイテムのグループアイテムに設定しました。ハッキーですが、私が思いついたのは最高です。

public partial class MainWindow : Window
{

    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; }
    }

    private void grid_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        if (grid.Items.Count > 0)
        {
            HitTestResult hitTest = VisualTreeHelper.HitTest(grid, new Point(5, 5));
            System.Windows.Controls.ListViewItem item = GetListViewItemFromEvent(null, hitTest.VisualHit) as System.Windows.Controls.ListViewItem;
            if (item != null)
                Head.Text = ((Data)item.Content).Date;
        }

    }

    System.Windows.Controls.ListViewItem GetListViewItemFromEvent(object sender, object originalSource)
    {
        DependencyObject depObj = originalSource as DependencyObject;
        if (depObj != null)
        {
            // go up the visual hierarchy until we find the list view item the click came from  
            // the click might have been on the grid or column headers so we need to cater for this  
            DependencyObject current = depObj;
            while (current != null && current != grid)
            {
                System.Windows.Controls.ListViewItem ListViewItem = current as System.Windows.Controls.ListViewItem;
                if (ListViewItem != null)
                {
                    return ListViewItem;
                }
                current = VisualTreeHelper.GetParent(current);
            }
        }

        return null;
    }

}

XAML:

<Window x:Class="header.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="auto" SizeToContent="Width">
<Window.Resources>
    <CollectionViewSource x:Key="data" Source="{Binding}">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="Date"/>
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>

    <Style x:Key="myHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
        <Setter Property="Visibility" Value="Collapsed" />
    </Style>
</Window.Resources>
<Grid>
    <StackPanel>
    <TextBlock Name="Head" Grid.Row="0"/>
            <ListView Name="grid" Grid.Column="0" Grid.Row="1" ItemsSource="{Binding Source={StaticResource data}}" Height="300" ScrollViewer.ScrollChanged="grid_ScrollChanged">
                <ListView.View>
                    <GridView ColumnHeaderContainerStyle="{StaticResource myHeaderStyle}">
                        <GridView.Columns>
                            <GridViewColumn DisplayMemberBinding="{Binding Col1}" Width="100"/>
                            <GridViewColumn DisplayMemberBinding="{Binding Col2}" Width="100"/>
                            <GridViewColumn 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>
    </StackPanel>
</Grid>

編集:ScrollChangedイベントを修正しました。

private void grid_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        if (grid.Items.Count > 0)
        {
            Point point = new Point(5, 5);
            foreach(Data lvItem in grid.Items)
            {
                HitTestResult hitTest = VisualTreeHelper.HitTest(grid, point);
                ListViewItem item = GetListViewItemFromEvent(null, hitTest.VisualHit) as System.Windows.Controls.ListViewItem;
                if (item != null)
                {
                    Data value = ((Data)item.Content);
                    Head.Text = ((Data)item.Content).Date;
                    break;
                }
                else
                {
                    point.X += 5;
                    point.Y += 5;
                }
            }
        }

    }
于 2013-03-08T21:51:55.953 に答える