2017-08-04 21 views
-2

テキストボックスのテキストが既存のファイルを表す場合、カスタムイベントを呼び出すTextChangedイベントのTextBoxがあります。このイベントでは、ファイルに対して何らかの処理を行う外部dllへの呼び出しがあります。これは完了までに1分以上かかります。このメソッドが私に返す結果に依存して、私が行ういくつかの後処理もあります。現在、これはUIをブロックしていますが、これは非常に望ましくありません。UIをブロックせずに続行する前に結果を待つ

基本的に2つの「オプション」/シナリオがあります。

  1. カスタムイベント内では、何らかの形で、UIをフリーにしたままでイベントを続行する前に、dll呼び出しが完了するのを待ちます。これは私のマルチスレッド化されていない自己からの最も簡単なアイデアのようですが、概念的には私には赤い旗が投げられます:カスタムイベント自体(TextChangedから呼び出されます)がUIスレッドにあることを考えれば可能ですか?
  2. Task.Run()を使用してカスタムイベント全体を独自のスレッドにスローします。ここでの欠点は、dllメソッドの呼び出しとは別に、longメソッドの後にgetter/setterの影響を受けるUI要素がかなりあることです。私は適切なInvokeRequiredに基づいて交互ゲッター/セッターを書くことができましたが、これを行うより正しい方法があれば、私はむしろそのアプローチを取っていきたいと思います。

    public partial class Form1 : Form 
    { 
        public Form1() 
        { 
         InitializeComponent(); 
    
         comboBox1.Items.Add("Select One..."); 
         comboBox1.Items.Add("Item 1"); 
         comboBox1.Items.Add("Item 2"); 
    
         Value = 0; 
        } 
    
        public string SetMessage 
        { 
         set 
         { 
          if (lblInfo.InvokeRequired) 
           lblInfo.BeginInvoke((MethodInvoker)delegate() { lblInfo.Text = Important ? value + "!" : value; }); 
          else 
           lblInfo.Text = Important ? value + "!" : value; 
         } 
        } 
    
        public bool Important 
        { 
         get 
         { 
          return chkImportant.Checked; 
         } 
         set 
         { 
          if (chkImportant.InvokeRequired) 
           chkImportant.BeginInvoke((MethodInvoker) delegate() { chkImportant.Checked = value; }); 
          else 
           chkImportant.Checked = value; 
         } 
        } 
    
        public SomeValue Value 
        { 
         get 
         { 
          if (comboBox1.InvokeRequired) 
          { 
           SomeValue v = (SomeValue)comboBox1.Invoke(new Func<SomeValue>(() => SomeValue.Bar)); 
           return v; 
          } 
          else 
          { 
           switch (comboBox1.SelectedIndex) 
           { 
            case 1: 
             return SomeValue.Foo; 
            case 2: 
             return SomeValue.Bar; 
            default: 
             return SomeValue.Nothing; 
           } 
          } 
         } 
         set 
         { 
          if (comboBox1.InvokeRequired) 
          { 
           comboBox1.BeginInvoke((MethodInvoker)delegate() 
           { 
            switch (value) 
            { 
             case SomeValue.Nothing: 
              comboBox1.SelectedIndex = 0; 
              break; 
             case SomeValue.Foo: 
              comboBox1.SelectedIndex = 1; 
              break; 
             case SomeValue.Bar: 
              comboBox1.SelectedIndex = 2; 
              break; 
            } 
           }); 
          } 
          else 
          { 
           switch (value) 
           { 
            case SomeValue.Nothing: 
             comboBox1.SelectedIndex = 0; 
             break; 
            case SomeValue.Foo: 
             comboBox1.SelectedIndex = 1; 
             break; 
            case SomeValue.Bar: 
             comboBox1.SelectedIndex = 2; 
             break; 
           } 
          } 
         } 
        } 
    
        private void CustomEvent(object sender, EventArgs e) 
        { 
         if (!Important) 
          Important = true; 
    
         SetMessage = "Doing some stuff"; 
    
         if (Value == SomeValue.Foo) 
          Debug.WriteLine("Foo selected"); 
    
         //I don't want to continue until a result is returned, 
         //but I don't want to block UI either. 
         if (ReturnsTrueEventually()) 
         { 
    
          Debug.WriteLine("True!"); 
         } 
    
         Important = false; 
         SetMessage = "Finished."; 
        } 
    
        public bool ReturnsTrueEventually() 
        { 
         //Simulates some long running method call in a dll. 
         //In reality, I would interpret an integer and return 
         //an appropriate T/F value based on it. 
         Thread.Sleep(5000); 
    
         return true; 
        } 
    
        private void textBox1_TextChanged(object sender, EventArgs e) 
        { 
         //Do I *need* to multithread the whole thing? 
         Task.Run(() => CustomEvent(this, new EventArgs())); 
        } 
    } 
    
    public enum SomeValue 
    { 
        Nothing = 0, 
        Foo = 100, 
        Bar = 200 
    } 
    

    を::

は、私が上からオプション2を使用して、私が後だものを基本的に示してはるかに短い(不自然ではあるが)サンプルプロジェクトを、作られた、私が求めていませんよ私のオプション2コードのコードレビュー。むしろ、オプション2を実行する必要があるかどうかを尋ねています。そのオプションは、プロセス全体を保持するメソッドが1つしかないので、コードのかなりの部分を変更するためです。

また、レプリケーションを防ぐためにこれらのプロパティのコードの一部を簡略化することもできます。自分自身にデモンストレーションし、デバッグするために、私はこの時点でそれを延期しています。ここで

が、私はオプション1(その呼び出すことなく、重複したコードとゲッター/セッターを左)に関連していたものです:

private async void CustomEvent(object sender, EventArgs e) 
{ 
    if (!Important) 
     Important = true; 

    SetMessage = "Doing some stuff"; 

    if (Value == SomeValue.Foo) 
     Debug.WriteLine("Foo selected"); 

    //I don't want to continue until a result is returned, 
    //but I don't want to block UI either. 
    if (await ReturnsTrueEventually()) 
    { 

     Debug.WriteLine("True!"); 
    } 

    Important = false; 
    SetMessage = "Finished."; 
} 

public async Task<bool> ReturnsTrueEventually() 
{ 
    //Simulates some long running method call in a dll. 
    //In reality, I would interpret an integer and 
    //return an appropriate T/F value based on it. 
    Thread.Sleep(5000); 

    return true; 
} 
+0

2つのオプションを比較する場合は、オプション1を書き留めて、その違いを確認してください。 – Servy

+0

async/awaitとtasksを使用してUIを応答可能にします。 .NET Frameworkのどのバージョンを使用していますか? – Dido

+0

@Servy - 私がそれをしたとき、私は基本的に、私が終わりに戻るのを待っていることを思い出しました。私は正しく理解すれば、とにかくブロックしてしまいます。もちろん、私は完全に間違ったアプローチを取っている可能性があることを実現します... –

答えて

0

これは、あなたが望むものは基本的です。私はここでいくつかのベストプラクティスに違反していますが、それはそれほど複雑ではありません。注意すべき点の1つは、ユーザーがこのボタンを複数回連続してクリックできることです。処理する前に無効にすることを検討することもできます。または、Monitor.TryEnter()を実行して、まだ実行されていないことを確認することもできます。

private async void buttonProcess_Click(object sender, RoutedEventArgs e) 
    { 
     textBlockStatus.Text = "Processing..."; 
     bool processed = await Task.Run(() => SlowRunningTask()); 
    } 

    private bool SlowRunningTask() 
    { 
     Thread.Sleep(5000); 
     return true; 
    } 
+0

イエス...私はこれを試したが、Task.Runの前に待っていたことを忘れていたと確信している(または他の場所に置いた、私は覚えていない)。私は今かなり面倒です。 –

+0

あなたが待っているのはTask.Run()ではなく、戻り値です。この場合はTask です。したがって、あなたは 'Task processedTask = Task.Run(...);'を実行し、後で結果が必要なときに 'bool processed = await processedTask;'を実行することができます。 – zzxyz

関連する問題