NullReferenceException for PART_HeaderButton in WPF Calendar

-2

I have a custom control template for my CalendarItem, and it works great except that when you click the month (<Button x:Name="PART_HeaderButton">) the app crashes with:

System.NullReferenceException: 'Object reference not set to an instance of an object.'

Here is most of the error detail.

System.NullReferenceException
  HResult=0x80004003
  Message=Object reference not set to an instance of an object.
  Source=PresentationFramework
  StackTrace:
   at System.Windows.Controls.Primitives.CalendarItem.<GetCalendarButtons>d__56.MoveNext()
   at System.Windows.Controls.Primitives.CalendarItem.GetCalendarButton(DateTime date, CalendarMode mode)
   at System.Windows.Controls.Primitives.CalendarItem.FocusDate(DateTime date)
   at System.Windows.Controls.Primitives.CalendarItem.HeaderButton_Click(Object sender, RoutedEventArgs e)
   at System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
   at System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
   at System.Windows.Controls.Primitives.ButtonBase.OnClick()
   at System.Windows.Controls.Button.OnClick()

I don't know how to deal with MoveNext or GetCalendarButton or anything, but the thing is, I don't care. I don't need the month name to be clickable. If a fix involved actually fixing it to make it work, that's fine, but what I really care about is that my app doesn't crash! I don't need a year mode or decade mode anyway.

Obviously, the simple answer is to set the button to IsEnabled="False", however, that's not working for some reason! I have <Button x:Name="PART_HeaderButton" IsEnabled="False"/> and the button is NOT disabled. Sometimes, if I delete the IsEnabled, and then put it back, the button becomes disabled, but when I move to another view and back, or restart the app, it's back to a normal button. Setting Command={x:Null} also doesn't stop the button from trying to execute (and hence crashing).

Since I'm at a loss for what the heck is going on, and I don't know where to focus on, here is my whole damn calendar:

<Calendar.CalendarItemStyle>
    <Style TargetType="{x:Type CalendarItem}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type CalendarItem}">
                    <ControlTemplate.Resources>
                        <!--DAY OF WEEK-->
                        <DataTemplate x:Key="{x:Static CalendarItem.DayTitleTemplateResourceKey}">
                            <TextBlock FontSize="12" TextAlignment="Center" Text="{Binding}" VerticalAlignment="Center"/>
                        </DataTemplate>
                    </ControlTemplate.Resources>

                    <!--Entire calendar-->
                    <Border Padding="8 0" Background="{StaticResource BackgroundBlack}" x:Name="itemBorder">
                        <Grid Name="PART_Root">

                            <Grid.RowDefinitions>
                                <!--Month and month nav row-->
                                <RowDefinition Height="{StaticResource monthHeaderHeight}"/>
                                <!--Month view (days grid)-->
                                <RowDefinition/>
                            </Grid.RowDefinitions>

                            <!--TOP ROW (Month)-->
                            <DockPanel Grid.Column="0" Grid.Row="0" VerticalAlignment="Center" LastChildFill="False">
                                <DockPanel.Resources>
                                    <Style TargetType="Button" BasedOn="{StaticResource PlainTextButton}"/>
                                    <Style TargetType="Button" BasedOn="{StaticResource PlainTextButton}" x:Key="arrowStyle">
                                        <Setter Property="Height" Value="14"/>
                                        <Setter Property="Width" Value="36"/>
                                    </Style>
                                </DockPanel.Resources>

                                <!--MONTH NAVIGATION-->
                                <!--Unclear why they have to be in reverse order.-->

                                <Button x:Name="PART_NextButton" DockPanel.Dock="Right" Style="{StaticResource arrowStyle}">
                                    <Image Source="/Resources/Images/Right.png"/>
                                </Button>
                                <Button x:Name="PART_PreviousButton" DockPanel.Dock="Right" Style="{StaticResource arrowStyle}">
                                    <Image Source="/Resources/Images/Left.png"/>
                                </Button>

                                <!--CURRENT MONTH-->
                                <!--TODO: Make sure this is NOT CLICKABLE because clicking crashes (IsEnabled="False" doesn't seem to fix it)-->
                                <Button x:Name="PART_HeaderButton" IsEnabled="False"/>

                            </DockPanel>

                            <Grid Grid.Column="0" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center">
                                <Grid x:Name="PART_MonthView">
                                    <Grid.RowDefinitions>
                                        <!--Weeks (first row is Days of the Week)-->
                                        <RowDefinition Height="{StaticResource dayOfWeekHeight}"/>
                                        <RowDefinition/>
                                        <RowDefinition/>
                                        <RowDefinition/>
                                        <RowDefinition/>
                                        <RowDefinition/>
                                        <RowDefinition/>
                                    </Grid.RowDefinitions>
                                    <Grid.ColumnDefinitions>
                                        <!--Days-->
                                        <ColumnDefinition/>
                                        <ColumnDefinition/>
                                        <ColumnDefinition/>
                                        <ColumnDefinition/>
                                        <ColumnDefinition/>
                                        <ColumnDefinition/>
                                        <ColumnDefinition/>
                                    </Grid.ColumnDefinitions>
                                </Grid>
                            </Grid>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</Calendar.CalendarItemStyle>
wpf
xaml
asked on Stack Overflow Jul 13, 2020 by Jonathan Tuzman

1 Answer

1

First, I always recommend looking at the .NET Reference Source to understand what is going on inside the control.

https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Primitives/CalendarItem.cs

The reason why IsEnabled isn't working is that CalendarItem automatically sets IsEnabled on the button internally.

You might want to consider one of these options:

  1. Set IsHitTestVisible="False" on the button instead to prevent it from being clickable
  2. Don't include the button at all and display your own month label using the DisplayDate property and a value converter to get the month name

I hope this is helpful.

answered on Stack Overflow Jul 13, 2020 by Keithernet

User contributions licensed under CC BY-SA 3.0