2

私のアプリケーションでは、長い操作を実行し、操作の進行状況を表示したいと考えています。長時間の運用では、サードパーティの 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.

どこでも解決策を見つけようとしましたが、できませんでした。ソリューションはどこでも、別のスレッドでログ プロセスを実行します。

どこが間違っているのか、問題を解決する方法を教えてください。

問題を再現するデモ プロジェクトをここからダウンロードできます。

4

3 に答える 3

0

同じ例外でこのスレッドを確認できますか? ドキュメントに従って、「ValueAsyncChanged」イベント ハンドラーを別のデリゲートでラップし、Dispatcher.BeginInvoke メソッドで「ValueAsyncChanged」を呼び出すことができます。WPF エンジンは、読み込み中は PushFrame 呼び出しを実行できないようです。

    public static readonly DependencyProperty ValueAsyncProperty =
    DependencyProperty.RegisterAttached("ValueAsync", 
        typeof (double), 
        typeof (ProgressBarAttachedBehavior), 

        new UIPropertyMetadata(default(double),
(o, e) => 
Dispatcher.BeginInvoke( 
new DependencyPropertyChangedEventHandler( ValueAsyncChanged), o, e);));
于 2013-06-08T14:33:45.213 に答える
0

プログレスバーの可視性を設定し、UI スレッドがその状態を更新するのに十分な時間を提供します。

ノート:

UI スレッドがスリープ状態から復帰すると、UI を集中的に使用するプロセスが実行されるため、アプリケーションは応答しなくなります。

UI を集中的に使用するプロセスが完了すると、アプリケーションは再び応答するようになります。

以下はコード例です。

XAML:

<telerik:RadProgressBar x:Name="progress"  
Visibility="{Binding ProgressVisibility, Mode=OneWay}" IsIndeterminate="True"  

ビューモデル:

const int MINIMUM_UI_WAIT_REQUIRED = 2;

ProgressVisibility = Visibility.Visible;
await Task.Factory.StartNew(() => { Thread.Sleep(MINIMUM_UI_WAIT_REQUIRED); });
于 2014-10-29T17:02:57.417 に答える