WPFで重たい処理をスレッド化せずに、処理の途中でViewを更新しようとしても変わらない。
本来はビジネスロジックはスレッドで動かして、UIスレッドを邪魔しない機構を検討すべきではあるが、下記参考ページの方法が手っ取り早かったので採用。
https://www.ipentec.com/document/csharp-wpf-implement-application-doevents
下記、プログレスバーの更新を例に解決方法を説明。
実行結果
XAML
プログレスバーと、重たい処理を発火するボタンを配置。
プログレスバーのレンジは0~100で、進捗値はViewModelの"prog.Value"にバインド。
<Window x:Class="prism_test.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="{Binding Title}" Height="170" Width="525">
<Grid>
<StackPanel>
<ProgressBar x:Name="progressBar" Maximum="100" Minimum="0" Value="{Binding prog.Value}" Height="50" Margin="10,10,10,10" />
<Button Content="Button" HorizontalAlignment="Left" Height="40" Margin="10,10,10,10" Width="100" Command="{Binding DoHeavyProc}"/>
</StackPanel>
</Grid>
</Window>
コードビハインド
肝になるのが"UpdataView"メソッド。これをViewModelからデリゲートを介して呼び出す。
using System.Windows;
using System.Windows.Threading;
namespace prism_test.Views
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
/// <summary>
/// ViewModelへの参照
/// </summary>
ViewModels.MainWindowViewModel vm;
/// <summary>
/// コンストラクタ
/// </summary>
public MainWindow()
{
InitializeComponent();
// ViewModelへの参照記憶
vm = (ViewModels.MainWindowViewModel)DataContext;
// View側処理をViewModelに設定
vm.UpdateView = UpdataView;
}
/// <summary>
/// Viewの更新
/// </summary>
/// <param name="prog">進捗</param>
public void UpdataView()
{
DispatcherFrame frame = new DispatcherFrame();
var callback = new DispatcherOperationCallback(obj =>
{
((DispatcherFrame)obj).Continue = false;
return null;
});
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, callback, frame);
Dispatcher.PushFrame(frame);
}
}
}
ViewModel
ボタン押下時に進捗値を20ms毎にカウントアップしています。
using Prism.Mvvm;
using Reactive.Bindings;
namespace prism_test.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
/// <summary>
/// 進捗
/// </summary>
public ReactiveProperty<int> prog { get; set; } = new ReactiveProperty<int>();
/// <summary>
/// コマンド
/// </summary>
public ReactiveCommand DoHeavyProc { get; }
/// <summary>
/// View処理へのデリゲート
/// </summary>
public System.Action UpdateView;
/// <summary>
/// コンストラクタ
/// </summary>
public MainWindowViewModel()
{
prog.Value = 0;
this.DoHeavyProc = new ReactiveCommand().WithSubscribe(this.buttonpush);
}
/// <summary>
/// ボタン押下
/// </summary>
public void buttonpush()
{
prog.Value = 0;
for(var i = 0; i < 100; i++)
{
prog.Value++;
// Viewを更新
UpdateView();
System.Threading.Thread.Sleep(20);
}
}
}
}