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

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

新規登録して質問してみよう
ただいま回答率
85.35%
MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

1回答

2554閲覧

【WPF】Viewへのコントロールの追加

退会済みユーザー

退会済みユーザー

総合スコア0

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

0グッド

0クリップ

投稿2021/04/18 07:30

前提・実現したいこと

ViewのGridのセルにViewModelで取得した値を表示させる方法を調べています。
元々参考にしていたものは、コードビハインドにてTextBlockを生成してGrid上に追加するようなやり方をしていたのですが、MVVMで書く方法が分からず調べています。
カレンダーの日付部分にあたる箇所で、手段が見つけられなかったため質問させていただきました。

該当のソースコード

xmal

1<Window 2 x:Class="Test.Views.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors" 6 xmlns:l="http://schemas.livet-mvvm.net/2011/wpf" 7 xmlns:v="clr-namespace:Test.Views" 8 xmlns:vm="clr-namespace:Test.ViewModels" 9 Title="MainWindow" 10 Width="525" 11 Height="350"> 12 13 <Window.DataContext> 14 <vm:MainWindowViewModel /> 15 </Window.DataContext> 16 17 18 <behaviors:Interaction.Triggers> 19 <!-- When ContentRendered event raised, Initialize method of ViewModel would be called. --> 20 <behaviors:EventTrigger EventName="ContentRendered"> 21 <l:LivetCallMethodAction MethodName="Initialize" MethodTarget="{Binding}" /> 22 </behaviors:EventTrigger> 23 24 <!-- Dispose method is called, when Window closing. --> 25 <behaviors:EventTrigger EventName="Closed"> 26 <l:DataContextDisposeAction /> 27 </behaviors:EventTrigger> 28 29 <!-- If you make user choose 'OK or Cancel' closing Window, then please use Window Close cancel Behavior. --> 30 31 </behaviors:Interaction.Triggers> 32 33 <Grid> 34 <GroupBox x:Name="CalendarGropu1" 35 Header="{Binding HeaderMonth}" 36 HorizontalAlignment="Stretch" 37 Margin="10,10,0,0" 38 VerticalAlignment="Stretch" 39 Style="{StaticResource gp-normal}" > 40 41 42 <Grid x:Name="CalendarGrid" 43 HorizontalAlignment="Stretch" 44 Margin="10,10,0,0" 45 VerticalAlignment="Stretch" 46 > 47 48 49 <Grid.ColumnDefinitions> 50 <ColumnDefinition Width="1*" /> 51 <ColumnDefinition Width="1*" /> 52 <ColumnDefinition Width="1*" /> 53 <ColumnDefinition Width="1*" /> 54 <ColumnDefinition Width="1*" /> 55 <ColumnDefinition Width="1*" /> 56 <ColumnDefinition Width="1*" /> 57 </Grid.ColumnDefinitions> 58 59 <Grid.RowDefinitions> 60 <RowDefinition Height="20" /> 61 <RowDefinition Height="1*" /> 62 <RowDefinition Height="1*" /> 63 <RowDefinition Height="1*" /> 64 <RowDefinition Height="1*" /> 65 <RowDefinition Height="1*" /> 66 </Grid.RowDefinitions> 67 68 <Rectangle Stroke="Black" 69 StrokeThickness="1" 70 Grid.ColumnSpan="7" 71 Grid.RowSpan="7"/> 72 <Rectangle Fill="#B0E0E6" 73 HorizontalAlignment="Stretch" 74 Grid.Row="0" 75 Grid.Column="0" 76 VerticalAlignment="Stretch" 77 Margin="1 1 0 0" 78 Panel.ZIndex="0" /> 79 <Rectangle Fill="#B0E0E6" 80 HorizontalAlignment="Stretch" 81 Grid.Row="0" 82 Grid.Column="1" 83 VerticalAlignment="Stretch" 84 Margin="1 1 0 0" 85 Panel.ZIndex="0"/> 86 <Rectangle Fill="#B0E0E6" 87 HorizontalAlignment="Stretch" 88 Grid.Row="0" 89 Grid.Column="2" 90 VerticalAlignment="Stretch" 91 Margin="1 1 0 0" 92 Panel.ZIndex="0"/> 93 <Rectangle Fill="#B0E0E6" 94 HorizontalAlignment="Stretch" 95 Grid.Row="0" 96 Grid.Column="3" 97 VerticalAlignment="Stretch" 98 Margin="1 1 0 0" 99 Panel.ZIndex="0"/> 100 <Rectangle Fill="#B0E0E6" 101 HorizontalAlignment="Stretch" 102 Grid.Row="0" 103 Grid.Column="4" 104 VerticalAlignment="Stretch" 105 Margin="1 1 0 0" 106 Panel.ZIndex="0"/> 107 <Rectangle Fill="#B0E0E6" 108 HorizontalAlignment="Stretch" 109 Grid.Row="0" 110 Grid.Column="5" 111 VerticalAlignment="Stretch" 112 Margin="1 1 0 0" 113 Panel.ZIndex="0"/> 114 <Rectangle Fill="#B0E0E6" 115 HorizontalAlignment="Stretch" 116 Grid.Row="0" 117 Grid.Column="6" 118 VerticalAlignment="Stretch" 119 Margin="1 1 1 0" 120 Panel.ZIndex="0"/> 121 <Rectangle Height="1" 122 Stroke="Black" 123 StrokeThickness="1" 124 VerticalAlignment="Bottom" 125 Grid.ColumnSpan="7"/> 126 127 <Label Content="日" 128 HorizontalAlignment="Center" 129 Grid.Row="0" 130 Grid.Column="0" 131 VerticalAlignment="Center" 132 FontSize="11" 133 Padding="0"/> 134 135 <Label Content="月" 136 HorizontalAlignment="Center" 137 Grid.Row="0" 138 Grid.Column="1" 139 VerticalAlignment="Center" 140 FontSize="11" 141 Padding="0"/> 142 143 <Label Content="火" 144 HorizontalAlignment="Center" 145 Grid.Row="0" 146 Grid.Column="2" 147 VerticalAlignment="Center" 148 FontSize="11" 149 Padding="0"/> 150 151 <Label Content="水" 152 HorizontalAlignment="Center" 153 Grid.Row="0" 154 Grid.Column="3" 155 VerticalAlignment="Center" 156 FontSize="11" 157 Padding="0"/> 158 159 <Label Content="木" 160 HorizontalAlignment="Center" 161 Grid.Row="0" 162 Grid.Column="4" 163 VerticalAlignment="Center" 164 FontSize="11" 165 Padding="0"/> 166 167 <Label Content="金" 168 HorizontalAlignment="Center" 169 Grid.Row="0" 170 Grid.Column="5" 171 VerticalAlignment="Center" 172 FontSize="11" 173 Padding="0"/> 174 175 <Label Content="土" 176 HorizontalAlignment="Center" 177 Grid.Row="0" 178 Grid.Column="6" 179 VerticalAlignment="Center" 180 FontSize="11" 181 Padding="0"/> 182 183 </Grid> 184 </GroupBox> 185 </Grid> 186</Window>

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

VisualStudio2019
.Net Core3.0
Livet v3.2.1

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

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

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

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

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

guest

回答1

0

ベストアンサー

WPFでMVVMするには、WinForms的な考え方は捨ててください。
コントロールにどう入れるかではなく、表示したいデータ(Model≒ViewModel)をバインドし、結果的にテンプレートが当たって表示されます。

カレンダーの日付部分にあたる箇所

カレンダーを作っているわけですね。
日付は1~31あってそれを表示したいのであれば、その数字のコレクション(例えばList<int>)を作ってバインドします。
コレクションですから、ItemsControlListBoxにバインドすることになります。

Gridにこだわりがあるなら↓こういった手法があります。
Gridへのアイテムのバインド(WPF編) | 泥庭

しかしカレンダーはUniformGridのほうが向いていますので、回答はそちらを採用しました。

ItemsControl 攻略 ~ 外観のカスタマイズ | grabacr.nét
こちらをじっくり読んで、各テンプレート等がどういう役割か把握しておいてください(ブックマーク推奨)

xml

1<Window 2 x:Class="Questions333874.Views.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors" 6 xmlns:l="http://schemas.livet-mvvm.net/2011/wpf" 7 xmlns:m="clr-namespace:Questions333874.Models" 8 xmlns:vm="clr-namespace:Questions333874.ViewModels" 9 Width="650" Height="550"> 10 11 <Window.Resources> 12 <DataTemplate DataType="{x:Type m:Schedule}"> 13 <TextBlock Text="{Binding Text}" /> 14 </DataTemplate> 15 </Window.Resources> 16 17 <Window.DataContext> 18 <vm:MainWindowViewModel /> 19 </Window.DataContext> 20 21 <behaviors:Interaction.Triggers> 22 <behaviors:EventTrigger EventName="ContentRendered"> 23 <l:LivetCallMethodAction MethodName="Initialize" MethodTarget="{Binding}" /> 24 </behaviors:EventTrigger> 25 </behaviors:Interaction.Triggers> 26 27 <DockPanel> 28 <DockPanel Margin="10" DockPanel.Dock="Bottom"> 29 <Button Command="{Binding AddCommand}" Content="Add" DockPanel.Dock="Right" IsDefault="True" /> 30 <TextBox Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" /> 31 </DockPanel> 32 33 <GroupBox Margin="10" Padding="10"> 34 <GroupBox.Header> 35 <StackPanel Orientation="Horizontal"> 36 <TextBlock VerticalAlignment="Center" Text="{Binding HeaderMonth}" /> 37 <Button Command="{Binding PreviousCommand}" Content="" /> 38 <Button Command="{Binding NextCommand}" Content="" /> 39 </StackPanel> 40 </GroupBox.Header> 41 42 <ListBox HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" ItemsSource="{Binding Items}" SelectedItem="{Binding Selected}"> 43 <ListBox.Template> 44 <ControlTemplate TargetType="ListBox"> 45 <Border BorderBrush="Black" BorderThickness="1"> 46 <DockPanel> 47 <Border BorderBrush="Black" BorderThickness="0,0,0,1" DockPanel.Dock="Top"> 48 <UniformGrid Columns="7"> 49 <UniformGrid.Resources> 50 <Style TargetType="Label"> 51 <Setter Property="Margin" Value="1,0,0,0" /> 52 <Setter Property="Padding" Value="0" /> 53 <Setter Property="HorizontalContentAlignment" Value="Center" /> 54 <Setter Property="Background" Value="#B0E0E6" /> 55 </Style> 56 </UniformGrid.Resources> 57 <Label Margin="0" Content="" Foreground="Red" /> 58 <Label Content="" /> 59 <Label Content="" /> 60 <Label Content="" /> 61 <Label Content="" /> 62 <Label Content="" /> 63 <Label Content="" Foreground="Blue" /> 64 </UniformGrid> 65 </Border> 66 <ItemsPresenter /> 67 </DockPanel> 68 </Border> 69 </ControlTemplate> 70 </ListBox.Template> 71 72 <ListBox.ItemsPanel> 73 <ItemsPanelTemplate> 74 <UniformGrid Columns="7" FirstColumn="{Binding FirstDayOffset}" /> 75 </ItemsPanelTemplate> 76 </ListBox.ItemsPanel> 77 78 <ListBox.ItemTemplate> 79 <DataTemplate> 80 <DockPanel> 81 <TextBlock HorizontalAlignment="Center" DockPanel.Dock="Top" Foreground="{Binding Color}" Text="{Binding Day}" /> 82 <ItemsControl ItemsSource="{Binding Schedules}" /> 83 </DockPanel> 84 </DataTemplate> 85 </ListBox.ItemTemplate> 86 </ListBox> 87 </GroupBox> 88 </DockPanel> 89</Window>

cs

1using Livet; 2using Livet.Commands; 3using Questions333874.Models; 4using System; 5using System.Collections.Generic; 6using System.Collections.ObjectModel; 7using System.Linq; 8using System.Windows.Media; 9 10namespace Questions333874.ViewModels 11{ 12 public class DayViewModel //: ViewModel 13 { 14 public DateTime DateTime { get; set; } 15 public int Day => DateTime.Day; 16 public Brush Color => DateTime.DayOfWeek == DayOfWeek.Sunday ? Brushes.Red 17 : DateTime.DayOfWeek == DayOfWeek.Saturday ? Brushes.Blue 18 : Brushes.Black; 19 public ObservableCollection<Schedule> Schedules { get; } = new ObservableCollection<Schedule>(); 20 } 21 22 public class MainWindowViewModel : ViewModel 23 { 24 public string HeaderMonth => current.ToString("yyyy/MM"); 25 26 private int firstDayOffset; 27 public int FirstDayOffset { get => firstDayOffset; set => RaisePropertyChangedIfSet(ref firstDayOffset, value); } 28 29 private DayViewModel selected; 30 public DayViewModel Selected 31 { 32 get => selected; 33 set 34 { 35 if (RaisePropertyChangedIfSet(ref selected, value) && selected != null) 36 current = selected.DateTime.Date; 37 } 38 } 39 40 private List<DayViewModel> items; 41 public List<DayViewModel> Items { get => items; set => RaisePropertyChangedIfSet(ref items, value); } 42 43 private string text; 44 public string Text 45 { 46 get => text; 47 set 48 { 49 if (RaisePropertyChangedIfSet(ref text, value)) 50 AddCommand.RaiseCanExecuteChanged(); 51 } 52 } 53 54 55 private ViewModelCommand previousCommand; 56 public ViewModelCommand PreviousCommand => previousCommand ??= new ViewModelCommand(Previous); 57 private void Previous() 58 { 59 current = current.AddMonths(-1); 60 Update(); 61 } 62 63 private ViewModelCommand nextCommand; 64 public ViewModelCommand NextCommand => nextCommand ??= new ViewModelCommand(Next); 65 private void Next() 66 { 67 current = current.AddMonths(1); 68 Update(); 69 } 70 71 private ViewModelCommand addCommand; 72 public ViewModelCommand AddCommand => addCommand ??= new ViewModelCommand(Add, () => !string.IsNullOrEmpty(Text)); 73 private void Add() 74 { 75 var s = new Schedule { DateTime = selected.DateTime.Date, Text = Text, }; 76 schedules.Add(s); 77 selected.Schedules.Add(s); 78 Text = null; 79 } 80 81 private List<Schedule> schedules = new List<Schedule>(); 82 private DateTime current = DateTime.Now.Date; 83 84 // 月変更時 Items再生成 雑w 85 private void Update() 86 { 87 // 選択月 日数 88 var days = DateTime.DaysInMonth(current.Year, current.Month); 89 90 // 日数分DayViewModel作成 91 Items = Enumerable.Range(1, days).Select(x => 92 { 93 var d = new DateTime(current.Year, current.Month, x); 94 return new DayViewModel { DateTime = d, }; 95 }).ToList(); 96 97 // DayViewModelにScheduleを突っ込む 98 foreach (var d in Items) 99 foreach (var s in schedules.Where(x => x.DateTime.Date == d.DateTime.Date)) 100 d.Schedules.Add(s); 101 102 firstDayOffset = (int)new DateTime(current.Year, current.Month, 1).DayOfWeek; 103 selected = Items.Single(x => x.DateTime.Date == current.Date); 104 current = selected.DateTime.Date; 105 106 // 更新 107 RaisePropertyChanged(nameof(FirstDayOffset)); 108 RaisePropertyChanged(nameof(HeaderMonth)); 109 RaisePropertyChanged(nameof(Items)); 110 RaisePropertyChanged(nameof(Selected)); 111 } 112 113 public void Initialize() 114 { 115 // 見本Scheduleデータ作成 116 var r = new Random(); 117 var n = DateTime.Now.Date; 118 schedules = Enumerable.Range(0, 10).Select(_ => 119 { 120 var d = n.AddDays(r.Next(-30, 30)); 121 return new Schedule { DateTime = d, Text = d.ToString("MM/dd の予定"), }; 122 }).ToList(); 123 124 Update(); 125 } 126 } 127}

cs

1using Livet; 2using System; 3using System.Diagnostics; 4 5 6namespace Questions333874.Models 7{ 8 [DebuggerDisplay("{DateTime} {Text}")] 9 public class Schedule : NotificationObject 10 { 11 private DateTime dateTime; 12 public DateTime DateTime { get => dateTime; set => RaisePropertyChangedIfSet(ref dateTime, value); } 13 14 private string text; 15 public string Text { get => text; set => RaisePropertyChangedIfSet(ref text, value); } 16 } 17}

アプリ画像


MVVM寄せで回答しましたが、私だったらこの方式は使いませんね。

カレンダー表示というのは、Viewの都合ですよね?(縦に並ぼうが横に並ぼうが、Modelは知ったこっちゃない)
カスタムコントロール(あるいはユーザーコントロール)を作って、List<Schedule>のようなものをバインドすれば勝手に表示するように作りますかね。

その際はバリバリにコードビハインドということになります(例えばこのような wpf/Calendar.cs at main · dotnet/wpf

MVVMがパワーワードすぎて、なかなか本質が見えないのが難しく感じる原因でしょうか。

投稿2021/04/18 22:05

編集2023/07/26 16:05
TN8001

総合スコア9862

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

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

退会済みユーザー

退会済みユーザー

2021/04/20 11:37

ありがとうございます。 回答を見て理解を深めていきたいと思います。 コードビハインドの方も別途確認しておこうと思います。 躓くことがあればまた質問させていただきます。 改めてありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問