我创建了一个表示日历月视图的UI用户控件。该控件由以7x6网格排列的42个边界组成(一周7天x每周显示6周)。
接下来,我创建了一个Appointment
类。它有一个DateTime AppointmentDate
属性,它应该确定我的控件中将出现约会的边界。
我希望能够为我的UI控件提供约会集合,然后控件应确定哪个边框将包含约会项目,哪些边界将保持为空。
实现这一目标的最佳方法是什么?我在考虑以下内容:向我的控件中的每个边框添加ItemsControl
,然后将每个边框绑定到约会集合。然后我将创建并应用每个ItemControls的过滤器来显示或省略相关的约会。这是智能,编码,内存和性能明智吗?有没有更好的方法来实现这一目标?
如果我希望每个边框只能容纳一个约会怎么办(集合中没有约会具有相同的约会日期)?我应该用ItemsControl
替换ContentControl
吗?是否可以对ContentControls应用过滤,如果可以,如何?
感谢您帮助我。
答案 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"]);
}
我可能在我的解决方案中犯了一个错误或两个错误。但是,我希望它传达了这个想法。这最终看起来像我这样。
三月日历
四月日历
现在,将这一切放在一起以便它适合您。这只是演示了这项技术。提出有意义的控制不应该那么难。
干杯。
答案 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。