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

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

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

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

MVVM

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

XAML

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

WPF

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

Q&A

解決済

1回答

5160閲覧

【C#(MVVM)】StackPanelをViewModel側で保持したい

k_mail

総合スコア90

C#

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

MVVM

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

XAML

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

WPF

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

1グッド

1クリップ

投稿2022/01/03 08:33

編集2022/01/03 08:35

StackPanelに動的にTextBlockを追加するため
ViewModel側でStackPanelを保持したいです。

コードビハインドでは「spanel.Children.Add(textBlock);」のように書くことができますが、
MVVMのViewModelで記載するにはどのようにすれば良いのでしょうか。
(DataContextにバインディングしようとしましたが、できていない気がします。)

イメージは下記のコードのLabelのように
ViewwModel側はプロパティで状態を保持し、変更がある度、
更新されるようにしたいです。
よろしくお願いします。

■View側

C#

1<Window x:Class="Test1.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:vm="clr-namespace:Test1" 5 Title="MainWindow" Height="350" Width="525"> 6 <Window.DataContext> 7 <vm:Test1ViewModel /> 8 </Window.DataContext> 9 <Grid> 10 <StackPanel 11 DataContext="{Binding TestStackPanel}"/> 12 <Label 13 Content="{Binding TestLabel}" HorizontalAlignment="Left" VerticalAlignment="Top"/> 14 </Grid> 15</Window>

■ViewModel側

C#

1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using System.Threading.Tasks; 6using System.Windows.Controls; 7using System.Windows.Documents; 8using System.Windows.Media; 9 10namespace Test1 11{ 12 class Test1ViewModel : ViewModelBase 13 { 14 /// <summary> 15 /// コンストラクタ 16 /// </summary> 17 public Test1ViewModel() 18 { 19 ShowList(); 20 } 21 22 private string _testLabel = "初期値"; 23 public string TestLabel 24 { 25 get { return _testLabel; } 26 set 27 { 28 if (value == _testLabel) 29 { 30 return; 31 } 32 } 33 } 34 35 private StackPanel _testStackPanel = new StackPanel(); 36 public StackPanel TestStackPanel 37 { 38 get { return _testStackPanel; } 39 set 40 { 41 if (value == _testStackPanel) 42 { 43 return; 44 } 45 } 46 } 47 48 49 private List<string> _itemList = new List<string>(); 50 private void ShowList() 51 { 52 _itemList.Add("テスト1"); 53 _itemList.Add("テスト2"); 54 _itemList.Add("テスト3"); 55 56 foreach (var item in _itemList) 57 { 58 // TextBlockのInlinesプロパティにRunを入れ込む 59 var txtblock = new TextBlock(); 60 61 txtblock.Inlines.Add(new Run 62 { 63 Text = item, 64 Foreground = new SolidColorBrush(Colors.Red) 65 }); 66 67 // ここで動的に作成したTextBlockを追加したい 68 // コードビハインドでは「spanel.Children.Add(textBlock); 」のように書ける 69 TestStackPanel.Children.Add(txtblock); 70 } 71 } 72 73 } 74}

(ViewModelBase)

using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading.Tasks; namespace Test1 { public abstract class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) { if (Equals(storage, value)) return false; storage = value; return true; } } }
TN8001👍を押しています

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

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

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

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

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

Zuishin

2022/01/03 08:45

それは MVVM ではありません。
k_mail

2022/01/03 09:26

StackPanelに動的にTextBlockを追加するには どのように書くのが良いのでしょうか
Zuishin

2022/01/03 09:46 編集

MVVM を使わないのであれば、好きなようにすればいいのでは? ViewModel を無視してコードビハインドで TextBlock のインスタンスを作って追加するのが簡単だと思います。
Zuishin

2022/01/03 09:55 編集

MVVM を使うのであれば ItemsSource にバインドします。 ググってみつかったサンプルにリンクしましたが、不適切だったので削除しました。
k_mail

2022/01/03 10:30

承知しました。 確認します。 ありがとうございます。
guest

回答1

0

ベストアンサー

StackPanelに動的にTextBlockを追加するため
ViewModel側でStackPanelを保持したいです。

MVVMではWindows Forms的な考え方は捨ててください。
「Windows Forms的な考え方」とは、↓のようなことです。

コードビハインドでは「spanel.Children.Add(textBlock);」のように書く

まったく手法が違うので最初は頭の切り替えが大変なのですが、ここがWPFの肝ですし頑張ってください(WPFでもWindows Forms的にコードビハインドでガリガリ書くのを否定するつもりはありません)

MVVMについては良記事がたくさんありますので、検索してください^^;


今回の目標は「StackPanelに動的にTextBlockを追加」ということですね。

StackPanel」ということは、アイテムが複数あるということです。
そして「動的」ということは、アイテムの増減があるということです。

この時点で、

  • ViewではItemsControlListBoxDataGrid等も含む)を使う
  • ViewModelではObservableCollectionを使う

ことが確定します(もうそういうもんだと覚えてください^^;

ItemsControl 攻略 ~ 外観のカスタマイズ | grabacr.nét

ObservableCollection<T> クラス (System.Collections.ObjectModel) | Microsoft Docs

コードを見てもらったほうが早いですが、ViewModelでは文字列のコレクションとして保持し、Viewでその文字列をDataTemplateTextBlock.Runに展開する感じです^^

xml

1<Window 2 x:Class="Questions376475.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:Questions376475" 6 Width="525" 7 Height="350"> 8 <Window.DataContext> 9 <local:ViewModel /> 10 </Window.DataContext> 11 <DockPanel> 12 <Label Content="{Binding TestLabel}" DockPanel.Dock="Top" /> 13 14 <Button 15 Command="{Binding AddCommand}" 16 Content="Add" 17 DockPanel.Dock="Bottom" /> 18 19 <ItemsControl ItemsSource="{Binding ItemList}"> 20 <ItemsControl.ItemTemplate> 21 <DataTemplate> 22 <TextBlock> 23 <Run Foreground="Red" Text="{Binding .}" /> 24 </TextBlock> 25 <!--<TextBlock Foreground="Red" Text="{Binding .}" />--> 26 </DataTemplate> 27 </ItemsControl.ItemTemplate> 28 </ItemsControl> 29 </DockPanel> 30</Window>

cs

1using System.Collections.ObjectModel; 2using System.Windows; 3using CommunityToolkit.Mvvm.ComponentModel; 4using CommunityToolkit.Mvvm.Input; 5 6namespace Questions376475 7{ 8 public class ViewModel : ObservableObject // ViewModelBase 9 { 10 private string _testLabel = "初期値"; 11 public string TestLabel 12 { 13 get => _testLabel; 14 set => SetProperty(ref _testLabel, value); 15 } 16 17 public ObservableCollection<string> ItemList { get; } 18 19 public RelayCommand AddCommand { get; } 20 21 22 public ViewModel() 23 { 24 ItemList = new ObservableCollection<string> 25 { 26 "テスト1", 27 "テスト2", 28 "テスト3", 29 }; 30 31 AddCommand = new RelayCommand(Add); 32 } 33 34 private void Add() 35 { 36 ItemList.Add($"テスト{ItemList.Count + 1}"); 37 38 TestLabel = $"{ItemList.Count}個です"; 39 } 40 } 41 42 public partial class MainWindow : Window 43 { 44 public MainWindow() => InitializeComponent(); 45 } 46}

ViewModelBaseICommand実装を書くのがだるいので、下記を使用しました。
NuGet Gallery | CommunityToolkit.Mvvm 7.1.2

Introduction to the MVVM Toolkit - Windows Community Toolkit | Microsoft Docs

投稿2022/01/03 10:23

編集2023/08/15 10:12
TN8001

総合スコア9862

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

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

k_mail

2022/01/03 10:32

ご丁寧な ご回答ありがとうございます。 「DataTemplate」調べてみます。
k_mail

2022/01/09 14:58

私の環境でもできました。 ありがとうございます!! 再質問でもすいません。 xamlに「{Binding .}」という記載があるのですが、 これはどのサイトを見れば理解できるのでしょうか。 {Binding }にはプロパティ名を指定しないといけないと思うのですが、 なぜドットにしても動いているのでしょうか。。 「xaml{Binding .}」や「WPF {Binding .}」などで 検索してもヒットしないため、検索方法(調べ方)や参考サイトを ご教示頂きたいです。 よろしくお願いします。
TN8001

2022/01/09 15:19

実はちょっと「突っ込まれるかな?」と思っていましたw 私の理解では{Binding}・{Binding Path=.}・{Binding .}は同じ意味だと思っています。 しかし{Binding}だとデザイナがエラーになってしまった(実行はできる)ので、仕方なく付けました^^; > xamlに「{Binding .}」という記載があるのですが、 >これはどのサイトを見れば理解できるのでしょうか。 公式だとこのあたりでしょうか? [Binding.Path プロパティ (System.Windows.Data) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.data.binding.path?view=net-6.0#-- [バインドのマークアップ拡張機能 - WPF .NET Framework | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/desktop/wpf/advanced/binding-markup-extension?view=netframeworkdesktop-4.8 ググるとすると「wpf binding path」あたりがいいと思います。 [wpf binding path - Google 検索](https://www.google.com/search?q=wpf+binding+path [【WPF】【XAML】Hoge="{Binding Path='.'}"のPath='.'って何? | 創造的プログラミングと粘土細工](http://pro.art55.jp/?eid=871963 [What does "{Binding Path=.}" mean in WPF binding? - Stack Overflow](https://stackoverflow.com/questions/1066262/what-does-binding-path-mean-in-wpf-binding
k_mail

2022/01/09 15:22

ありがとうございます。 確認してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問