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

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

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

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

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

WPF

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

Q&A

解決済

1回答

4191閲覧

wpfでの非同期バインディングの方法について

Hayashi_Jelly

総合スコア26

MVVM

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

VB.NET

Microsoft Visual Basic .NETのことで、Microsoft Visual Basic(VB6)の後継。 .NET環境向けのプログラムを開発することができます。 現在のVB.NETでは、.NET Frameworkを利用して開発を行うことが可能です。

WPF

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

1グッド

0クリップ

投稿2021/02/08 09:13

編集2021/02/08 14:27

実現したいこと・分からないこと

  • wpfで、重い処理の進捗をUI(View)に逐次反映させたいですが、反映できません。
  • MVVMでコマンドから起動する処理が発行するメッセージを、処理が終わってから一度に表示するのではなく、都度Viewに反映させたいです。
  • 非同期(Task)は使ったことがなかったので、あまり理解できてないかもしれません。
  • 逐次反映させる方法が知りたいです。

よろしくお願いいたします。

VB

1 ' ViewModel 2 Private _MyCommand As ICommand 3 Public ReadOnly Property MyCommand As ICommand 4 Get 5 If Me._MyCommand Is Nothing Then 6 Me._MyCommand = New DelegateCommand With { 7 .ExecuteHandler = AddressOf _MyCommandExecute, 8 .CanExecuteHandler = AddressOf _MyCommandCanExecute 9 } 10 End If 11 Return Me._MyCommand 12 End Get 13 End Property 14 Private Sub MyCommandExecute(ByVal para As Object) 15 Threading.Thread.Sleep(1000) 16 Me.Output = "hoge" ' 進捗1 17 Threading.Thread.Sleep(1000) 18 Me.Output &= "fuga" ' 進捗2 19 End Sub 20 Private Function MyCommandCanExecute(ByVal para As Object) As Boolean 21 Return True 22 End Function

xaml

1 <!-- View --> 2 <Grid> 3 <Menu> 4 <MenuItem Icon="{Binding Icon}" Command="{Binding MyCommand}" /> 5 </Menu> 6 <TextBlock Text="{Binding Output}" /> 7 </Grid>

やってみたこと

ViewのIsAsyncプロパティにTrueを指定

xaml

1 <!-- View --> 2 <Grid> 3 <Menu> 4 <MenuItem Icon="{Binding Icon}" Command="{Binding MyCommand}" /> 5 </Menu> 6 <TextBlock Text="{Binding Output, IsAsync=True}" /> 7 </Grid>
  • バインディングソースにセットするメソッドをわざわざ非同期にしなくとも、ターゲット側を非同期に反映できればいいのかと思ったが、msdnのIsAsyncに関する資料を見たら、どうやら読み書きに時間が掛かる場合に、一旦パスができるという、こちらの期待と違う感じ。

非同期処理(?)にセットさせてみる

VB

1 Private Async Sub MyCommandExecute(ByVal para As Object) 2 Dim t As Task(Of String) = Task.Run( 3 Function() 4 Me.Output &= "hoge" 5 Return "hoge" 6 End Function 7 ) 8 Await t 9 Threading.Thread.Sleep(1000) 10 Me.Output &= t.Result 11 12 Threading.Thread.Sleep(1000) 13 Dim t2 As Task(Of String) = Task.Run( 14 Function() 15 Me.Output &= "fuga" 16 Return "fuga" 17 End Function 18 ) 19 Await t2 20 Me.Output &= t2.Result 21 End Sub
  • そもそも、これで非同期処理が書けているのかが疑問。また参考にしたサイトによると、「UIスレッド以外でセットさせると例外が発生する」とのことだが、発生しないのが謎(コマンドからの起動がおかしい?)

一応、別スレッド側で値を返して、本スレッドでセットさせるロジックも書いてみたが、やはりセットされない。
(そもそも本スレッドでセットしたら、非同期にならないんじゃないかとも思う)

追記

考えが逆だったかも・・・
下のように書いたら、逐次反映できてた

Private Async Sub MyCommandExecute(ByVal para As Object) Dim t As Task(Of String) = Task.Run( Function() Threading.Thread.Sleep(1000) End Function ) Await t Me.Output &= "hoge" Threading.Thread.Sleep(1000) Dim t2 As Task(Of String) = Task.Run( Function() Threading.Thread.Sleep(1000) End Function ) Await t2 Me.Output &= "fuga" End Sub
TN8001👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

基本的には重い処理をTask.Run()に包むだけです。

「UI以外にセットさせると例外が発生する」

UIスレッド以外からいじれないのはコントロール類です(textBlock.Text=""のような)
バインディングに使用しているプロパティは、スレッドに関係なく変更できます。

xml

1<Window 2 x:Class="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:Questions321254" 6 Width="800" 7 Height="450"> 8 <Window.DataContext> 9 <local:ViewModel /> 10 </Window.DataContext> 11 <DockPanel> 12 <Menu DockPanel.Dock="Top"> 13 <MenuItem Command="{Binding MyCommand}" Header="MyCommand" /> 14 <MenuItem Command="{Binding MyCommand2}" Header="MyCommand2" /> 15 <MenuItem Command="{Binding AsyncReactiveCommand}" Header="AsyncReactiveCommand" /> 16 </Menu> 17 <TextBlock Text="{Binding Output}" /> 18 </DockPanel> 19</Window>

vb

1Imports System.Threading 2Imports Prism.Commands 3Imports Prism.Mvvm 4Imports Reactive.Bindings 5 6Public Class ViewModel 7 Inherits BindableBase 8 9 Private _Output As String 10 Public Property Output As String 11 Get 12 Return _Output 13 End Get 14 Set(ByVal value As String) 15 SetProperty(_Output, value) 16 End Set 17 End Property 18 19 'MyCommand2で実行可否に使用 20 Private _CanExecute As Boolean = True 21 Private Property CanExecute As Boolean 22 Get 23 Return _CanExecute 24 End Get 25 Set(ByVal value As Boolean) 26 SetProperty(_CanExecute, value) 27 End Set 28 End Property 29 30 Public ReadOnly Property MyCommand As DelegateCommand 31 Public ReadOnly Property MyCommand2 As DelegateCommand 32 Public ReadOnly Property AsyncReactiveCommand As AsyncReactiveCommand 33 34 Public Sub New() 35 MyCommand = New DelegateCommand(AddressOf MyCommandExecute) 36 MyCommand2 = New DelegateCommand(Async Sub() Await MyCommand2Execute()).ObservesCanExecute(Function() CanExecute) 37 AsyncReactiveCommand = New AsyncReactiveCommand().WithSubscribe(AddressOf AsyncReactiveCommandExecute) 38 End Sub 39 40 '何の制御もないので連打できてしまう 41 Private Sub MyCommandExecute() 42 Task.Run(Sub() HeavyWork()) '投げっぱなしTask 43 End Sub 44 45 '処理中は押せないようにCanExecuteプロパティで制御 46 Private Async Function MyCommand2Execute() As Task 47 CanExecute = False 48 Await Task.Run(Sub() HeavyWork()) 49 CanExecute = True 50 End Function 51 52 'AsyncReactiveCommandならおまかせできる 53 Private Async Function AsyncReactiveCommandExecute() As Task 54 Await Task.Run(Sub() HeavyWork()) 55 End Function 56 57 Private Sub HeavyWork() 58 Output += "はじめるよ" & vbLf 59 Thread.Sleep(1000) 60 Output += "もうちょっと" & vbLf 61 Thread.Sleep(1000) 62 Output += "おわったよ" & vbLf 63 End Sub 64End Class

VBは全然わかっていないので、変な書き方をしていたらすいません。

DelegateCommand実装は↓を使用しました。
NuGet Gallery | Prism.Core 8.0.0.1909

おまかせ例のAsyncReactiveCommandは↓に含まれています。
NuGet Gallery | ReactiveProperty 7.7.0

投稿2021/02/08 14:54

編集2023/07/26 13:57
TN8001

総合スコア9321

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

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

Hayashi_Jelly

2021/02/09 04:41

ありがとうございます。おっしゃる通り、処理をTask.Run()内に囲っておいたら、すべて逐次更新されました。 xamlやVBのソースもありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問