none
DatePickerの最大最小設定 RRS feed

  • 質問

  • DatePicker(https://msdn.microsoft.com/ja-jp/library/system.windows.controls.datepicker.aspx)に最大最小を設定したいのですが、どのようにすると良いのでしょうか?範囲外を入力したら、ロストフォーカス時に、前の有効な値に戻るようにしたいと思っています。

    DatePickerのDisplayDateStartやDisplayDateEndで設定可能かと思いましたが、テキストボックスへ直接入力すると、範囲設定として機能していないようでした。

    ご助言いただけると幸いです。よろしくお願いいたします。

    2018年8月24日 12:09

回答

  • こんな

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <StackPanel>
                <StackPanel Orientation="Horizontal">
                    <Calendar x:Name="dateStart" SelectedDate="2018-8-1" DisplayDateEnd="{Binding ElementName=dateEnd,Path=SelectedDate}"/>
                    <Calendar x:Name="dateEnd" SelectedDate="2018-8-31" DisplayDateStart="{Binding ElementName=dateStart,Path=SelectedDate}"/>
                </StackPanel>
    
                <DatePicker Margin="20" MinWidth="200" HorizontalAlignment="Left"
                            DisplayDateStart="{Binding ElementName=dateStart,Path=SelectedDate,NotifyOnTargetUpdated=True,Mode=OneWay}" 
                            DisplayDateEnd="{Binding ElementName=dateEnd,Path=SelectedDate,NotifyOnTargetUpdated=True,Mode=OneWay}" 
                            TargetUpdated="DatePicker_TargetUpdated">
                            <!-- DisplayDateStart/Endの変更イベントは無いのでバインディングのTargetUpdateで検出-->
                </DatePicker>
                
                <TextBox />
            </StackPanel>
        </Grid>
    </Window>
    using System;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace WpfApp1
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void DatePicker_TargetUpdated(object sender, System.Windows.Data.DataTransferEventArgs e)
            {
                if (e.Property == DatePicker.DisplayDateStartProperty
                 || e.Property == DatePicker.DisplayDateEndProperty)
                {
                    SetBlackoutDates((DatePicker)sender);
                }
            }
    
            private static void SetBlackoutDates(DatePicker dp)
            {
                dp.BlackoutDates.Clear();
                if (dp.DisplayDateStart != null)
                {
                    dp.BlackoutDates.Add(new CalendarDateRange(DateTime.MinValue, dp.DisplayDateStart.Value.AddDays(-1)));
                }
    
                if (dp.DisplayDateEnd != null)
                {
                    dp.BlackoutDates.Add(new CalendarDateRange(dp.DisplayDateEnd.Value.AddDays(1), DateTime.MaxValue));
                }
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク yj0529 2018年8月29日 0:24
    2018年8月25日 5:02
  • 良い感じの案を思いつかなかったので、妥協案としてどうぞという位置づけで投稿しようと思っていましたw
    一応自分のも残しておきます。

    MainWindow.xaml
    <Window x:Class="WpfApp3.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp3"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        
        <StackPanel>
    
            <DatePicker 
                x:Name="datepicker1" 
                DisplayDateStart="2018/1/1" 
                DisplayDateEnd="2018/9/1" 
                LostFocus="DatePicker_LostFocus" />
    
            <Button 
                Content="button1" />
    
        </StackPanel>
        
    </Window>
    

    MainWindow.xaml.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace WpfApp3
    {
        /// <summary>
        /// MainWindow.xaml の相互作用ロジック
        /// </summary>
        public partial class MainWindow : Window
        {
            DateTime? StartDate;
            DateTime? EndDate;
            DateTime? PreviousDate;
    
            public MainWindow()
            {
                InitializeComponent();
                this.StartDate = this.datepicker1.DisplayDateStart;
                this.EndDate = this.datepicker1.DisplayDateEnd;
            }
    
            private void DatePicker_LostFocus(object sender, RoutedEventArgs e)
            {
                var ctrl = sender as DatePicker;
                //var startDate = ctrl.DisplayDateStart;
                //var endDate = ctrl.DisplayDateEnd;    // Text プロパティに連動して?、設定値が更新されてしまっている。誤判定してしまう
                var checkDate = DateTime.MinValue;
                if (!DateTime.TryParse(ctrl.Text, out checkDate))
                {
                    return;
                }
    
                // 最小日付より前の日付の場合、最小日付に強制変更(ただし、前回日付がバックアップされている場合はこちらを優先)
                if (checkDate < StartDate)
                {
                    if (this.PreviousDate == null)
                    {
                        ctrl.Text = StartDate.Value.ToString("yyyy/MM/dd");
                    }
                    else
                    {
                        ctrl.Text = this.PreviousDate.Value.ToString("yyyy/MM/dd");
                    }
                }
    
                // 最大日付より後の日付の場合、最大日付に強制変更(ただし、前回日付がバックアップされている場合はこちらを優先)
                if (EndDate < checkDate)
                {
                    if (this.PreviousDate == null)
                    {
                        ctrl.Text = EndDate.Value.ToString("yyyy/MM/dd");
                    }
                    else
                    {
                        ctrl.Text = PreviousDate.Value.ToString("yyyy/MM/dd");
                    }
                }
    
                // 今回確定の日付をバックアップ
                this.PreviousDate = DateTime.Parse(ctrl.Text);
    
            }
    
        }
    }
    




    • 回答としてマーク yj0529 2018年8月29日 0:25
    2018年8月25日 5:11

すべての返信

  • こんな

    <Window x:Class="WpfApp1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp1"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        <Grid>
            <StackPanel>
                <StackPanel Orientation="Horizontal">
                    <Calendar x:Name="dateStart" SelectedDate="2018-8-1" DisplayDateEnd="{Binding ElementName=dateEnd,Path=SelectedDate}"/>
                    <Calendar x:Name="dateEnd" SelectedDate="2018-8-31" DisplayDateStart="{Binding ElementName=dateStart,Path=SelectedDate}"/>
                </StackPanel>
    
                <DatePicker Margin="20" MinWidth="200" HorizontalAlignment="Left"
                            DisplayDateStart="{Binding ElementName=dateStart,Path=SelectedDate,NotifyOnTargetUpdated=True,Mode=OneWay}" 
                            DisplayDateEnd="{Binding ElementName=dateEnd,Path=SelectedDate,NotifyOnTargetUpdated=True,Mode=OneWay}" 
                            TargetUpdated="DatePicker_TargetUpdated">
                            <!-- DisplayDateStart/Endの変更イベントは無いのでバインディングのTargetUpdateで検出-->
                </DatePicker>
                
                <TextBox />
            </StackPanel>
        </Grid>
    </Window>
    using System;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace WpfApp1
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
    
            private void DatePicker_TargetUpdated(object sender, System.Windows.Data.DataTransferEventArgs e)
            {
                if (e.Property == DatePicker.DisplayDateStartProperty
                 || e.Property == DatePicker.DisplayDateEndProperty)
                {
                    SetBlackoutDates((DatePicker)sender);
                }
            }
    
            private static void SetBlackoutDates(DatePicker dp)
            {
                dp.BlackoutDates.Clear();
                if (dp.DisplayDateStart != null)
                {
                    dp.BlackoutDates.Add(new CalendarDateRange(DateTime.MinValue, dp.DisplayDateStart.Value.AddDays(-1)));
                }
    
                if (dp.DisplayDateEnd != null)
                {
                    dp.BlackoutDates.Add(new CalendarDateRange(dp.DisplayDateEnd.Value.AddDays(1), DateTime.MaxValue));
                }
            }
        }
    }

    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答としてマーク yj0529 2018年8月29日 0:24
    2018年8月25日 5:02
  • 良い感じの案を思いつかなかったので、妥協案としてどうぞという位置づけで投稿しようと思っていましたw
    一応自分のも残しておきます。

    MainWindow.xaml
    <Window x:Class="WpfApp3.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp3"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
        
        <StackPanel>
    
            <DatePicker 
                x:Name="datepicker1" 
                DisplayDateStart="2018/1/1" 
                DisplayDateEnd="2018/9/1" 
                LostFocus="DatePicker_LostFocus" />
    
            <Button 
                Content="button1" />
    
        </StackPanel>
        
    </Window>
    

    MainWindow.xaml.cs
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    
    namespace WpfApp3
    {
        /// <summary>
        /// MainWindow.xaml の相互作用ロジック
        /// </summary>
        public partial class MainWindow : Window
        {
            DateTime? StartDate;
            DateTime? EndDate;
            DateTime? PreviousDate;
    
            public MainWindow()
            {
                InitializeComponent();
                this.StartDate = this.datepicker1.DisplayDateStart;
                this.EndDate = this.datepicker1.DisplayDateEnd;
            }
    
            private void DatePicker_LostFocus(object sender, RoutedEventArgs e)
            {
                var ctrl = sender as DatePicker;
                //var startDate = ctrl.DisplayDateStart;
                //var endDate = ctrl.DisplayDateEnd;    // Text プロパティに連動して?、設定値が更新されてしまっている。誤判定してしまう
                var checkDate = DateTime.MinValue;
                if (!DateTime.TryParse(ctrl.Text, out checkDate))
                {
                    return;
                }
    
                // 最小日付より前の日付の場合、最小日付に強制変更(ただし、前回日付がバックアップされている場合はこちらを優先)
                if (checkDate < StartDate)
                {
                    if (this.PreviousDate == null)
                    {
                        ctrl.Text = StartDate.Value.ToString("yyyy/MM/dd");
                    }
                    else
                    {
                        ctrl.Text = this.PreviousDate.Value.ToString("yyyy/MM/dd");
                    }
                }
    
                // 最大日付より後の日付の場合、最大日付に強制変更(ただし、前回日付がバックアップされている場合はこちらを優先)
                if (EndDate < checkDate)
                {
                    if (this.PreviousDate == null)
                    {
                        ctrl.Text = EndDate.Value.ToString("yyyy/MM/dd");
                    }
                    else
                    {
                        ctrl.Text = PreviousDate.Value.ToString("yyyy/MM/dd");
                    }
                }
    
                // 今回確定の日付をバックアップ
                this.PreviousDate = DateTime.Parse(ctrl.Text);
    
            }
    
        }
    }
    




    • 回答としてマーク yj0529 2018年8月29日 0:25
    2018年8月25日 5:11
  • gekka様

    BlackoutDatesのことは思いつきませんでした。非常に参考になりました。ありがとうございます!

    2018年8月29日 0:28
  • sutefu7様

    とても参考になる内容でした。本当にありがとうございます!

    2018年8月29日 0:37