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

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

新規登録して質問してみよう
ただいま回答率
85.51%
.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

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

MVVM

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

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

WPF

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

Q&A

解決済

1回答

1908閲覧

MVVMで処理をループさせViewへ通知する際の実装方法

locoJr.

総合スコア15

.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

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

MVVM

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

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

WPF

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

1グッド

2クリップ

投稿2022/08/06 07:23

前提

MVVMで、Viewのボタンを押したらその後一定間隔で対象機へポーリングし、実行結果をViewへ通知するような仕組みを作ろうとしています。
※MVVM初心者。

MVVMでやるとなった場合に、どこでタイマーを稼働させるべきなのか(Command?Model?)、Modelだったとした場合はModelへ更にViewModelのインスタンスを渡すようにするのか、通常の実装方法が分かりませんでした。
※後者ではできそうだとは思っています。

解決したいこと

  • 一般的にどう実装するものなのか、教えていただきたいです。
    • どこでタイマーを稼働させるべきなのか(Command?Model?)
    • Modelだったとした場合はModelへ更にViewModelのインスタンスを渡すようにするのか

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

VS2022 .NET6

TN8001👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

どこでタイマーを稼働させるべきなのか(Command?Model?)

それが一連の処理なら、ModelなりServiceなりになるんじゃないでしょうか(少なくともViewModelではないような)

Modelだったとした場合はModelへ更にViewModelのインスタンスを渡すようにするのか

MVVMでModelがViewModelを参照することはありません。

xml

1<Window 2 x:Class="Qu1hlld0oo83bby.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 <StackPanel> 8 <Button Command="{Binding StartPollingCommand}" Content="StartPolling(連打可)" /> 9 <Button Command="{Binding StartPolling2Command}" Content="StartPolling2(連打不可)" /> 10 <TextBlock Text="{Binding Model.Result}" /> 11 12 <Button Command="{Binding GetResultCommand}" Content="GetResult(連打不可)" /> 13 <TextBlock Text="{Binding Result}" /> 14 </StackPanel> 15</Window>

cs

1using System; 2using System.Threading; 3using System.Threading.Tasks; 4using System.Windows; 5using CommunityToolkit.Mvvm.ComponentModel; 6using CommunityToolkit.Mvvm.Input; 7 8namespace Qu1hlld0oo83bby 9{ 10 // ModelがINotifyPropertyChangedを実装し変更通知する 11 public partial class Model : ObservableObject 12 { 13 [ObservableProperty] 14 // ↓がソースジェネレータで生成されています 15 //public string? Result { get => result; set => SetProperty(ref result, value); } 16 private string? result; 17 18 private int count; 19 20 public void StartPolling() 21 { 22 for (var i = 0; i < 20; i++) 23 { 24 Thread.Sleep(100); 25 } 26 27 Result = $"{count++}"; 28 } 29 } 30 31 // 結果を取得できればいいだけのようなイメージでService 32 public class Service 33 { 34 private int count; 35 36 public async Task<string> GetResultAsync() 37 { 38 for (var i = 0; i < 20; i++) 39 { 40 await Task.Delay(TimeSpan.FromMilliseconds(100)); 41 } 42 43 return $"{count++}"; 44 } 45 } 46 47 public partial class ViewModel : ObservableObject 48 { 49 public Model Model { get; } // ラップするのがめんどいので直公開w 50 51 [ObservableProperty] 52 private string? result; 53 54 private Service service; 55 56 public ViewModel(Model model, Service service) 57 { 58 Model = model; 59 this.service = service; 60 } 61 62 [RelayCommand] 63 // ↓がソースジェネレータで生成されています 64 //private RelayCommand? startPollingCommand; 65 //public IRelayCommand StartPollingCommand => startPollingCommand ??= new RelayCommand(StartPolling); 66 private void StartPolling() 67 { 68 Task.Run(() => Model.StartPolling()); // 投げっぱなし 69 } 70 71 [RelayCommand] 72 private async Task StartPolling2Async() 73 { 74 await Task.Run(() => Model.StartPolling()); 75 } 76 77 [RelayCommand] 78 private async Task GetResultAsync() 79 { 80 Result = await service.GetResultAsync(); 81 } 82 } 83 84 public partial class MainWindow : Window 85 { 86 public MainWindow() 87 { 88 InitializeComponent(); 89 90 DataContext = new ViewModel(new Model(), new Service()); 91 } 92 } 93}

NuGet Gallery | CommunityToolkit.Mvvm 8.0.0
MVVM Toolkit の概要 - .NET Community Toolkit | Microsoft Docs


タイミングが悪いことに、今現在VSがバグっていてエラーが出ますw(回避コードを追加してください)
CommunityToolKit.Mvvm source generators run twice on Visual Studio 17.2.6 · Issue #343 · CommunityToolkit/dotnet
.csproj

xml

1<Project Sdk="Microsoft.NET.Sdk"> 2 3 <PropertyGroup> 4 <OutputType>WinExe</OutputType> 5 <TargetFramework>net6.0-windows</TargetFramework> 6 <Nullable>enable</Nullable> 7 <UseWPF>true</UseWPF> 8 </PropertyGroup> 9 10 <ItemGroup> 11 <PackageReference Include="CommunityToolkit.Mvvm" Version="8.0.0" /> 12 </ItemGroup> 13 14 <Target Name="RemoveDuplicateAnalyzers" BeforeTargets="CoreCompile"> 15 <!-- Work around https://github.com/dotnet/wpf/issues/6792 --> 16 17 <ItemGroup> 18 <FilteredAnalyzer Include="@(Analyzer->Distinct())" /> 19 <Analyzer Remove="@(Analyzer)" /> 20 <Analyzer Include="@(FilteredAnalyzer)" /> 21 </ItemGroup> 22 </Target> 23 24</Project>

この処理に「進捗報告する・キャンセル対応する」等があると、また話が変わってきます。
wpf iprogress - Google 検索

投稿2022/08/06 11:27

編集2022/08/06 11:36
TN8001

総合スコア9220

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

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

locoJr.

2022/08/06 15:00 編集

ご回答ありがとうございます。 今更ながらMVVMの勉強をしており、まずはFWを使わずに標準の実装方法でやっていました。 ※参考サイト:https://resanaplaza.com/%e4%b8%96%e7%95%8c%e3%81%a7%e4%b8%80%e7%95%aa%e7%9f%ad%e3%81%84%e3%82%b5%e3%83%b3%e3%83%97%e3%83%ab%e3%81%a7%e8%a6%9a%e3%81%88%e3%82%8bmvvm%e5%85%a5%e9%96%80/ Prismなどはそのあとでやろうとしていましたが、「CommunityToolkit.Mvvm」とやらを使うとかなり記述が減らせそうですね。 一旦作り終わってから、このやり方でリファクタリングしてみようと思います。 また、INotifyPropertyChangedはModelにも実装して良いのですね。 ViewModelだけという縛りだと思っていました。 > この処理に「進捗報告する・キャンセル対応する」等があると、また話が変わってきます。 すみません、質問時に具体的にどの結果をバインドしたいのか記述するべきでした。 今回は単純な処理結果をバインドしたいわけではなく、「都度タイマーElapsedイベントでモデル側の処理を実行させ、非同期で実行結果(bool)と実行結果詳細(複数のプロパティ)をバインドさせる」ことをしたいです。 ※非同期や後者(適切でないと仰った、ModelからViewModelへの通知)での実装はできております。 ただ、INotifyPropertyChangedをModelにも実装すればいけそうですね。 提示いただいた内容は、MVVMから逸脱はしない感じですか? ※ModelがModel+ViewModelになっている?
TN8001

2022/09/23 10:26 編集

> 今更ながらMVVMの勉強をしており、まずはFWを使わずに標準の実装方法でやっていました。 INotifyPropertyChanged・ICommand実装についてはフレームワークというほどのものでもなく、実装できていれば何でもいいです^^(自分で書くのはだるいのと文字制限もあるので既存のものを使用しました) その辺のブログのコードでもいいですし、↓この辺りを使ってもいいでしょう。 [TemplateStudio/Observable.cs at main · microsoft/TemplateStudio](https://github.com/microsoft/TemplateStudio/blob/main/code/SharedFunctionality.UI/Mvvm/Observable.cs) [TemplateStudio/RelayCommand.cs at main · microsoft/TemplateStudio](https://github.com/microsoft/TemplateStudio/blob/main/code/SharedFunctionality.UI/Mvvm/RelayCommand.cs) Prism.Coreにも入っています(BindableBase・DelegateCommand) [NuGet Gallery | Prism.Core 8.1.97](https://www.nuget.org/packages/Prism.Core/) 今回.NET6とのことでしたので、高パフォーマンスなCommunityToolkit.Mvvmを採用しました。 [dotnet/ObservableObject.cs at main · CommunityToolkit/dotnet](https://github.com/CommunityToolkit/dotnet/blob/main/CommunityToolkit.Mvvm/ComponentModel/ObservableObject.cs) [dotnet/RelayCommand.cs at main · CommunityToolkit/dotnet](https://github.com/CommunityToolkit/dotnet/blob/main/CommunityToolkit.Mvvm/Input/RelayCommand.cs) > また、INotifyPropertyChangedはModelにも実装して良いのですね。 > ViewModelだけという縛りだと思っていました。 Livet作者の尾上さんの記事 [MVVMのModelにまつわる誤解 - the sea of fertility](https://ugaya40.hateblo.jp/entry/model-mistake) ReactiveProperty作者の河合さんの記事 [neue cc - ReactiveProperty ver 0.3.0.0 - MとVMのバインディングという捉え方](https://neue.cc/2011/11/20_354.html) どちらもModelにも使っています。 過去(WPF初期のころ)議論があったようですが、現在は「使ってもよい」という共通認識だと思います。 > 今回は単純な処理結果をバインドしたいわけではなく、「都度タイマーElapsedイベントでモデル側の処理を実行させ、非同期で実行結果(bool)と実行結果詳細(複数のプロパティ)をバインドさせる」ことをしたいです。 Model層ならSystem.Timers.Timerで問題ないです(「DispatcherTimerでなくてよい」という意味) > ただ、INotifyPropertyChangedをModelにも実装すればいけそうですね。 そうですね。 > 提示いただいた内容は、MVVMから逸脱はしない感じですか? > ※ModelがModel+ViewModelになっている? どうでしょうね。 わたしはMVVM原理主義者ではないので厳格に守る気はさらさらないのですが、「Modelの直公開」なんかは怒る人もいるかもしれませんw ラップすること自体は(めんどくさいだけで)簡単ですけど、「コードが増えるだけでそれ意味あんの?」感がしてしまって(ReactivePropertyを使えれば簡単&省コードで済みます) ちゃんとMVVMしようとするほどコードが膨れ上がるので、規模が小さいうちはある程度の妥協は気にしてません^^; 重要視しているのはテスタビリティですね(単体テストができない(しにくい)のは、何かがおかしいとみて間違いないです) 別に100%も狙いませんしViewもテストしませんが、大事なとことか異常系はしっかり押さえたいです。 疎結合やDIなんかは、必要になってから意識するような感じです。 ちょっとしたツール程度しか作らない趣味グラマなんで、プロの方のご意見は違うかもしれません^^;
locoJr.

2022/08/07 17:20

たくさん教えていただき、ありがとうございます。 INotifyPropertyChanged・ICommand実装などでは、色々なパターン(実装コード)があるのですね。 ModelでINotifyPropertyChangedが使える(使うのがアンチパターンではない)と聞けて良かったです! ※わざわざViewModelへプロパティを設定するなど、『無駄なコード感』があったためです。 今回は、Model層にSystem.Timers.Timerをセットし、処理することにしました。 「ReactiveProperty」←この辺の単語も、後日リファクタ時に確認してみます! そうですね、重要な処理や異常系はテスト必須ですね。 自分も100%は現実的ではないと考えていますので、基本的なパスを通す&ある程度のパスを通すようにしています。 選択肢を提示していただいたり、ご自分の場合ならどうするかなど、大変勉強になりました。 ありがとうございます。 一旦初期段階の実装が完了したため、いただいた情報よりリファクタを進めてみます。 本当にありがとうございました!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問