2
votes

I've recently started development on a Windows 10 app which requires the display of events and extra information on a CalendarView. With the release of the new API, a new CalendarView component was also introduced, so I decided to give it a try. It's a nice widget, but customization has been a hell.

I've gotten to the point where I can display custom information using a ControlTemplate, but binding events and styling with VisualState has been quite a struggle.

This is the ControlTemplate I'm using wrapped in a Style.

<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>

The style is then directly set on the CalenderView component.

<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>

Extra information and states are controlled by the CalenderViewDayItemChanging event in the code-behind.

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

Setting the visual state does nothing (I've checked if it's being called), moving the VisualState(Group) outside of the ControlTemplate just gives me an error that the targets could not be found.

I'm looking to control the event listview style through visual states, which for now are custom since I'm not sure what built-in states CalendarViewDayItem has. I'm quite new to visual states, so any pointers are much appreciated.

Thanks.

1
What exactly are you trying to do? What properties do you want to style? (I don't want to go through your VSM code).Lukkha Coder
I am trying to style the list according to specific states the CalendarViewDayItem might be in. For example, the DayItem for the current day has an accentuated style so I would also like to style the TextBlocks in the list in the same manner.Wesley Vrancken
Any news on this? I have the same question (trying to show some event info in a UWP CalendarView), but I can't imagine how you finally do it only with the info in this question :Pwebo80

1 Answers

2
votes

Your code will not work because VisualStateManager.GoToState takes a Control as the first parameter; however, your VisualStateManager is defined inside a StackPanel of which type is Panel.

You will need to implement your own VSM which takes a Panel instead. Have a look at the answer to this post on how to do this.

However, this still won't fix your problem. As you need to somehow locate the StackPanels (note 'cause it's within a ListView, there could be multiple) and then call the ExtendedVisualStateManager.GoToState.

I would suggest you to wrap this template within a UserControl (by doing this you might do not even need to extend the VSM as you can use GoToStateAction instead) and have a dependency property (IsToday) to control the states. Then you can use ElementName binding to pass a property at the CalendarViewDayItem level down to IsToday in order to make a state change.

Update

I was actually wrong about the need of using the ExtendedVisualManager to change the visual states. Since it's already inside a UserControl, you can actually call VisualStateManager.GoToState(this, "statename", flag); directly.

However, the way you define the dependency property is wrong.

Replace the code behind with the following and it should work.

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