基本的にxamlは上から順に生成されていきます。
今回のようにイベントがすぐ飛ぶ状態(SelectedDate
・SelectedDateChanged
ともに指定)ですと、xamlで下にある要素がまだない状態でイベントが来てしまいます。
動作的にはイヤなのですが、これは今後も変わることはないと思います。
でどうするかですが、
案1
提示コードのようにnullチェックする。
ばかばかしいが安全ではある。
そのうちtxtBox?.Text = "test";
こういう構文が、許されるようになるかもしれない (今はないしいつ来るかももわからない)
C#14で書けるようになりました^^
What's new in C# 14 | Microsoft Learn
案2
単純な解決法は、xaml上でTextBox
が先に来るようにする。
しかし順番次第でエラーになることがあるのはやや不安が残る。
案3
SelectedDate
・SelectedDateChanged
どちらかを、InitializeComponent
後に設定する。
全部できてからイベントが飛ぶようにすれば安心だが、少し不格好かもしれない。
案4
何をしたいかにもよるが、バインドで用が済むなら一番良い。
xml
1<Window
2 x:Class="Q9jmho7nu44o3nz.MainWindow"
3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5 Width="800"
6 Height="450">
7 <UniformGrid>
8 <GroupBox Header="nullチェック必要">
9 <StackPanel>
10 <DatePicker SelectedDate="2023/01/01" SelectedDateChanged="DatePicker_SelectedDateChanged" />
11 <TextBox x:Name="txtBox" />
12 </StackPanel>
13 </GroupBox>
14
15 <GroupBox Header="TextBoxを先に書く">
16 <Grid>
17 <Grid.RowDefinitions>
18 <RowDefinition Height="Auto" />
19 <RowDefinition Height="Auto" />
20 </Grid.RowDefinitions>
21 <TextBox x:Name="txtBox2" Grid.Row="1" />
22 <DatePicker SelectedDate="2023/01/01" SelectedDateChanged="DatePicker2_SelectedDateChanged" />
23 </Grid>
24 </GroupBox>
25
26 <GroupBox Header="Initialize後 設定">
27 <StackPanel>
28 <DatePicker x:Name="datePicker3" SelectedDateChanged="DatePicker3_SelectedDateChanged" />
29 <TextBox x:Name="txtBox3" />
30 </StackPanel>
31 </GroupBox>
32
33 <GroupBox Header="バインド">
34 <StackPanel>
35 <DatePicker x:Name="datePicker4" SelectedDate="2023/01/01" />
36 <TextBox Text="{Binding SelectedDate, StringFormat=yyyy/MM/dd, ElementName=datePicker4, Mode=OneWay}" />
37 </StackPanel>
38 </GroupBox>
39 </UniformGrid>
40</Window>
cs
1using System;
2using System.Windows;
3using System.Windows.Controls;
4
5namespace Q9jmho7nu44o3nz
6{
7 public partial class MainWindow : Window
8 {
9 public MainWindow()
10 {
11 InitializeComponent();
12
13 datePicker3.SelectedDate = DateTime.Parse("2023/01/01");
14 }
15
16 private void DatePicker_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
17 {
18 if (txtBox == null) return; // これが無いと、起動時にNullエラーとなる
19 if (sender is DatePicker picker)
20 {
21 txtBox.Text = picker.SelectedDate?.ToString("d");
22 }
23 }
24 private void DatePicker2_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
25 {
26 if (sender is DatePicker picker)
27 {
28 txtBox2.Text = picker.SelectedDate?.ToString("d");
29 }
30 }
31 private void DatePicker3_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
32 {
33 if (sender is DatePicker picker)
34 {
35 txtBox3.Text = picker.SelectedDate?.ToString("d");
36 }
37 }
38 }
39}
やりたい事は選択した日付からn日後を計算して表示する事でしたが、WPFの基本的なところでハマっていました。
ViewModel
で計算したプロパティを用意するパターンと、Converter
で計算するパターン例。
実際はViewModel
レスというわけにもいかないので、この中間くらい(ViewModel
に必要ない値はConverter
で計算)になることが多いと思います。
xml
1<Window
2 x:Class="Q9jmho7nu44o3nz.MainWindow"
3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
5 xmlns:local="clr-namespace:Q9jmho7nu44o3nz"
6 xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
7 Width="800"
8 Height="450">
9 <Window.DataContext>
10 <local:ViewModel />
11 </Window.DataContext>
12 <Window.Resources>
13 <local:DaysLaterConverter x:Key="DaysLaterConverter" />
14 </Window.Resources>
15 <UniformGrid>
16 <GroupBox Header="ViewModel">
17 <StackPanel>
18 <DatePicker SelectedDate="{Binding SelectedDate}" />
19 <xctk:IntegerUpDown Minimum="0" Value="{Binding DaysLater}" />
20 <TextBlock HorizontalAlignment="Right" Text="日後" />
21 <TextBox Text="{Binding SelectedDateDaysLater, StringFormat=yyyy/MM/dd, Mode=OneWay}" />
22 </StackPanel>
23 </GroupBox>
24
25 <GroupBox Header="Converter">
26 <StackPanel>
27 <DatePicker x:Name="datePicker" SelectedDate="2023/01/01" />
28 <xctk:IntegerUpDown
29 x:Name="integerUpDown"
30 Minimum="0"
31 Value="0" />
32 <TextBlock HorizontalAlignment="Right" Text="日後" />
33 <TextBox>
34 <TextBox.Text>
35 <MultiBinding
36 Converter="{StaticResource DaysLaterConverter}"
37 Mode="OneWay"
38 StringFormat="yyyy/MM/dd">
39 <Binding ElementName="datePicker" Path="SelectedDate" />
40 <Binding ElementName="integerUpDown" Path="Value" />
41 </MultiBinding>
42 </TextBox.Text>
43 </TextBox>
44 </StackPanel>
45 </GroupBox>
46 </UniformGrid>
47</Window>
cs
1using System;
2using System.Globalization;
3using System.Windows;
4using System.Windows.Data;
5using CommunityToolkit.Mvvm.ComponentModel;
6
7namespace Q9jmho7nu44o3nz
8{
9 public class ViewModel : ObservableObject
10 {
11 // ソースジェネレーター(LangVersion 8以上)を使えれば、もっとすっきり書けるのだが...
12 // [ObservableProperty 属性 - .NET Community Toolkit | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/communitytoolkit/mvvm/generators/observableproperty#notifying-dependent-properties)
13 public int DaysLater
14 {
15 get => _DaysLater;
16 set
17 {
18 if (SetProperty(ref _DaysLater, value))
19 OnPropertyChanged(nameof(SelectedDateDaysLater));
20 }
21 }
22 private int _DaysLater;
23
24 public DateTime? SelectedDate
25 {
26 get => _SelectedDate;
27 set
28 {
29 if (SetProperty(ref _SelectedDate, value))
30 OnPropertyChanged(nameof(SelectedDateDaysLater));
31 }
32 }
33 private DateTime? _SelectedDate = DateTime.Parse("2023/01/01");
34
35 public DateTime? SelectedDateDaysLater => SelectedDate?.AddDays(DaysLater);
36 }
37
38 class DaysLaterConverter : IMultiValueConverter // 雑いw
39 {
40 public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
41 {
42 if (values[0] is DateTime d && values[1] is int i)
43 return d.AddDays(i);
44 return null;
45 }
46 public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotImplementedException();
47 }
48
49
50 public partial class MainWindow : Window
51 {
52 public MainWindow() => InitializeComponent();
53 }
54}
NuGet Gallery | CommunityToolkit.Mvvm 8.0.0
MVVM Toolkit の概要 - .NET Community Toolkit | Microsoft Learn
NuGet Gallery | Extended.Wpf.Toolkit 4.5.0
IntegerUpDown · xceedsoftware/wpftoolkit Wiki