locked
.NET compact framework でのInvokeの実装方法について教えてください

    質問

  • いつもお世話になっております。

    今回、WebRequest.BeginGetResponse メソッド を利用して非同期でネットワークから画像データを取得し、それを既存のPanelクラスに貼り付けようとしています。

    最初は安直にHandleEventを使用して既存クラスにコールバックしたのですが、Pictureboxを貼り付けるときに以下のExeptionが発生しました。

    「別のスレッドで作成されたコントロールと対話するには、Control.Invoke を使用してください。」

    そこでInvokeを使用して処理を渡してあげようとしたのですが、MSDNのリファレンスに

    「.NET Compact Framework アプリケーションでは、デリゲートは EventHandler のインスタンスである必要があります。」

    とありますが、EventHandlerを呼び出し元&コールバック先でどのように宣言して、それをDelegateクラスとしてInvokeをコールすればいいのかいまひとつ理解できません。以下のように試したのですが、コンパイルは通るのですがなかなかコールバック先に通知が届きません。

      public event EventHandler ImageDownloaded; // 最初に実装したEventHandlerコールバック

      public delegate void myMethodDelegate(object sender, EventArgs e); // EventHandlerのつもりのDelegate宣言

      void RespCallback(IAsyncResult asynchronousResult) // WebRequest.BeginGetResponse メソッド のサンプルのコールバック
      {
       try
       { 
    ...
        // Read the response into a Stream object.
        Stream responseStream = myRequestState.response.GetResponseStream();
        myRequestState.streamResponse = responseStream;

        
         // 画像ファイルのときの処理

        String str = myRequestState.response.ContentType.ToLower();
        if (str.Contains("jpeg") || str.Contains("jpg"))
        {
        Delegate myDelegate = new myMethodDelegate(ImageDownloaded);
        myRequestState.Invoke(myDelegate); // myRequestStateはサンプルではただのクラスですがInvokeを使用するのでControlクラスを継承しています
        //ImageDownloaded(myRequestState, null); // 最初に試したHandleEventを使用したコールバック
         return;

        ......

     

    お分かりの方いましたら、ご教示いただければと思います。

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

    2011年1月8日 17:43

回答

  • wootanbo さん

    伊勢 シンと申します。

    > 「.NET Compact Framework アプリケーションでは、デリゲートは EventHandler のインスタンスである必要があります。」

    という記述ははじめて知りました。というのも、今まで以下のようなコードを記述して問題なく動作していたからです。
    (手っ取り早くWindows Mobile 6.5 エミュレータ上の .NET Compact Framework 3.5 で試しましたが、同じようなコードをWM6.0のCF2.0でも動かしていたはずです)

    private delegate void DownloadCompleted(Image image); 
    
    private void menuItem1_Click(object sender, EventArgs e)
    {
      WebRequest req = HttpWebRequest.Create("http://giraffe.iseteki.net/img/giraffe.png");
      req.BeginGetResponse(new AsyncCallback(GetResponseAction), req);
    }
    
    private void GetResponseAction(IAsyncResult ar)
    {
      try
      {
        HttpWebRequest req = (HttpWebRequest) ar.AsyncState;
        HttpWebResponse res = (HttpWebResponse) req.EndGetResponse(ar);
    
        Stream stream = res.GetResponseStream();
    
        Bitmap b = new Bitmap(stream);
    
        // this は Form なので、その Invoke をそのまま利用しています。
        this.Invoke(new DownloadCompleted(Completed), b);
      }
      catch (Exception)
      {
        throw;
      }
    }
    
    private void Completed(Image image)
    {
      pictureBox1.Image = image;
    }
    
    

     ただ、マニュアルに合わせるのであれば、event も delegate として扱うことができるので、示して頂いたコードは以下のようにすればよいのではないでしょうか。

    public event EventHandler ImageDownloaded;
    
    // (snip)
    
    String str = myRequestState.response.ContentType.ToLower();
    if (str.Contains("jpeg") || str.Contains("jpg"))
    {
      // EventHandler 型の引数に合うよう、追加のパラメータを指定する
      myRequestState.Invoke(ImageDownloaded, this, EventArgs.Empty);
      return;
    }
    
    

    なお、環境は多種多様ですので、対象プラットフォーム(WM5、WM6 など)とCompact Frameworkのバージョンを書いておいて頂けるとより適切なアドバイスができると思いますので、今後質問されるときは表記いただけますようお願いします。

    以上、参考になれば幸いです。

    • 回答としてマーク wootanbo 2011年1月15日 9:03
    2011年1月9日 2:29

すべての返信

  • Compact Framework での開発経験はありませんので、論理的に気になるところだけ指摘します。

    以下のように試したのですが、コンパイルは通るのですがなかなかコールバック先に通知が届きません。

    「なかなか」と表現されるとわかりづらいと感じました。
    待っていればくるのでしょうか?待っていてもこないのでしょうか?

      public delegate void myMethodDelegate(object sender, EventArgs e); // EventHandlerのつもりのDelegate宣言

    なぜ、これが必要なのでしょうか?

        Delegate myDelegate = new myMethodDelegate(ImageDownloaded);

    なぜ、new EventHandler(ImageDownloaded) と書いていないのでしょうか。
    EventHandler である必要があると書かれていたのですよね?

    メソッドの戻り値が void、引数が object, EventArgs であれば EventHandler デリゲートとできますよ。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年1月9日 1:08
  • wootanbo さん

    伊勢 シンと申します。

    > 「.NET Compact Framework アプリケーションでは、デリゲートは EventHandler のインスタンスである必要があります。」

    という記述ははじめて知りました。というのも、今まで以下のようなコードを記述して問題なく動作していたからです。
    (手っ取り早くWindows Mobile 6.5 エミュレータ上の .NET Compact Framework 3.5 で試しましたが、同じようなコードをWM6.0のCF2.0でも動かしていたはずです)

    private delegate void DownloadCompleted(Image image); 
    
    private void menuItem1_Click(object sender, EventArgs e)
    {
      WebRequest req = HttpWebRequest.Create("http://giraffe.iseteki.net/img/giraffe.png");
      req.BeginGetResponse(new AsyncCallback(GetResponseAction), req);
    }
    
    private void GetResponseAction(IAsyncResult ar)
    {
      try
      {
        HttpWebRequest req = (HttpWebRequest) ar.AsyncState;
        HttpWebResponse res = (HttpWebResponse) req.EndGetResponse(ar);
    
        Stream stream = res.GetResponseStream();
    
        Bitmap b = new Bitmap(stream);
    
        // this は Form なので、その Invoke をそのまま利用しています。
        this.Invoke(new DownloadCompleted(Completed), b);
      }
      catch (Exception)
      {
        throw;
      }
    }
    
    private void Completed(Image image)
    {
      pictureBox1.Image = image;
    }
    
    

     ただ、マニュアルに合わせるのであれば、event も delegate として扱うことができるので、示して頂いたコードは以下のようにすればよいのではないでしょうか。

    public event EventHandler ImageDownloaded;
    
    // (snip)
    
    String str = myRequestState.response.ContentType.ToLower();
    if (str.Contains("jpeg") || str.Contains("jpg"))
    {
      // EventHandler 型の引数に合うよう、追加のパラメータを指定する
      myRequestState.Invoke(ImageDownloaded, this, EventArgs.Empty);
      return;
    }
    
    

    なお、環境は多種多様ですので、対象プラットフォーム(WM5、WM6 など)とCompact Frameworkのバージョンを書いておいて頂けるとより適切なアドバイスができると思いますので、今後質問されるときは表記いただけますようお願いします。

    以上、参考になれば幸いです。

    • 回答としてマーク wootanbo 2011年1月15日 9:03
    2011年1月9日 2:29
  • 早速のご回答ありがとうございます。

      メソッドの戻り値が void、引数が object, EventArgs であれば EventHandler デリゲートとできますよ

    これは知りませんでした。ということは結構無駄な処理をしていたということですね。

    ちょっと見直してみたいと思います。

    2011年1月15日 8:50
  • ご返信ありがとうございます。開発ターゲットはcompact framework3.5のWM6.1をターゲットとしております。

    やろうとしていたことがまさに提示していただいた内容と一緒です!

    ただし、ネットワーク関連の処理が別のクラスとして定義してある所が異なる点になります。こちらを反映してもうまく結果を受け取ることができませんでした。

    もしかするとコールバックで帰ってきたときのスレッドが別の(ネットワークアクセス用の)クラスで生成されたスレッドなので、そこから既存クラスのオブジェクトへのアクセスができないということなのかもしれませんね。(ExeptionではInvoke を使用してくださいといっていますが)

    ちょっと設計から見直してみようと思います。

    ありがとうございました

    2011年1月15日 9:03