Displaying/styling 关于新 Windows 10 CalendarView 的额外信息
Displaying/styling extra info on new Windows 10 CalendarView
我最近开始开发 Windows 10 应用程序,它需要在 CalendarView 上显示事件和额外信息。随着新API的发布,也引入了一个新的CalendarView组件,所以我决定试一试。这是一个不错的小部件,但定制一直是地狱。
我已经到了可以使用 ControlTemplate 显示自定义信息的地步,但是使用 VisualState 绑定事件和样式一直很困难。
这是我使用的封装在样式中的 ControlTemplate。
<Style x:Key="dayItemStyle" TargetType="CalendarViewDayItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CalendarViewDayItem">
<Grid x:Name="DayItemEventListRoot">
<ListView ItemsSource="{TemplateBinding DataContext}" Padding="20,0,0,0" x:Name="EventInfoList"
IsItemClickEnabled="True" cm:Message.Attach="[Event ItemClick] = [ListTapped]">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="EventInfoPanel" Orientation="Horizontal">
<TextBlock x:Name="EventTime" Text="{Binding Date, Converter={StaticResource StringFormatter}, ConverterParameter=\{0:HH:mm\} }"
Foreground="{Binding Foreground, RelativeSource={RelativeSource Self}}" >
</TextBlock>
<TextBlock x:Name="EventDesc" Text="{Binding Name}" Padding="5,0,0,0" Foreground="Black" >
</TextBlock>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Normal" />
<VisualState x:Name="Today" >
<VisualState.Setters>
<Setter Target="EventTime.Foreground" Value="White" />
<Setter Target="EventDesc.Foreground" Value="White" />
<Setter Target="EventTime.Text" Value="DASFASDDF" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
然后直接在 CalenderView 组件上设置样式。
<CalendarView Name="FlowCalendar" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalDayItemAlignment="Top" HorizontalDayItemAlignment="Left"
Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="5" Grid.RowSpan="4" CalendarViewDayItemChanging="FlowCalendar_CalendarViewDayItemChanging"
CalendarViewDayItemStyle="{StaticResource dayItemStyle}">
</CalendarView>
额外信息和状态由代码隐藏中的 CalenderViewDayItemChanging 事件控制。
private void FlowCalendar_CalendarViewDayItemChanging(CalendarView sender, CalendarViewDayItemChangingEventArgs args)
{
if(args.Item.Date.Date.Equals(DateTime.Now.Date))
{
VisualStateManager.GoToState(args.Item, "Today", false);
// Testing, gives NullPointerException
//TextBlock bla = (TextBlock) args.Item.FindName("EventTime");
//bla.Text = "SDADASFASDF";
}
if (args.Phase == 0)
{
var eventsByDate = ViewModel.Upcoming.FirstOrDefault(eg => eg.Key.Date == args.Item.Date.Date);
if (eventsByDate != null)
{
args.Item.DataContext = eventsByDate.ToList();
}
}
}
设置视觉状态没有任何作用(我检查过它是否被调用),将 VisualState(Group) 移到 ControlTemplate 之外只会给我一个找不到目标的错误。
我希望通过视觉状态来控制事件列表视图样式,目前这些视觉状态是自定义的,因为我不确定 CalendarViewDayItem 具有哪些内置状态。我对视觉状态很陌生,所以非常感谢任何指示。
谢谢。
您的代码将无法运行,因为 VisualStateManager.GoToState
将 Control
作为第一个参数;但是,您的 VisualStateManager
是在类型为 Panel
的 StackPanel
中定义的。
您需要实现自己的 VSM
,它需要一个 Panel
。查看 this post 的答案,了解如何执行此操作。
但是,这仍然不能解决您的问题。因为你需要以某种方式找到 StackPanel
s(注意'因为它在 ListView
内,可能有多个)然后调用 ExtendedVisualStateManager.GoToState
.
我建议您将此模板包装在 UserControl
中(这样您 可能 做 甚至不需要扩展 VSM
因为你可以使用 GoToStateAction
而不是 ) 并且有一个依赖项 属性 (IsToday
) 来控制状态。然后您可以使用 ElementName
绑定将 CalendarViewDayItem
级别的 属性 向下传递到 IsToday
以进行状态更改。
更新
关于需要使用 ExtendedVisualManager
来改变视觉状态,我实际上是错误的。由于它已经在 UserControl
中,您实际上可以直接调用 VisualStateManager.GoToState(this, "statename", flag);
。
但是,您定义依赖关系的方式 属性 是错误的。
用下面的代码替换后面的代码,它应该可以工作。
public bool IsToday
{
get { return (bool)GetValue(IsTodayProperty); }
set { SetValue(IsTodayProperty, value); }
}
public static readonly DependencyProperty IsTodayProperty =
DependencyProperty.Register("IsToday", typeof(bool), typeof(EventListTemplate), new PropertyMetadata(false, OnIsTodayChanged));
static void OnIsTodayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var template = (EventListTemplate)d;
if ((bool)e.NewValue)
{
var stateset = VisualStateManager.GoToState(template, "DayItemToday", false);
Debug.WriteLine("did it:", stateset);
}
}
我最近开始开发 Windows 10 应用程序,它需要在 CalendarView 上显示事件和额外信息。随着新API的发布,也引入了一个新的CalendarView组件,所以我决定试一试。这是一个不错的小部件,但定制一直是地狱。
我已经到了可以使用 ControlTemplate 显示自定义信息的地步,但是使用 VisualState 绑定事件和样式一直很困难。
这是我使用的封装在样式中的 ControlTemplate。
<Style x:Key="dayItemStyle" TargetType="CalendarViewDayItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CalendarViewDayItem">
<Grid x:Name="DayItemEventListRoot">
<ListView ItemsSource="{TemplateBinding DataContext}" Padding="20,0,0,0" x:Name="EventInfoList"
IsItemClickEnabled="True" cm:Message.Attach="[Event ItemClick] = [ListTapped]">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="EventInfoPanel" Orientation="Horizontal">
<TextBlock x:Name="EventTime" Text="{Binding Date, Converter={StaticResource StringFormatter}, ConverterParameter=\{0:HH:mm\} }"
Foreground="{Binding Foreground, RelativeSource={RelativeSource Self}}" >
</TextBlock>
<TextBlock x:Name="EventDesc" Text="{Binding Name}" Padding="5,0,0,0" Foreground="Black" >
</TextBlock>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Normal" />
<VisualState x:Name="Today" >
<VisualState.Setters>
<Setter Target="EventTime.Foreground" Value="White" />
<Setter Target="EventDesc.Foreground" Value="White" />
<Setter Target="EventTime.Text" Value="DASFASDDF" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
然后直接在 CalenderView 组件上设置样式。
<CalendarView Name="FlowCalendar" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" VerticalDayItemAlignment="Top" HorizontalDayItemAlignment="Left"
Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="5" Grid.RowSpan="4" CalendarViewDayItemChanging="FlowCalendar_CalendarViewDayItemChanging"
CalendarViewDayItemStyle="{StaticResource dayItemStyle}">
</CalendarView>
额外信息和状态由代码隐藏中的 CalenderViewDayItemChanging 事件控制。
private void FlowCalendar_CalendarViewDayItemChanging(CalendarView sender, CalendarViewDayItemChangingEventArgs args)
{
if(args.Item.Date.Date.Equals(DateTime.Now.Date))
{
VisualStateManager.GoToState(args.Item, "Today", false);
// Testing, gives NullPointerException
//TextBlock bla = (TextBlock) args.Item.FindName("EventTime");
//bla.Text = "SDADASFASDF";
}
if (args.Phase == 0)
{
var eventsByDate = ViewModel.Upcoming.FirstOrDefault(eg => eg.Key.Date == args.Item.Date.Date);
if (eventsByDate != null)
{
args.Item.DataContext = eventsByDate.ToList();
}
}
}
设置视觉状态没有任何作用(我检查过它是否被调用),将 VisualState(Group) 移到 ControlTemplate 之外只会给我一个找不到目标的错误。
我希望通过视觉状态来控制事件列表视图样式,目前这些视觉状态是自定义的,因为我不确定 CalendarViewDayItem 具有哪些内置状态。我对视觉状态很陌生,所以非常感谢任何指示。
谢谢。
您的代码将无法运行,因为 VisualStateManager.GoToState
将 Control
作为第一个参数;但是,您的 VisualStateManager
是在类型为 Panel
的 StackPanel
中定义的。
您需要实现自己的 VSM
,它需要一个 Panel
。查看 this post 的答案,了解如何执行此操作。
但是,这仍然不能解决您的问题。因为你需要以某种方式找到 StackPanel
s(注意'因为它在 ListView
内,可能有多个)然后调用 ExtendedVisualStateManager.GoToState
.
我建议您将此模板包装在 UserControl
中(这样您 可能 做 甚至不需要扩展 VSM
因为你可以使用 ) 并且有一个依赖项 属性 (GoToStateAction
而不是 IsToday
) 来控制状态。然后您可以使用 ElementName
绑定将 CalendarViewDayItem
级别的 属性 向下传递到 IsToday
以进行状态更改。
更新
关于需要使用 ExtendedVisualManager
来改变视觉状态,我实际上是错误的。由于它已经在 UserControl
中,您实际上可以直接调用 VisualStateManager.GoToState(this, "statename", flag);
。
但是,您定义依赖关系的方式 属性 是错误的。
用下面的代码替换后面的代码,它应该可以工作。
public bool IsToday
{
get { return (bool)GetValue(IsTodayProperty); }
set { SetValue(IsTodayProperty, value); }
}
public static readonly DependencyProperty IsTodayProperty =
DependencyProperty.Register("IsToday", typeof(bool), typeof(EventListTemplate), new PropertyMetadata(false, OnIsTodayChanged));
static void OnIsTodayChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var template = (EventListTemplate)d;
if ((bool)e.NewValue)
{
var stateset = VisualStateManager.GoToState(template, "DayItemToday", false);
Debug.WriteLine("did it:", stateset);
}
}