基于日期的ListView分隔符

时间:2018-02-06 12:17:38

标签: c# .net wpf listview

我的ListView上有很多行,上面有日期戳,我想做的就是在几周之间添加一个非常清晰的分隔符,例如行之间的4px彩色线表示下周开始的地方(IE:最后一次)周日排,下周一排第一排)。

由于似乎几乎不可能找到任何这方面的例子,我开始认为这不是那么微不足道的事情?

3 个答案:

答案 0 :(得分:0)

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

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

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

然后,您可以ListViewItem.Style使用Trigger WeekComparer的结果RelativeSource PreviousData。使用<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 BorderThicknessTrigger仅在项目上方绘制边框。您可以在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; } }

中应用任何其他类型的样式更改

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

MyDateVM

<li> <label class="switchFilterOn"> <input class="firstOnOfInput" type="checkbox" checked> <span class="sliderOnOrOf round"></span> </label> <span class="onOfSpan">SHOW ONLY PROJECT MEMBERS:</span> </li> <li> <label class="switchFilterOn"> <input class="firstOnOfInput" type="checkbox"> <span class="sliderOnOrOf round"></span> </label> <span class="onOfSpan">SHOW INACTIVE EMPLOYEES:</span> </li> <li> <div class="input-group"> <select required="" class="filterSelectOptions" name="filterByProfession" id="filterByProfession"> <option>PROFESSION</option> </select> </div> </li> <li> <div class="input-group"> <select required="" class="filterSelectOptions" name="filterByType" id="filterByType"> <option>TYPE</option> <option value="1">INTERNAL</option> <option value="2">EXTERNAL</option> </select> </div> </li> <li> <div class="input-group"> <input type="number" min="0" required class="filterSelectOptions" id="filterByAge"> <small class="ageLabelFilter" for="filterByAge">AGE</small> </div> </li> <li> <div class="input-group"> <select required="" class="filterSelectOptions" name="filterByGender" id="filterByGender"> <option>GENDER</option> <option value="M">MALE</option> <option value="F">FEMALE</option> </select> </div> </li> 只是一个存储项属性的普通数据类。

答案 1 :(得分:0)

实现这一目标的几种简单方法:

您可以使用ListViewGroupCollection中的ListView

e.g:

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

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

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

答案 2 :(得分:0)

首先,让每个对象返回它们落入哪个星期。我用Google搜索了,所以可能需要更改。

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>

我更喜欢在代码中设置它,因为我可以在运行时操作过滤,排序和/或分组。