質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

Q&A

解決済

1回答

579閲覧

[WPF]カレンダーの日付のBackgroundにトグル機能のような動きをさせたい

k_mail

総合スコア90

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

1グッド

0クリップ

投稿2023/04/19 16:24

実現したいこと

カレンダーコントロールの日付のBackgroundにトグル機能のような動きをさせたいです。
カレンダーの日付をクリックすると日付のBackground色が変わり、
もう一度クリックすると元のデフォルトのBackground色に戻る、というような動きにしたいです。
日付は複数選択可能としたいです。

試したこと

下記のコードでクリックした時色を変更するとことができましたが、
もう一度クリックしても背景色が元に戻らないです。

C#

1<Trigger Property="IsSelected" Value="True"> 2 <Setter TargetName="calDate" Property="Background" Value="#EAAA21"/> 3</Trigger>

また、カレンダーコントロールにSelectedDatesChangedイベントがありますが、
選択日付を変えずに同じ日付を連続で選択した場合、SelectedDatesChangedイベントが呼ばれません。

C#

1 private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e) 2 { 3 DateTime selectedDay = (DateTime)e.AddedItems[0]; 4 Console.WriteLine(selectedDay.ToString("yyyy/MM/dd")); 5 6 7 if (calendar.SelectedDates.Contains(selectedDay)) 8 { 9 calendar.Background = SystemColors.ControlBrush; 10 } 11 else 12 { 13 calendar.SelectedDates.Add(selectedDay); 14 calendar.Background = Brushes.Yellow; 15 } 16 }

補足情報(FW/ツールのバージョンなど)

VisualStudio2019,.netFramework4.8

TN8001👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

カレンダーコントロールの日付のBackgroundにトグル機能のような動きをさせたいです。
日付は複数選択可能としたいです。

ListBoxでいうところのSelectionMode="Multiple"の動作ということですね。

CalendarSelectionModeにはなぜかMultipleがありません(UWPにはあるのですが...)
Calendar.SelectionMode プロパティ (System.Windows.Controls) | Microsoft Learn

MultipleRangeListBoxExtendedに似た動作になります(Ctrl+クリックやドラッグで複数選択)

仕方がないのでPreviewMouseLeftButtonDownで、そういう動作になるように捏造します。
Create a Behavior for Calendar in a WPF UserControl to Select and Unselect dates with one click

キーボード操作があると選択が消えたり面倒なことになるので、使わないようにするかつぶしておいたほうがいいでしょう。
Calendar クラス (System.Windows.Controls) | Microsoft Learn

選択日付を変えずに同じ日付を連続で選択した場合、SelectedDatesChangedイベントが呼ばれません。

選択が変わってないので当然呼ばれません。
この改造後は選択・選択解除と変更があるので呼ばれるようになります。

xml

1<Window 2 x:Class="Qil3fftqm79tcvt.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 <Grid> 8 <Calendar 9 x:Name="calendar" 10 PreviewKeyDown="Calendar_PreviewKeyDown" 11 SelectedDatesChanged="Calendar_SelectedDatesChanged" 12 SelectionMode="MultipleRange"> 13 <Calendar.CalendarDayButtonStyle> 14 <Style BasedOn="{StaticResource {x:Type CalendarDayButton}}" TargetType="CalendarDayButton"> 15 <EventSetter Event="PreviewMouseLeftButtonDown" Handler="CalendarDayButton_PreviewMouseLeftButtonDown" /> 16 <Style.Triggers> 17 <Trigger Property="IsSelected" Value="True"> 18 <Setter Property="Background" Value="#EAAA21" /> 19 </Trigger> 20 </Style.Triggers> 21 </Style> 22 </Calendar.CalendarDayButtonStyle> 23 </Calendar> 24 </Grid> 25</Window>

cs

1using System; 2using System.Diagnostics; 3using System.Windows; 4using System.Windows.Controls; 5using System.Windows.Controls.Primitives; 6using System.Windows.Input; 7 8namespace Qil3fftqm79tcvt 9{ 10 public partial class MainWindow : Window 11 { 12 public MainWindow() => InitializeComponent(); 13 14 private void CalendarDayButton_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 15 { 16 if (sender is CalendarDayButton dayButton && !dayButton.IsBlackedOut) 17 { 18 var date = (DateTime)dayButton.DataContext; 19 20 if (calendar.SelectedDates.Contains(date)) 21 calendar.SelectedDates.Remove(date); 22 else 23 calendar.SelectedDates.Add(date); 24 25 e.Handled = true; 26 } 27 } 28 29 private void Calendar_PreviewKeyDown(object sender, KeyEventArgs e) => e.Handled = true; 30 31 private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e) 32 { 33 if (0 < e.AddedItems.Count) 34 Debug.WriteLine($"add: {(DateTime)e.AddedItems[0]:yyyy/MM/dd}"); 35 36 if (0 < e.RemovedItems.Count) 37 Debug.WriteLine($"remove: {(DateTime)e.RemovedItems[0]:yyyy/MM/dd}"); 38 } 39 } 40}

ControlTemplateが不明なのでStyle.Triggersを使いましたが、ControlTemplate.Triggersでもいいです。


追記 ControlTemplateResourceDictionary

MainWindow.xaml

xml:MainWindow.xaml

1<Window 2 x:Class="Qil3fftqm79tcvt.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="600"> 7 <Grid> 8 <Calendar 9 x:Name="calendar" 10 PreviewKeyDown="Calendar_PreviewKeyDown" 11 SelectedDatesChanged="Calendar_SelectedDatesChanged" 12 SelectionMode="MultipleRange"> 13 <Calendar.CalendarDayButtonStyle> 14 <Style BasedOn="{StaticResource CalendarDayButtonStyle1}" TargetType="CalendarDayButton"> 15 <EventSetter Event="PreviewMouseLeftButtonDown" Handler="CalendarDayButton_PreviewMouseLeftButtonDown" /> 16 </Style> 17 </Calendar.CalendarDayButtonStyle> 18 </Calendar> 19 </Grid> 20</Window>

cs:MainWindow.xaml.cs

1using System; 2using System.Diagnostics; 3using System.Windows; 4using System.Windows.Controls; 5using System.Windows.Controls.Primitives; 6using System.Windows.Input; 7 8namespace Qil3fftqm79tcvt 9{ 10 public partial class MainWindow : Window 11 { 12 public MainWindow() => InitializeComponent(); 13 14 private void Calendar_PreviewKeyDown(object sender, KeyEventArgs e) => e.Handled = true; 15 16 private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e) 17 { 18 if (0 < e.AddedItems.Count) 19 Debug.WriteLine($"add: {(DateTime)e.AddedItems[0]:yyyy/MM/dd}"); 20 21 if (0 < e.RemovedItems.Count) 22 Debug.WriteLine($"remove: {(DateTime)e.RemovedItems[0]:yyyy/MM/dd}"); 23 } 24 25 private void CalendarDayButton_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 26 { 27 if (sender is CalendarDayButton dayButton && !dayButton.IsBlackedOut) 28 { 29 var date = (DateTime)dayButton.DataContext; 30 31 if (calendar.SelectedDates.Contains(date)) 32 calendar.SelectedDates.Remove(date); 33 else 34 calendar.SelectedDates.Add(date); 35 36 e.Handled = true; 37 } 38 } 39 } 40}

Dictionary1.xaml

xml:Dictionary1.xaml

1<ResourceDictionary 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 4 5 <Style x:Key="CalendarDayButtonStyle1" TargetType="{x:Type CalendarDayButton}"> 6 <Setter Property="Template"> 7 <Setter.Value> 8 <ControlTemplate TargetType="{x:Type CalendarDayButton}"> 9 <Grid x:Name="calDate" Background="White"> 10 <Border BorderBrush="Turquoise" BorderThickness="1"> 11 <StackPanel MinWidth="80" MinHeight="80"> 12 <TextBlock 13 Margin="2" 14 FontSize="16" 15 Text="{Binding StringFormat={}{0:dd}}" /> 16 <ItemsControl> 17 <ItemsControl.ItemsPanel> 18 <ItemsPanelTemplate> 19 <WrapPanel Margin="2" /> 20 </ItemsPanelTemplate> 21 </ItemsControl.ItemsPanel> 22 </ItemsControl> 23 </StackPanel> 24 </Border> 25 <Path 26 x:Name="Blackout" 27 Margin="3" 28 HorizontalAlignment="Stretch" 29 VerticalAlignment="Stretch" 30 Data="M8.1772461,11.029181 L10.433105,11.029181 L11.700684,12.801641 L12.973633,11.029181 L15.191895,11.029181 L12.844727,13.999395 L15.21875,17.060919 L12.962891,17.060919 L11.673828,15.256231 L10.352539,17.060919 L8.1396484,17.060919 L10.519043,14.042364 z" 31 Fill="#FF000000" 32 Opacity="0" 33 RenderTransformOrigin="0.5,0.5" 34 Stretch="Fill" /> 35 </Grid> 36 <ControlTemplate.Triggers> 37 <Trigger Property="IsSelected" Value="True"> 38 <Setter TargetName="calDate" Property="Background" Value="#EAAA21" /> 39 </Trigger> 40 <Trigger Property="IsBlackedOut" Value="True"> 41 <Setter TargetName="Blackout" Property="Opacity" Value="0.2" /> 42 </Trigger> 43 </ControlTemplate.Triggers> 44 </ControlTemplate> 45 </Setter.Value> 46 </Setter> 47 </Style> 48 49</ResourceDictionary>

投稿2023/04/19 21:47

編集2023/04/23 00:23
TN8001

総合スコア9317

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

k_mail

2023/04/21 12:08 編集

ご回答ありがとうございます。 返信が遅くなり申し訳ございません。 ControlTemplate.Triggersに書き直しながら対応中です。 改めて質問させて頂くかもしれませんが、よろしくお願いします。
k_mail

2023/04/22 14:11 編集

ResourceDictionaryファイルの <ControlTemplate TargetType="{x:Type CalendarDayButton}">の中に 宣言しようとするのですが、どこに記載してもビルドエラーになります。 <EventSetter Event="PreviewMouseLeftButtonDown" Handler="CalendarDayButton_PreviewMouseLeftButtonDown" /> 上記を正しいところに記載するための情報の調べ方を教えいて頂くことは可能でしょうか。
TN8001

2023/04/22 22:06

> ResourceDictionaryファイルの ResourceDictionaryにあるんですか^^; では、 1. プロジェクトにクラス「ResourceDictionary名.xaml.cs」を追加してください。 「Dictionary1.xaml」だったら「Dictionary1.xaml.cs」ということです。 Visual Studio 2022では自動的にxamlにぶら下がりましたが、2019では未確認(分かれていても動きはするはず) あるいはいったんWindowやUserControlとして追加して、丸ごと書き換えても行けると思う。 2. xamlとcsを追記した回答コードのように変更してください。 xamlの「x:Class="プロジェクト名.クラス名(ResourceDictionary名)"」と、csの「partial」忘れに注意してください。
TN8001

2023/04/22 22:11

> ResourceDictionaryファイルの > <ControlTemplate TargetType="{x:Type CalendarDayButton}">の中に あれ? ResourceDictionaryにはControlTemplateだけがあるってこと? だったらStyleには関係しないよねぇ?
k_mail

2023/04/22 23:30 編集

追記のご回答ありがとうございます。 確認します。 ちなみに、2019ではResourceDictionaryはxamlのみになります。
TN8001

2023/04/22 23:58

> ちなみに、2019ではResourceDictionaryはxamlのみになります。 2022でもそうです。csは普通はありません。 しかしResourceDictionaryにStyleを書きつつEventSetter(Handler)も使用したいとなると、csを作るしかありません。 ControlTemplateだけResourceDictionaryに書いて、StyleはMainWindow内でよければこんな面倒なことはいりません^^; 「未確認」なのはcsを追加したときに、「ソリューション エクスプローラー」上で親子関係になる(xamlの下階層にcsが来る)かどうかという話です(2019は手元にないので)
k_mail

2023/04/23 00:18

そういう事ですね。 承知しました。
TN8001

2023/04/23 00:23

> しかしResourceDictionaryにStyleを書きつつEventSetter(Handler)も使用したいとなると、csを作るしかありません。 あ、イヤBasedOnすりゃいいのか(回答で使っておきながら忘れてたw 回答をさらに編集しました^^;
k_mail

2023/04/23 01:44

ありがとうございます。 動くところまで確認できました。 コードが理解しきれていないのでもうしばらく調べてみます。 ありがとうございます!!
TN8001

2023/04/23 02:15 編集

> 動くところまで確認できました。 寝ぼけて追記しお手間とらせてすいませんでした。 > コードが理解しきれていないのでもうしばらく調べてみます。 はい。 回答に疑問点があればお気軽にコメントください^^
k_mail

2023/04/24 05:45

2点追加で質問です。 ■1点目 CalendarDayButton_PreviewMouseLeftButtonDownメソッドの e.Handled = true;をコメントアウトするだけで複数選択ができなくなりました。 e.Handled = true;とは何をしているのでしょうか。 SelectionMode="MultipleRange"が記載されているにも関わらず、 e.Handled = true;の1行で複数選択の機能が左右される理由が分からないです。 また、参考になるサイトがあれば共有頂けますと幸いです。 ■2点目 「検索力」のような話になりますが、 SelectedDatesChangedメソッドではなく、 PreviewMouseLeftButtonDownメソッドで実装する必要があると分かった経緯を教えて頂きたいです。 検索キーワードを 「WPF カレンダー クリック」 「WPF カレンダー 複数選択」では PreviewMouseLeftButtonDownメソッドは出て来なかったです。
TN8001

2023/04/24 09:50 編集

> ■1点目 > e.Handled = true;とは何をしているのでしょうか。 イベントを「処理済みとしてマーク」します。 [RoutedEventArgs.Handled プロパティ (System.Windows) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.routedeventargs.handled) 自身(CalendarDayButton)のMouseLeftButtonDownを抑制しているんではないかと(ぶっちゃけあんまりわかってません^^; > SelectionMode="MultipleRange"が記載されているにも関わらず、 > e.Handled = true;の1行で複数選択の機能が左右される理由が分からないです。 通常のMultipleRangeの動作は把握されていますか? Ctrl+ドラッグ(やCtrl+クリックやCtrl+Shift+クリック)で複数の範囲を選択します。 (Ctrlを押していない)単にクリックだとそれまでの選択がなくなり、クリックした日だけが選択されます(MultipleRangeでも!) その選択し直しが今回は邪魔なので、「e.Handled = true」でデフォルトの動作を呼び出さないようにしています(ソースまで見ていませんがMouseLeftButtonDownでやっているんでしょう) その代わり選択もされないので、自前で選択処理をしています。 > また、参考になるサイトがあれば共有頂けますと幸いです。 ルーティングイベントはなかなか複雑な仕組みです。 [ルーティング イベントの概要 - WPF .NET | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/desktop/wpf/events/routed-events-overview) このぐらいの認識があれば十分ではないかと。 [WPF4.5入門 その46 「WPFのイベントシステム」 - かずきのBlog@hatena](https://blog.okazuki.jp/entry/2014/08/22/211021) > ■2点目 > 検索キーワードを 日本語で出てこなければ英語にしてください。大抵はStack Overflowがヒットするはず(だが今回は違った^^; まず「SelectedDatesChanged」は選択が変わった後にくるイベントですから、そもそもよくありません。 「同じ日付を連続で選択した場合」も呼ばれませんし、その中で選択をいじったりすると無限ループの恐れもあります。 もしも自分で実装するのなら「CalendarDayButtonをいじるしかないのかなぁ?」と漠然と思いましたが、特に見込みも立っていませんでした。 しかし自分も(マウス大好き派なので)なんでそういうモードがないか不思議に思いました(ListBoxはMultipleあるのに!) おそらくすぐ出てくるだろうと 「wpf calendar toggle select」 「wpf calendar multiple selection」 等で調べましたが全く見当たらず WinUI3はどうなんだろう?と脱線したり(XAML Islandsも検討はしたがやっぱ面倒でやめた^^; [WinUI 3 Gallery - Microsoft Store アプリ](https://apps.microsoft.com/store/detail/winui-3-gallery/9P3JFPWWDZRC?hl=ja-jp&gl=jp) ソースを見ようとしてみたり(すぐあきらめたw [Calendar.cs](https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/Calendar.cs) 仕方なくちょっとコードを書いて確認をしてみるかと思いました。 この時点では見込みは全く立っていません(特にドラッグでの選択の抑制が大変そうに思えた) 前回の回答コードに <Calendar.CalendarDayButtonStyle> <Style TargetType="{x:Type CalendarDayButton}"> <EventSetter Event="Click" Handler="CalendarDayButton_Click" /> と(何の気なしに)書いたところ、なぜか呼ばれませんでした。 「wpf eventsetter calendardaybutton not working」と検索しました(今思うとワードはだいぶクソいですw で、こちらが出てきたという顛末です。 [Create a Behavior for Calendar in a WPF UserControl to Select and Unselect dates with one click](https://social.msdn.microsoft.com/Forums/sqlserver/en-US/5ffc6d94-429a-4cb9-884f-5c053fc17583) すぐ手元で試し理想的な動作だったので、内心は「わースゲー天才だなぁ!!」って思っていました^^ 回答ではテンション上げても仕方がないので、淡々と書きましたw 今回は割と偶然です^^; 結果的には「wpf calendar unselect」と、検索すべきだったのかもしれません。 自力で解決に至らなかったと言って、へこむ必要は全くありません。 なにせ↑のページがなければ、わたしも(自力で)解決できたかは大変怪しいです。 今後はこの質問のおかげで、日本語で検索しても解決しやすくなったはずです^^
k_mail

2023/04/25 00:07 編集

ご回答ありがとうございます。 英語アレルギー克服しないとだめですね... 参考リンクありがとうございます!! 読んでみます。 (今回のe.Handled = true;はルーティングイベント関連ですかね。。 勉強になりました。)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問