【问题标题】:ListView separator based on date基于日期的 ListView 分隔符
【发布时间】:2018-02-06 12:17:37
【问题描述】:

我的 ListView 有很多行,上面有日期戳,我想做的是以某种方式在周之间添加一个非常清晰的分隔符,例如行之间的 4px 彩色线表示下周开始的位置(即:在最后周日的第一行,下周周一的第一行)。

既然似乎几乎不可能找到任何这样的例子,我开始认为这不是那么微不足道吗?

【问题讨论】:

  • IMO 您必须根据ListViewSelector 创建自己的控件。另一种方法是使用组,但(我认为)需要为组分隔符创建一些样式。
  • 你能用datagridview代替吗?它的Rows 有一个DividerHeight 属性(像素整数)。您可以轻松地遍历行并将该值设置为 4。
  • @LocEngineer 感谢您的建议,但该应用程序已经在 Listview 控件上投入过多,现在无法对其进行更改。
  • @MasoudMokhtari 不要使用 inline code 来突出显示随机术语。
  • @CodeCaster 好的。感谢您记住这一点。

标签: c# .net wpf listview


【解决方案1】:

您应该首先确定与上一个条目相比具有新一周的条目。一种方法是编写一个比较器,将先前条目的日期与当前条目的日期进行比较:

public class WeekComparer : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        if (values.Length != 2)
        {
            throw new Exception("Bad values");
        }
        var prev = values[0] as DateTime?;

        // first entry has no predecessor, so the predecessor is not a different week
        if (!prev.HasValue)
        {
            return true;
        }
        var dt1 = prev.Value;
        var dt2 = (DateTime)values[1];

        if (Math.Abs((dt1 - dt2).Days) > 7)
        {
            return false;
        }
        // assume monday to be the first day of week
        var dow1 = (dt1.DayOfWeek + 7 - DayOfWeek.Monday) % 7;
        var dow2 = (dt2.DayOfWeek + 7 - DayOfWeek.Monday) % 7;
        var result = (dow1 < dow2) == (dt1 < dt2);
        return result;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

请注意,此比较器可能不完整。在计算 Timespan.Days 进行比较之前,可能需要删除 Time 部分。

然后您可以在WeekComparer 的结果上使用ListViewItem.StyleTrigger。使用RelativeSource PreviousData获取上一个item值与当前item进行比较:

<Grid x:Name="grid1">
    <Grid.Resources>
        <local:WeekComparer x:Key="sameWeekComparer"/>
    </Grid.Resources>
    <ListView x:Name="lv1" ItemsSource="{Binding}">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Style.Triggers>
                    <DataTrigger Value="False">
                        <DataTrigger.Binding>
                            <MultiBinding Converter="{StaticResource sameWeekComparer}">
                                <Binding Path="MyDate" RelativeSource="{RelativeSource PreviousData}"/>
                                <Binding Path="MyDate"/>
                            </MultiBinding>
                        </DataTrigger.Binding>
                        <Setter Property="BorderBrush" Value="Red"/>
                        <Setter Property="BorderThickness" Value="0 4 0 0"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.View>
            <GridView x:Name="gv1">
                <GridViewColumn DisplayMemberBinding="{Binding Name}" Header="Name"/>
                <GridViewColumn DisplayMemberBinding="{Binding MyDate}" Header="MyDate"/>
            </GridView>
        </ListView.View>
    </ListView>
</Grid>

这将使用Red BorderBrush 和仅在项目上方绘制边框的BorderThickness 绘制日期与前几行日期不同的行。您可以在Trigger 中应用任何其他类型的样式更改。

将 Grid 视为某个窗口的内容。我用以下数据进行了测试:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        List<MyDateVM> items = new List<MyDateVM>();

        items.Add(new MyDateVM { Name = "Test 1", MyDate = new DateTime(2018, 12, 31) }); // first entry
        items.Add(new MyDateVM { Name = "Test 2", MyDate = new DateTime(2019, 1, 6) }); // same week as end of december 2018
        items.Add(new MyDateVM { Name = "Test 3", MyDate = new DateTime(2019, 1, 3) }); // changed order of date
        items.Add(new MyDateVM { Name = "Test 4", MyDate = new DateTime(2019, 1, 9) }); // new week
        items.Add(new MyDateVM { Name = "Test 5", MyDate = new DateTime(2019, 2, 10) }); // long gap, new week

        grid1.DataContext = items;
    }
}

MyDateVM 只是一个用于存储项目属性的普通数据类。

【讨论】:

  • 经过一些修改后效果很好……我做了“var result = (dow2
  • @AndreasAndersson 我没有检查所有细节,因此很可能需要以不同方式处理某些边缘情况。很高兴听到通用方法对您有用。
  • @AndreasAndersson 我认为您处理的问题与同一天的不同时间有关。只要您的日期值被排序(总是升序),您的解决方法就会很好,但如果它们的排序方式不同,那么解决方法就会崩溃。最好尽早开始计算纯日期值:var dt1 = prev.Value.Date; var dt2 = ((DateTime)values[1]).Date;
  • 我会摆弄它,看看它是如何工作的。目前,所有内容始终按升序排序,但正如您所指出的,这可能会改变,所以再次感谢您的输入!
【解决方案2】:

几个简单的方法来实现这一点:

您可以在ListView 中使用ListViewGroupCollection

例如:

MyListview.Group[0].Items.Add(TheItem);

可以在设计器视图中添加组(右键单击和编辑组等)。

或者,您也可以添加一个虚拟行作为分隔符并覆盖颜色和外观 (TheItem.BackgroundColor = xxx)。

【讨论】:

    【解决方案3】:

    首先,让每个对象返回它们所在的周。我用谷歌搜索了这个,所以可能需要修改。

    public property int WeekNumber { get { return CultureInfo.InvariantCulture.Calendar.GetWeekOfYear( SomeDate, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Monday); } }
    

    接下来为您的资源添加组样式。这控制了它的外观。我把我的放在 UserControl/Window 中:

    <DataTemplate x:Key="DateSeparator">
        <Border BorderThickness="0,1,0,0" BorderBrush="Black" VerticalAlignment="Center" />
    </DataTemplate>
    

    在您的 ListView 中引用该样式:

    <ListView ...>
        <ListView.GroupStyle>
            <GroupStyle HeaderTemplate="{StaticResource DateSeparator}" />
        </ListView.GroupStyle>
    </ListView>
    

    最后,在代码中,获取其默认集合并设置其分组属性:

    var view = CollectionViewSource.GetDefaultView( YourBoundList );
    view.GroupDescriptions.Add( new PropertyGroupDescription( "WeekNumber" ) );
    

    也可以通过 XAML 而不是代码来设置分组:

    <CollectionViewSource x:Key="GroupedDataSource" Source="{Binding YourBoundList}">
        <CollectionViewSource.GroupDescriptions>
            <PropertyGroupDescription PropertyName="WeekNumber" />
        </CollectionViewSource.GroupDescriptions>
    </CollectionViewSource>
    ...
    <ListView ItemsSource="{Binding Source={StaticResource GroupedDataSource}" ...></ListView>
    

    我更喜欢在代码中进行设置,因为这样我就可以在运行时根据自己的喜好操作过滤、排序和/或分组。

    【讨论】:

    • 谢谢,这种方法看起来也很有趣。我还没有尝试过,但我会在本周晚些时候尝试!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-22
    • 1970-01-01
    • 2011-04-28
    • 1970-01-01
    • 2019-05-08
    • 2015-10-07
    相关资源
    最近更新 更多