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

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

ただいまの
回答率

88.03%

WPFでawait Task.RunにしたらSTAエラーになっています

受付中

回答 2

投稿

  • 評価
  • クリップ 2
  • VIEW 10K+

score 249

こんにちは。 
Windows10でWPFのアプリケーションを開発しています。 
Visual Studio 2015 Communityを使っています。 

前提・実現したいこと

重たい処理の時にProgressBarを表示したいです。

試したこと

xamlでprogressbarを配置し、ボタンの処理にvisible/hidden処理を追加、重たい処理をawait Task.Runで囲みました。

await Task.Run(() =>
{
});
を外せば問題なく動作します。

public static Window CreateView<T>(T viewModel)
public static bool? ShowModalWindow<T>(T viewModel)
private async void outputExamResultAsync()
をすべて        [STAThread]
にしても、同じエラーです。

発生している問題・エラーメッセージ

型 'System.Reflection.TargetInvocationException' のハンドルされていない例外が mscorlib.dll で発生しました
{"呼び出しスレッドは、多数の UI コンポーネントが必要としているため、STA である必要があります。"}

該当のソースコード

        <Border Width="300" Height="100" HorizontalAlignment="Center" VerticalAlignment="Center" Background="LightGray" Visibility="{Binding IndicatorVisibility}">
            <Grid>
                <ProgressBar IsIndeterminate="True"/>
            </Grid>
        </Border>
        private Visibility indicatorVisibility = Visibility.Hidden;
        public Visibility IndicatorVisibility
        {
            get { return indicatorVisibility; }
            set
            {
                indicatorVisibility = value;
                RaisePropertyChanged("IndicatorVisibility");
            }
        }
        private async void outputExamResultAsync()
        {
            IndicatorVisibility = Visibility.Visible;

            await Task.Run(() =>
            {
                ResultWindowViewModel resultWindowViewModel = new ResultWindowViewModel();
                resultWindowViewModel.重たい処理();
                LocatorClass.ShowModalWindow(resultWindowViewModel);
            });

            IndicatorVisibility = Visibility.Hidden;
        }
LocatorClass
        public static bool? ShowModalWindow<T>(T viewModel)
        {
            var window = CreateView(viewModel);
            if (window == null)
            {
                return null;
            }

            Application.Current.MainWindow.IsEnabled = false;

            window.Owner = Application.Current.MainWindow;
            window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
            var result = window.ShowDialog();

            Application.Current.MainWindow.IsEnabled = true;
            return result;
        }

        public static Window CreateView<T>(T viewModel)
        {
               // View を生成し、DataContext に ViewModel を設定する
               Type viewType = ViewModels[viewModel.GetType()];
               Window window = Activator.CreateInstance(viewType) as Window; //<=この行でエラー。
               return window;
        }

補足情報(言語/FW/ツール等のバージョンなど)

Microsoft Visual Studio Community 2015 
Version 14.0.25424.00 Update 3 
Microsoft .NET Framework 
Version 4.6.01038 

インストールしているバージョン:Community 

Visual C# 2015   00322-20000-00000-AA575 
Microsoft Visual C# 2015 

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

+2

基本は、絶対にUIの処理はUIのスレッドで行わなくてはならない。
そのため、UIの処理を他スレッドで行わない。そのため、taskの外に出せばいい。

    private async void outputExamResultAsync()
        {
            IndicatorVisibility = Visibility.Visible;

            var result = await Task.Run<ResultWindowViewModel>(() =>
            {
                ResultWindowViewModel resultWindowViewModel = new ResultWindowViewModel();
                resultWindowViewModel.重たい処理();
return resultWindowViewModel                
            });
            LocatorClass.ShowModalWindow(result);
            IndicatorVisibility = Visibility.Hidden;
        }

大体は、そのパターンでできるが、たまにできない時があって、
その時は、UIスレッドを指定して、UIスレッドで実行するということをすればいい。
いろいろと面倒なので、
GalaSoft.MvvmLight.Threading.DispatcherHelper.CheckBeginInvokeOnUI(() => { });
を使うと楽。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/14 13:05

    いつもありがとうございます。
    まだ
    var result = window.ShowDialog();
    でthreadちがうとエラーです。

    GalaSoftはNuGet必要でしょうか。今回はNuGet禁止で…。

    キャンセル

  • 2017/06/14 14:58

    別に大したことやっていないので、コピペでいいのでは。https://github.com/asimmon/mvvmlight/blob/a506c22781d98c3371a2e8de525aa2410c29b6e3/GalaSoft.MvvmLight/GalaSoft.MvvmLight.Platform%20(NET45)/Threading/DispatcherHelper.cs 複数の環境対応のコードは消さないとだめだろうけど。

    キャンセル

  • 2017/06/14 16:39

    ありがとうございます。
    ちょっとコードが長く、内容のわからないブラックボックスは入れたくないので、これを使うのはまた次回。
    結局今回は、できずに時間切れで終了になりました。

    キャンセル

0

<Window x:Class="pbartest1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:pbartest1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ProgressBar Name="p" HorizontalAlignment="Center" Height="30" VerticalAlignment="Center" Width="300" Margin="105,97,112,192"/>
        <Button Content="スタート" HorizontalAlignment="Left" Margin="220,193,0,0" VerticalAlignment="Top" Width="75" Height="30" Click="Heaby_Task"/>
    </Grid>
</Window>
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Threading;
using System.Windows.Threading;

namespace pbartest1
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += Check_Count;
        }

        DispatcherTimer timer;

        void Check_Count(object sender, EventArgs e)
        {
            p.Value = p_count;

            if (p.Value == p.Maximum)
            {
                MessageBox.Show("作業終了");
                p_count = 0;
                p.Value = 0;
                timer.Stop();
            }
        }

        public int p_count = 0;

        private async void Heaby_Task(object sender, RoutedEventArgs e)
        {
            int i = 5;
            p.Maximum = i;

            timer.Start();

            await Task.Run(() => {
                for (int n = 0; n < i; n++)
                {
                    System.Threading.Thread.Sleep(4000);
                    p_count++;
                }
            });
        }
    }
}


プログラミング初心者の私の使っている方法です。
カウントを外において、タイマーを使って1秒ごとに確かめて
プログレスバーの値に反映させるというものです。
(質問者様の場合でも正確に動くかどうかは分かりません。)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/06/14 16:40

    コメントありがとうございます。
    タイマーも考えたのですが、変更が多いので今回はなしで。
    すみません。

    キャンセル

  • 2017/06/14 17:46

    いえいえ。ご丁寧にありがとうございます。

    キャンセル

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

  • ただいまの回答率 88.03%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る