UWP - How to format time in TimePicker_TimeChanged

2019-08-14 16:35发布

问题:

I am developing UWP App (Win10 VS2015). I have two problems.

1- How can I get the time in this format (4:00 PM or 9:34 AM etc..) in 12hours format, I can get the value without PM/AM via this TimePicker.Time = sender.Time.ToString(@"hh\:mm"), but I need the actual format as I mentioned.

XAML Code

<TimePicker ClockIdentifier="12HourClock" TimeChanged="TimePicker_TimeChanged" Style="{StaticResource TimePickerStyleCustom}"/>

.cs Code

private void TimePicker_TimeChanged(object sender, TimePickerValueChangedEventArgs e)
    {            
        timeTitle.Text = (sender as TimePicker).Time.ToString(@"hh\:mm");
    }

Via the above code, I can get the value without AM/PM and also it is in 24hour format i.e. 4:00PM is in 16:00, but I need it in 4:00PM or 4:00AM (this is just an example). If I put .ToString(@"hh\:mm tt"); it throws exception. How to get this please.

2- 2nd problem is, When we tap on the Timepicker, a TimePickerFlyout expands and we select time by clicking on hours/minutes and when finalize then click on the (Tick) Mark to select Time ... but I need to remove these buttons (Done (_/) & Cancel (X)) and select time by selecting the Hour/Min in Flyout panel rather than button click and assign it to a string. I can remove the buttons from TimePickerFlyoutPresenter Style but then how to make the Selection functional like button click.

See the screenshot, in first portion the 2 buttons are available and it worked, but I need the 2nd portion as shown in the right side.

回答1:

For First problem TimeSpan represents a time interval not a time of day. You have to convert it to DateTime then format it

 private void TestTimePicker_TimeChanged(object sender, TimePickerValueChangedEventArgs e)
        {
          string Text = (sender as TimePicker).Time.ToString(@"hh\:mm");
            var dateTime = new DateTime((sender as TimePicker).Time.Ticks); // Date part is 01-01-0001
            var formattedTime = dateTime.ToString("h:mm tt", CultureInfo.InvariantCulture);
        }

Problem 2 For this either you have to implement your own TimerPickerFlyout from PickerFlyoutBase or from Flyout. It is bit complicated and I havent worked on that. You can watch this link for that

There is a easy workaround . As you mentioned in question you have to edit TimePickerFlyoutPresenter style.

I tried adding Tapped event handler to FirstPickerHost,SecondPickerHost,ThirdPickerHost.But you cant add event handlers in app.xaml. So i used Behavioural SDK's interactions. If you have Template10 used in your project you dont have to download anything just add following namespaces in app.xaml

xmlns:interact="using:Microsoft.Xaml.Interactivity" 
xmlns:interactcore="using:Microsoft.Xaml.Interactions.Core"



 <Style TargetType="TimePickerFlyoutPresenter">
        <Setter Property="Width" Value="242" />
        <Setter Property="MinWidth" Value="242" />
        <Setter Property="MaxHeight" Value="396" />
        <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
        <Setter Property="FontWeight" Value="Normal" />
        <Setter Property="IsTabStop" Value="False" />
        <Setter Property="Background" Value="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" />
        <Setter Property="AutomationProperties.AutomationId" Value="TimePickerFlyoutPresenter" />
        <Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundTransparentBrush}" />
        <Setter Property="BorderThickness" Value="{ThemeResource DateTimeFlyoutBorderThickness}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="TimePickerFlyoutPresenter">
                    <Border x:Name="Background"
                        Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}"
                        MaxHeight="396">
                        <Grid x:Name="ContentPanel">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="*" />
                           </Grid.RowDefinitions>

                            <Grid >
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" x:Name="FirstPickerHostColumn" />
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="*" x:Name="SecondPickerHostColumn" />
                                    <ColumnDefinition Width="Auto" />
                                    <ColumnDefinition Width="*" x:Name="ThirdPickerHostColumn" />
                                </Grid.ColumnDefinitions>

                                <Rectangle x:Name="HighlightRect" Fill="{ThemeResource SystemControlHighlightListAccentLowBrush}" Grid.Column="0" Grid.ColumnSpan="5" VerticalAlignment="Center" Height="44"  >

                                </Rectangle>

                                <Border x:Name="FirstPickerHost" Grid.Column="0" >
                                    <interact:Interaction.Behaviors>
                                        <interactcore:EventTriggerBehavior EventName="Tapped">
                                            <interactcore:InvokeCommandAction Command="{Binding ClosePopUp}"/>
                                        </interactcore:EventTriggerBehavior>
                                    </interact:Interaction.Behaviors>
                                </Border>
                                <Rectangle x:Name="FirstPickerSpacing" Fill="{ThemeResource SystemControlForegroundBaseLowBrush}" HorizontalAlignment="Center" Width="2" Grid.Column="1" >

                                </Rectangle>
                                <Border x:Name="SecondPickerHost" Grid.Column="2" >
                                    <interact:Interaction.Behaviors>
                                        <interactcore:EventTriggerBehavior EventName="Tapped">
                                            <interactcore:InvokeCommandAction Command="{Binding ClosePopUp}"/>
                                        </interactcore:EventTriggerBehavior>
                                    </interact:Interaction.Behaviors>
                                </Border>
                                <Rectangle x:Name="SecondPickerSpacing" Fill="{ThemeResource SystemControlForegroundBaseLowBrush}" HorizontalAlignment="Center" Width="2" Grid.Column="3" >

                                </Rectangle>
                                <Border x:Name="ThirdPickerHost" Grid.Column="4" >
                                    <interact:Interaction.Behaviors>
                                        <interactcore:EventTriggerBehavior EventName="Tapped">
                                            <interactcore:InvokeCommandAction Command="{Binding ClosePopUp}"/>
                                        </interactcore:EventTriggerBehavior>
                                    </interact:Interaction.Behaviors>
                                </Border>

                            </Grid>

                            <Grid Grid.Row="1"  Visibility="Collapsed">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="*" />
                                </Grid.ColumnDefinitions>
                                <Rectangle Height="2" VerticalAlignment="Top" Fill="{ThemeResource SystemControlForegroundBaseLowBrush}" Grid.ColumnSpan="2" />

                                <Button x:Name="AcceptButton" Grid.Column="0" Content="&#xE8FB;" FontFamily="{ThemeResource SymbolThemeFontFamily}" FontSize="16" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Style="{StaticResource DateTimePickerFlyoutButtonStyle}" Margin="0,2,0,0" />
                                <Button x:Name="DismissButton" Grid.Column="1" Content="&#xE711;" FontFamily="{ThemeResource SymbolThemeFontFamily}" FontSize="16" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Style="{StaticResource DateTimePickerFlyoutButtonStyle}" Margin="0,2,0,0" />
                            </Grid>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

And you have to set datacontext of Timepicker to your viewmodel.

   <TimePicker x:Name="TestTimePicker" Time="{Binding SelectedTime,Mode=TwoWay}" ClockIdentifier="12HourClock" Time="0" TimeChanged="TestTimePicker_TimeChanged" >
     </TimePicker>

    public MainPage()
            {
                this.InitializeComponent();
                DataContext = new TestViewModel();
                TestTimePicker.DataContext = this.DataContext;
            }

    public class TestViewModel:INotifyPropertyChanged
        {
            public DelegateCommand<TappedRoutedEventArgs> ClosePopUp { get; set; }
 TimeSpan selectedTime;
        public TimeSpan SelectedTime
        { get { return selectedTime; }
            set
            {
                if (value != selectedTime)
                {
                    selectedTime = value;
                    OnPropertyChanged("SelectedTime");
                }
            }
        }

            public TestViewModel()
        {

            ClosePopUp = new DelegateCommand<TappedRoutedEventArgs>((args) =>
            {
                if (args.OriginalSource is Grid)
                {
                    Grid grid = args.OriginalSource as Grid;
                    if (grid != null)
                    {
                       var fly = FlyoutBase.GetAttachedFlyout(grid);
                        var flyoutpresenter = FindParent<TimePickerFlyoutPresenter>(grid);
                        if (flyoutpresenter != null)
                            (flyoutpresenter.Parent as Popup).IsOpen = false;
                       var firstPicker= FindParent(grid,"FirstPickerHost");
                        var secondPicker = FindParent(grid, "SecondPickerHost");
                        var thirdPicker = FindParent(grid, "ThirdPickerHost");
                        var textblock = FindElementInVisualTree<TextBlock>(grid);
                        if (firstPicker != null)
                        {
                            SelectedTime = new TimeSpan(int.Parse(textblock.Text), SelectedTime.Minutes, SelectedTime.Seconds);

                        }
                         if(secondPicker!=null)
                        {
                            SelectedTime = new TimeSpan(SelectedTime.Hours, int.Parse(textblock.Text), SelectedTime.Seconds);
                        }
                        if (thirdPicker != null)
                        {
                          // AM/PM
                        }

                    }
                }
                else if(args.OriginalSource is TextBlock)
                {
                    TextBlock textblock = args.OriginalSource as TextBlock;
                    if (textblock != null)
                    {
                        var fly = FlyoutBase.GetAttachedFlyout(textblock);
                        var flyoutpresenter = FindParent<TimePickerFlyoutPresenter>(textblock);
                        if (flyoutpresenter != null)
                            (flyoutpresenter.Parent as Popup).IsOpen = false;
                        var firstPicker = FindParent(textblock, "FirstPickerHost");
                        var secondPicker = FindParent(textblock, "SecondPickerHost");
                        var thirdPicker = FindParent(textblock, "ThirdPickerHost");

                        if (firstPicker != null)
                        {
                            SelectedTime = new TimeSpan(int.Parse(textblock.Text), SelectedTime.Minutes, SelectedTime.Seconds);

                        }
                        if (secondPicker != null)
                        {
                            SelectedTime = new TimeSpan(SelectedTime.Hours, int.Parse(textblock.Text), SelectedTime.Seconds);
                        }
                        if (thirdPicker != null)
                        {
                           //  AM/PM
                        }
                    }
                }
                else
                {

                }
            });

        }

        public event PropertyChangedEventHandler PropertyChanged;
        void OnPropertyChanged(string propertyName)
        {
            // the new Null-conditional Operators are thread-safe:
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private T FindParent<T>(DependencyObject child) where T : DependencyObject
        {
            var parent = VisualTreeHelper.GetParent(child);
            if (parent != null && parent is T)
                return (T)parent;
            else if (parent == null)
                return null;
            else
            {
                var result = FindParent<T>(parent);
                if (result != null)
                    return result;

            }
            return null;
        }
        private DependencyObject FindParent(DependencyObject child,string parentName) 
        {
            var parent = VisualTreeHelper.GetParent(child);
            if (parent != null && (parent as FrameworkElement).Name.Equals(parentName))
                return parent;
            else if (parent == null)
                return null;
            else
            {
                var result = FindParent(parent,parentName);
                if (result != null)
                    return result;

            }
            return null;
        }
       private T FindElementInVisualTree<T>(DependencyObject parentElement) where T : DependencyObject
    {
        var count = VisualTreeHelper.GetChildrenCount(parentElement);
        if (count == 0) return null;

        for (int i = 0; i < count; i++)
        {
            var child = VisualTreeHelper.GetChild(parentElement, i);
            if (child != null && child is T)
                return (T)child;
            else
            {
                var result = FindElementInVisualTree<T>(child);
                if (result != null)
                    return result;
            }
        }
        return null;
    }
    }

What i'm doing above in ClosePopUp command is programmatically finding TimePickerFlyoutPresenter using VisualTreeHelper getparaent() method TimePickerFlyoutPresenter parent is a PopUp that is actually your TimePickerFlyout. Set popup's IsOpen to false

// Updated the code to reflect selected hour and minute in timepicker. One issue left is update the selected AM or PM. I ll update if i get the solution

Here is a link to complete project which solves all issues Source Code