我正在使用通用Windows平台构建Windows Phone 10应用程序。在我的应用程序中,我有一个标准的CalendarView,我希望在有事件的日期显示密度颜色。我们的想法是在加载页面后立即加载日历,发出API请求,并且在成功检索数据后,CalendarView会刷新它的UI以便调用CalendarViewDayItemChanging
事件。从那里我可以为有事件的单元格设置密度颜色。
除了一部分,我几乎所有工作都正常。当日历首次加载时,我将其最小/最大日期范围设置为当前月份,这样我们一次只能看到一个月。这会导致日历的UI按预期刷新。但是,在我的API请求完成后,如果我尝试再次将最小/最大日期范围设置为相同的日期,则日历不会刷新它的UI。因此,我无法强制CalendarView刷新它的UI。
我尝试过调用UpdateLayout,我尝试重置最小/最大日期范围,并且我尝试将日历的DataContext绑定到我的代码中的ObservableCollection我的数据更新时更新。这些都不起作用,我没有看到任何方法来更新UI。
我对UWP很陌生,所以不确定我做错了什么。我知道数据绑定的概念是UWP的一个重要组成部分,但我不确定如何将我的数据绑定到此CalendarView,以便在刷新数据时刷新。有什么建议吗?
下面是我现在的代码的快速摘录。
XAML
<CalendarView
Name="Calendar"
NumberOfWeeksInView="6"
CalendarViewDayItemChanging="CalendarView_DayItemChanging"
DataContext="{Binding CalendarDates}">
</CalendarView>
代码隐藏
namespace Pages
{
public sealed partial class CalendarPage : BasePage
{
#region Private Variables
private CalendarPageModel PageModel = new CalendarPageModel();
private ObservableCollection<DateTime> CalendarDates;
#endregion
#region Constructor
public CalendarPage()
{
this.InitializeComponent();
CalendarDates = new ObservableCollection<DateTime>();
}
#endregion
#region Events
private void Page_Loaded(object sender, RoutedEventArgs args)
{
SetCalendarDateRange(); //NOTE: This is done here so that my UI consistantly shows the correct dates on the screen
LoadData();
}
private void CalendarView_DayItemChanging(CalendarView sender, CalendarViewDayItemChangingEventArgs args)
{
if (!PageModel.DateHasEvent(args.Item.Date))
{
args.Item.SetDensityColors(null);
}
else
{
List<Color> colors = new List<Color>();
Color? color = Application.Current.Resources["CalendarHasEventDensityColor"] as Color?;
if (color != null)
{
colors.Add((Color)color);
}
args.Item.SetDensityColors(colors);
}
}
#endregion
#region Data
private void SetCalendarDateRange()
{
Calendar.MinDate = PageModel.StartDate;
Calendar.MaxDate = PageModel.EndDate;
}
private async void LoadData()
{
// get data
await PageModel.RefreshData(PageModel.StartDate, PageModel.EndDate);
// force calendar to update
//NOTE: This only works if the date range is actually different than what it's currently set to
SetCalendarDateRange();
//NOTE: I have tried to just manually add a date to my observable collection to see if it'll kick off the calendar refresh, but it doesn't
CalendarDates.add(DateTime.Now);
}
#endregion
}
}
答案 0 :(得分:1)
坏消息
不幸的是,CalendarView
控件是针对此方案设计的。由于它在显示大量天数时针对性能进行了优化,因此仅在加载单个日期时刷新UI。
...然而
好消息
可以修改控件以创建此行为,但需要一些工作。
基本原则是负责绘制“密度”颜色块并将它们绑定到可以通过绑定更新的内容。
作为此工作的一个示例,请将以下内容添加到页面
<Page.Resources>
<local:ColorBrushConverter x:Key="BrushConverter" />
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel>
<CalendarView Name="Calendar"
DisplayMode="Month"
CalendarViewDayItemChanging="CalendarView_DayItemChanging"
>
<CalendarView.CalendarViewDayItemStyle>
<Style TargetType="CalendarViewDayItem" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CalendarViewDayItem">
<Grid Opacity="0.5">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="0" Fill="{Binding FifthColor, Converter={StaticResource BrushConverter}}" />
<Rectangle Grid.Row="1" Fill="{Binding FourthColor, Converter={StaticResource BrushConverter}}" />
<Rectangle Grid.Row="2" Fill="{Binding ThirdColor, Converter={StaticResource BrushConverter}}" />
<Rectangle Grid.Row="3" Fill="{Binding SecondColor, Converter={StaticResource BrushConverter}}" />
<Rectangle Grid.Row="4" Fill="{Binding FirstColor, Converter={StaticResource BrushConverter}}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</CalendarView.CalendarViewDayItemStyle>
</CalendarView>
<Button Click="AddEventClicked">Add random event</Button>
</StackPanel>
</Grid>
随附代码:
public sealed partial class MainPage : Page
{
private MyViewModel ViewModel;
private DateTime today;
private DateTime minDate;
private DateTimeOffset maxDate;
public MainPage()
{
this.InitializeComponent();
// Keep these for reference
this.today = DateTime.Now.Date;
this.minDate = new DateTime(today.Year, today.Month, 1);
this.maxDate = minDate.AddMonths(1);
// Create our viewmodel
ViewModel = MyViewModel.Generate(minDate.Date, maxDate.Date);
Calendar.MinDate = minDate;
Calendar.MaxDate = maxDate;
// Add data for the next three days - will be shown when page loads
ViewModel.Dates[today.AddDays(1)].Add(Colors.Red);
ViewModel.Dates[today.AddDays(2)].Add(Colors.Purple);
ViewModel.Dates[today.AddDays(2)].Add(Colors.Blue);
ViewModel.Dates[today.AddDays(3)].Add(Colors.Green);
}
private void CalendarView_DayItemChanging(CalendarView sender, CalendarViewDayItemChangingEventArgs args)
{
// When the DayItem in the calendar is loaded
var itemDate = args?.Item?.Date.Date ?? DateTime.MinValue;
if (ViewModel.Dates.ContainsKey(itemDate))
{
// Set the datacontext for our custom control
// - Which does support 2way binding :)
args.Item.DataContext = ViewModel.Dates[itemDate];
}
}
private void AddEventClicked(object sender, RoutedEventArgs e)
{
var rand = new Random();
var randomColor = Color.FromArgb(
255,
(byte) rand.Next(0, 254),
(byte)rand.Next(0, 254),
(byte)rand.Next(0, 254));
var randomDay = rand.Next(1, 29);
var randDateInMonth = new DateTime(today.Year, today.Month, randomDay);
if (ViewModel.Dates.ContainsKey(randDateInMonth))
{
ViewModel.Dates[randDateInMonth].Add(randomColor);
}
}
}
public class MyViewModel
{
// The VM really just holds this dictionary
public Dictionary<DateTime, DensityColors> Dates { get; }
private MyViewModel()
{
this.Dates = new Dictionary<DateTime, DensityColors>();
}
// Static constructor to limit the dates and populate dictionary
public static MyViewModel Generate(DateTime minDate, DateTime maxDate)
{
var generated = new MyViewModel();
for (var i = 0; i < (maxDate - minDate).TotalDays; i++)
{
generated.Dates.Add(minDate.AddDays(i), new DensityColors());
}
return generated;
}
}
public class DensityColors : ObservableCollection<Color>, INotifyPropertyChanged
{
// Properties that expose items in underlying OC
public Color FirstColor => Items.Any() ? Items.First() : Colors.Transparent;
public Color SecondColor => Items.Count > 1 ? Items.Skip(1).First() : Colors.Transparent;
public Color ThirdColor => Items.Count > 2 ? Items.Skip(2).First() : Colors.Transparent;
public Color FourthColor => Items.Count > 3 ? Items.Skip(3).First() : Colors.Transparent;
public Color FifthColor => Items.Count > 4 ? Items.Skip(4).First() : Colors.Transparent;
protected override void InsertItem(int index, Color item)
{
base.InsertItem(index, item);
// Hacky forcing of updating UI for all properties
OnPropertyChanged(nameof(FirstColor));
OnPropertyChanged(nameof(SecondColor));
OnPropertyChanged(nameof(ThirdColor));
OnPropertyChanged(nameof(FourthColor));
OnPropertyChanged(nameof(FifthColor));
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ColorBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is Color)
{
return new SolidColorBrush((Color)value);
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
这仅限于每天5个条目(不像内置控件那样10个,不再被忽略),但应该让您了解如何实现您之后的内容或根据需要进行修改。