WPF:过滤ItemsControl和ContentControl中的项目

时间:2011-03-25 17:55:22

标签: wpf filter wpf-controls itemscontrol contentcontrol

我创建了一个表示日历月视图的UI用户控件。该控件由以7x6网格排列的42个边界组成(一周7天x每周显示6周)。

接下来,我创建了一个Appointment类。它有一个DateTime AppointmentDate属性,它应该确定我的控件中将出现约会的边界。

我希望能够为我的UI控件提供约会集合,然后控件应确定哪个边框将包含约会项目,哪些边界将保持为空。

实现这一目标的最佳方法是什么?我在考虑以下内容:向我的控件中的每个边框添加ItemsControl,然后将每个边框绑定到约会集合。然后我将创建并应用每个ItemControls的过滤器来显示或省略相关的约会。这是智能,编码,内存和性能明智吗?有没有更好的方法来实现这一目标?

如果我希望每个边框只能容纳一个约会怎么办(集合中没有约会具有相同的约会日期)?我应该用ItemsControl替换ContentControl吗?是否可以对ContentControls应用过滤,如果可以,如何?

感谢您帮助我。

3 个答案:

答案 0 :(得分:2)

我嘲笑了一种与你的建议不同的方法。这是简化的,我做了一两个假设,但让我们试一试。

不要使用网格和匹配天数进入网格,让我们使用WrapPanel,只需将子项放入其中,每个代表一天。

在App.xaml.cs中,您可以放置​​一些代码来创建Day对象。

public class Day
{
    public DateTime Date { get; set; }
    public List<Appointment> Appointments { get; set; }
}

public partial class App : Application
{
    protected override void OnActivated(EventArgs e)
    {
        base.OnActivated(e);

        var daysCollection = new List<Day>();
        for (int i = 1; i <= 30; i++)
        {
            daysCollection.Add(new Day
                {
                    // arbitrary sample data
                    Date = new DateTime(2011, 04, i),
                    Appointments =
                        new List<Appointment>
                            {
                                new Appointment
                                    {
                                        Date = new DateTime(2011, 04, i),
                                        Description = "Some descriptive text"
                                    }
                            }
                });
        }

        this.Properties.Add("DaysCollection", daysCollection );
    }
}

现在我们收集了一些日子。这部分样本的约会并不重要。

现在,我们创建一个简单的日历UserControl并将其绑定到CalendarViewModel。

<UserControl x:Class="DaysCalendarBinding.Views.Calendar"
             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" 
             Height="210" Width="210">
    <WrapPanel x:Name="wrapPanel" Orientation="Horizontal" 
               ItemHeight="30" ItemWidth="30" 
               Loaded="wrapPanel_Loaded">
    </WrapPanel>
</UserControl>

ViewModel

public class CalendarViewModel
{
    public CalendarViewModel()
    {

    }

    public CalendarViewModel(IEnumerable<Day> inputDays)
    {
        // determine first day of the month passed in
        var firstDate =
            (from day in inputDays 
             orderby day.Date.Day 
             select day.Date).First();
        var todayIs = firstDate.DayOfWeek;
        var valueOfToday = (int) todayIs;

        // create this many blank day children
        DaysInMonth = new List<Day>();
        for (int i = valueOfToday; i > 0; i--)
        {
            // the start of some cheeze. I know. It's a sample.
            DaysInMonth.Add(new Day { Date = new DateTime(1,1,1) });
        }

        // add the rest ofthe days in to the collection
        foreach(var day in inputDays)
        {
            DaysInMonth.Add(day);
        }
    }

    public List<Day> DaysInMonth { get; private set; }
}

使用加载wrapPanel时的事件处理程序

private void wrapPanel_Loaded(object sender, RoutedEventArgs e)
{
    foreach (var day in ((CalendarViewModel)DataContext).DaysInMonth)
    {
        wrapPanel.Children.Add(
                 new DayView { 
                        DataContext = new DayViewModel(day) });
    }
}

现在,我们创建我们正在创建并添加到WrapPanel的DayViewModel和DayView控件。

<UserControl x:Class="DaysCalendarBinding.Views.DayView"
             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="30" d:DesignWidth="30">
    <Border BorderBrush="Black" BorderThickness="1">
        <StackPanel Height="30" Width="30" Background="AliceBlue">
            <TextBlock FontSize="7"  Text="{Binding DayDate}"/>
        </StackPanel>
    </Border> </UserControl>

ViewModel

public class DayViewModel
{
    private Day innerDay;

    public DayViewModel() {}

    public DayViewModel(Day day)
    {
        innerDay = day;
    }

    public string DayDate
    {
        get
        {
            // I know this is a cheesy approach. It's a sample. :)
            if (innerDay.Date.Year != 1)
                // this only intended to demonstrate some content
                return innerDay.Date.DayOfWeek.ToString().Remove(3) +
                       "   " + innerDay.Date.Day;
            return string.Empty;
        }
    }
}

现在终于,我们的主窗口,我们添加一个日历控件,添加一个CalendarViewModel,希望当我们按F5时,它会显示给你。 :)

<Window x:Class="DaysCalendarBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:Views="clr-namespace:DaysCalendarBinding.Views" Title="Calendar Demo" Height="350" Width="525">
    <Grid>
        <Views:Calendar x:Name="calendarControl"></Views:Calendar>
    </Grid>
</Window>

MainWindow.xaml.cs中的代码隐藏

protected override void OnActivated(EventArgs e)
{
    base.OnActivated(e);
    calendarControl.DataContext = 
                          new CalendarViewModel((IEnumerable<Day>)Application
                              .Current
                              .Properties["DaysCollection"]);
}

我可能在我的解决方案中犯了一个错误或两个错误。但是,我希望它传达了这个想法。这最终看起来像我这样。

三月日历

March Calendar Screenshot

四月日历

April Calendar Screenshot

现在,将这一切放在一起以便它适合您。这只是演示了这项技术。提出有意义的控制不应该那么难。

干杯。

答案 1 :(得分:1)

为了就第一个问题提供建议,我会引导您前往CollectionViewSource并提及

  

当用户将WPF属性绑定到   自动收集数据,WPF   创建一个包装集合的视图,   并将属性绑定到视图,   不是原始的集合。 Source

这可以帮助您分离整个数据集和可见部分之间的关​​注点。

对于第二个问题,一旦选择了过滤逻辑,就可以更好地建模控件以使用它。由于我无法看到您的代码并且对您正在做的事情知之甚少,因此这是非常通用的。我建议将控件绑定到单个约会(因此它只显示一个约会,如您所要求的)。如果为null或为空,则在该日期不显示任何内容。这将允许您操纵数据(模型)而不是控件(视图),但仍然可以实现您期望的结果。

答案 2 :(得分:0)

我可能会对整个事情采取不同的方法。我会为Days创建一个Collection,每天都会有Date,Week,DayOfWeek的整数属性和一个约会集合。

然后我会创建一个绑定到此Collection的ItemsControl。

我将ItemsControl.PanelTemplate放入一个包含7列和6行的Grid,并将ItemsControl.ItemTemplate放入包含Date的Border和一个ListBox或ItemsControl来保存约会。我将设置ItemsControl.ItemStyle,以便Grid.Row和Grid.Column分别绑定到Week和DayOfWeek。