私のアプリケーションでは、長い操作を実行し、操作の進行状況を表示したいと考えています。長時間の運用では、サードパーティの dll を使用します。残念ながら、その dll は非メイン スレッドからの呼び出しをサポートしていません。したがって、別のスレッドを使用してプロセスを開始することはできません。
Dispather を使用して、メイン スレッドの進行状況バーを更新する方法を見つけました。最初は単純な WPF アプリケーションを作成し、コード ビハインドで単純なメソッドを記述しました。
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
for (int i = 0; i <= 100; i++)
{
Dispatcher.Invoke(DispatcherPriority.Loaded,
(Action)(() =>
{
pb.Value = i;
}));
Thread.Sleep(10);
}
}
このコードは正常に動作します。ウィンドウに進行状況が表示されます。しかし、問題はMVVMを使用しているため、この方法を使用できません。
私の問題を解決するために、AttachedProperty を作成しました
internal class ProgressBarAttachedBehavior
{
public static readonly DependencyProperty ValueAsyncProperty =
DependencyProperty.RegisterAttached("ValueAsync",
typeof (double),
typeof (ProgressBarAttachedBehavior),
new UIPropertyMetadata(default(double), ValueAsyncChanged));
private static void ValueAsyncChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var pb =
d as ProgressBar;
if (pb == null)
{
return;
}
var dispatcher =
d.Dispatcher;
//if (dispatcher == null || dispatcher.CheckAccess())
//{
// pb.Value = (double) e.NewValue;
//}
//else
{
DispatcherFrame frame =
new DispatcherFrame(true);
var dispatcherOperation = dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() =>
{
pb.Value = (double)e.NewValue;
frame.Continue = false;
})
);
Dispatcher.PushFrame(frame);
}
}
public static void SetValueAsync(ProgressBar progressBar, double value)
{
progressBar.SetValue(ValueAsyncProperty, value);
}
public static double GetValueAsync(ProgressBar progressBar)
{
return (double)progressBar.GetValue(ValueAsyncProperty);
}
XAMLで私が書いた
<ProgressBar tesWpfAppMvvm:ProgressBarAttachedBehavior.ValueAsync="{Binding Progress}"/>
そして私のViewModelコード
class Workspace1ViewModel : WorkspaceViewModel
{
private ICommand _startCommand;
private double _progress;
public ICommand StartCommand
{
get
{
if (_startCommand == null)
{
_startCommand =
new RelayCommand(Start);
}
return _startCommand;
}
}
private void Start()
{
for (int i = 0; i <= 100; i++)
{
Progress = i;
Thread.Sleep(20);
}
}
public double Progress
{
get
{
return _progress;
}
set
{
_progress = value;
RaisePropertyChanged(() => Progress);
}
}
}
コードは正常に動作します。メインスレッドで長いプロセスが実行され、ウィンドウに進行状況が表示されます。
しかし、Active ViewModel を別のモデルに変更すると、エラーが発生するという問題があります。
Cannot perform this operation while dispatcher processing is suspended.
どこでも解決策を見つけようとしましたが、できませんでした。ソリューションはどこでも、別のスレッドでログ プロセスを実行します。
どこが間違っているのか、問題を解決する方法を教えてください。
問題を再現するデモ プロジェクトをここからダウンロードできます。