LoginSignup
2
2

More than 1 year has passed since last update.

WPF-Viewを明示的に更新する

Posted at

WPFで重たい処理をスレッド化せずに、処理の途中でViewを更新しようとしても変わらない。
本来はビジネスロジックはスレッドで動かして、UIスレッドを邪魔しない機構を検討すべきではあるが、下記参考ページの方法が手っ取り早かったので採用。
https://www.ipentec.com/document/csharp-wpf-implement-application-doevents

下記、プログレスバーの更新を例に解決方法を説明。

実行結果

progress.gif

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);
            }
        }
    }
}

2
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2