C#のGraphicsクラスを用いて画像をフェードインで表示させよとしています。
まず以下のコードをごらんください。
ただ、フェードインで画像は表示されるものの、フォームの操作が一切できなくなってしまいます。
そのためマルチスレッドにしようとしたのですが
using System;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
using System.Web;
using System.Net;
using System.Text;
using System.Threading;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Diagnostics;
public class MainClass{
public static void Main(string [] args){
NewForm formObj = new NewForm();
Application .Run(formObj);
}
}
public class NewForm : Form {
//インスタンス変数の宣言
public Graphics g ;
public Bitmap mapObj ;
public Image imageObj;
public ImageAttributes ia;
public ColorMatrix cm;
public Thread th ;
public ParameterizedThreadStart ts;
public PaintEventArgs e;
public Rectangle rec;
public int flag = 0;
public delegate void TestDelegate();
public TestDelegate deleObj;
public NewForm(){
Button buttonObj = new Button();
buttonObj.Width=100;
buttonObj.Height = 30;
//フェードさせるためのイベント発行用ボタンの設置
buttonObj.Click += new EventHandler(this.SetMethod);
this.Controls.Add(buttonObj);
}
public void SetMethod(object sender , EventArgs e){
this.Paint += new PaintEventHandler(this.ThreadMethod);
//フォームコントロールの再描画を促す
this.Invalidate();
}
public void ThreadMethod(object sender ,PaintEventArgs eventObj){
this.ts = new ParameterizedThreadStart(this.ThreadRenderMethod);
this.th = new Thread(this.ts);
this.th.Start(eventObj);
MessageBox.Show("ThreadMethod実行後");
MessageBox.Show(InvokeRequired.ToString());
this.th.Join();
}
public void ThreadRenderMethod(object paintObj){
MessageBox.Show(InvokeRequired.ToString());
this.deleObj =delegate(){
//無現ループしてしまうので、再描画イベント後イベントハンドラーを削除
this.Paint -= new PaintEventHandler(this.ThreadMethod);
PaintEventArgs e = (PaintEventArgs)paintObj;
try{
Console.WriteLine("paint メソッド発生");
this. g = e.Graphics;
this.mapObj = new Bitmap(this.Width,this.Height);
this.imageObj = Image.FromFile("C:\\c#\\test.jpg");
//this.g = Graphics.FromImage(this.mapObj);
this.cm = new ColorMatrix();
this.cm.Matrix00 = 1;
this.cm.Matrix11 = 1;
this.cm.Matrix22 = 1;
this.cm.Matrix33 = 0.0F;
this.cm.Matrix44 = 1;
this.ia = new ImageAttributes();
this.ia.SetColorMatrix(this.cm);
this.rec = new Rectangle(0, 0, this.Width, this.Height);
this.g.DrawImage(this.imageObj,rec,0,0,this.imageObj.Width,imageObj.Height,GraphicsUnit.Pixel,this.ia);
this.BackgroundImage = mapObj;
for(double i = 0.0; i <= 1.0; i = i + 0.001){
this.cm.Matrix33 = (float) i;
this.ia.SetColorMatrix(this.cm);
this.g.DrawImage(this.imageObj,rec,0,0,this.imageObj.Width,imageObj.Height,GraphicsUnit.Pixel,this.ia);
this.BackgroundImage = this.mapObj;
Thread.Sleep(100);
}
//this.imageObj.Dispose();
//this.g.Dispose();
}catch(Exception ex){
Console.WriteLine(ex.ToString());
}
Console.WriteLine("paint end");
};
this.deleObj();
//this.Invoke(this.deleObj);
this.BackgroundImage = Image.FromFile("C:\\c#\\test.jpg");
}
}
上記コードでも、フェードは動作するものの、やはりフォームの操作ができなくなります。
どう対処したらよいでしょうか?
識者の方、よろしくご教授ください
お願いいたします
No.3ベストアンサー
- 回答日時:
> ただ今回はタイマーで定期的に描画させましたが
> これはやはり別スレッドよりタイマーのほうがいいのでしょうかね?
長時間処理には,タイマーが向く用途とスレッドが向く用途があります。
・UIの変化に「時間」というファクターがあるものは,タイマーが向く (非UIスレッドはUIを変化させることが出来ないから)
・UIとは関係なく処理に時間がかかるのであれば,スレッドが向く (タイマーは無駄に処理を細切れにするだけ。さらに,非CPU原因で止まる可能性がある)
今回のフェードインやフェードアウトは,まさしく「UIの変化に時間というファクターがあるもの」なので,タイマーが向きます。
逆に,例えばファイルの圧縮などの処理はUIとは関係ないのでスレッドが向きます (無理をすればタイマーでもできなくないですが……)。
ネットワーク処理などは,
・非同期I/O (HttpWebRequest.BeginGetResponse/EndGetResponseなど) を使う
・スレッドを用意して同期処理する
などができますが,タイマーを使うことはできません (システムメソッドの呼び出し自体に時間がかかっているから)。
もちろん,両方を混ぜることもできます。
アニメーションなどは,UI自体はタイマーで描画させて,裏のスレッドで次々とフレームを生成しておく,などの方法がとれるでしょう。
> ただ、謎なのが、Grahpicsオブジェクトを this.CreateGraphics();
> という形で生成しているんですよね・・・・。
作りの問題ですね。
CreateGraphicsはあまり使うことがないと思いますが……。
・ボタンを押したとき (=初期設定)
1. ボタンのEnabledをfalseにする
2. RenderingMethodの
> this.imageObj = Image.FromFile("C:\\c#\\test.jpg");
から
> this.ia.SetColorMatrix(this.cm);
を実行する。ただし,
> this.cm.Matrix33 = this.splitTime;
は不要。
# this.Width/this.Heightよりもthis.ClientSize.Widthとthis.ClientSize.Heightの方がやりたいことのような気がしますが……。
3. タイマーを起動する (Enabled = true)
4. this.Refreshで再描画
・Timer.Tickイベント (=時間経過に伴う処理)
1. this.splitTimeの再設定
2. this.splitTimeを確認して,必要時間が経過していたらタイマーのEnabledをfalseにしてタイマーを止めて,ボタンのEnabledをtrueにして,イベントハンドラから出る
3. this.cm.Matrix33の再設定
4. this.Refreshで再描画
・Paintイベント (=描画のみを行う)
1. 画像が用意されていれば,描画する
と,役割分担をちゃんとすれば,おかしなことにはなりません。
# タイマーイベントでCreateGraphics呼び出して直接描画しても良いですが,これだと別ウィンドウに隠れた後に再度表示された場合などの再描画がなされません。
No.2
- 回答日時:
ソースが難読でしたので想像で。
Paint でInvalidate を呼んだら、再帰無限ループとはいかなくても再描画(WM_PAINT) の嵐になりそうですね。
それに・・・マルチスレッドになってないような。デリゲート無しにバックグランドスレッドから
this.BackgroundImage = this.mapObj;
を行えば、普通は例外になるはず(たぶん)。
ちゃんとバックグラウンドスレッドでフェードアウト処理を行い、デリゲートでもってメインスレッドで
this.BackgroundImage = this.mapObj;
を呼べばいいと思います。そこでInvalidate も呼ぶと。
非常にトリッキーな操作でデッドロックしちゃってるみたいですね。
/*********************************************
画像をフェードインさせるための処理
*********************************************/
using System;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
using System.Web;
using System.Net;
using System.Text;
using System.Threading;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Diagnostics;
public class MainClass{
public static void Main(string [] args){
NewForm formObj = new NewForm();
Application .Run(formObj);
}
}
public class NewForm : Form {
//インスタンス変数の宣言
public Graphics g ;
public Bitmap mapObj ;
public Image imageObj;
public ImageAttributes ia;
public ColorMatrix cm;
public Thread th ;
public ParameterizedThreadStart ts;
public PaintEventArgs eventE;
public Rectangle rec;
public int flag = 0;
public float splitTime = 0.0F;
//delegate
public delegate void TestDelegate();
public TestDelegate deleObj;
//指定秒間ごとにフェードさせるためのタイマーオブジェクト
public System.Windows.Forms.Timer timerObj ;
public NewForm(){
Button buttonObj = new Button();
buttonObj.Width=100;
buttonObj.Height = 30;
//フェードさせるためのイベント発行用ボタンの設置
buttonObj.Click += new EventHandler(this.SetMethod);
this.Controls.Add(buttonObj);
//タイマーオブジェクトの作成
this.timerObj = new System.Windows.Forms.Timer();
}
public void SetMethod(object sender , EventArgs e){
this.g = null;
this.splitTime = 0.0F;
this.Paint += new PaintEventHandler(this.SetTimerEventMthod);
//フォームコントロールの再描画を促す
this.Invalidate();
Console.WriteLine("SetMethod終了");
}
//描画が開始されたらセットされるタイマーイベント
public void SetTimerEventMthod(Object sender,PaintEventArgs e){
Console.WriteLine("SetTimerEventMthod開始");
this.Paint -= new PaintEventHandler(this.SetTimerEventMthod);
this.eventE =e;
//タイマーイベントの作成
this.timerObj.Tick += new EventHandler(this.RenderingMethod);
this.timerObj.Interval =100;
this.timerObj.Start();
Console.WriteLine("SetTimerEventMthod終了");
}
public void RenderingMethod(Object sender,EventArgs e){
Console.WriteLine("RenderingMethod開始");
try{
Console.WriteLine("paint メソッド発生");
this. g = this.CreateGraphics();
Console.WriteLine("gオブジェクト取得");
this.mapObj = new Bitmap(this.Width,this.Height);
this.imageObj = Image.FromFile("C:\\c#\\test.jpg");
this.cm = new ColorMatrix();
this.cm.Matrix00 = 1;
this.cm.Matrix11 = 1;
this.cm.Matrix22 = 1;
this.cm.Matrix33 = 0.0F;
this.cm.Matrix44 = 1;
this.ia = new ImageAttributes();
this.cm.Matrix33 = this.splitTime;
this.ia.SetColorMatrix(this.cm);
this.rec = new Rectangle(0, 0, this.Width, this.Height);
this.g.DrawImage(this.imageObj,rec,0,0,this.imageObj.Width,imageObj.Height,GraphicsUnit.Pixel,this.ia);
Console.WriteLine("最初描画終了");
this.splitTime += 0.001F;
Console.WriteLine(this.splitTime);
if(this.splitTime >1.0F){
this.timerObj.Tick -= new EventHandler(this.RenderingMethod);
this.timerObj.Stop();
}
}catch(Exception ex){
Console.WriteLine(ex.ToString());
Console.WriteLine(ex.StackTrace);
}
Console.WriteLine("paint end");
}
}
ただ、謎なのが、Grahpicsオブジェクトを this.CreateGraphics();
という形で生成しているんですよね・・・・。
PaintEventArgs e の
Graphics g = e.Graphics;
というペイントイベント引数から生成するのと違いがわからないんですよね:・・・・
・・・なぞです。
No.1
- 回答日時:
UIスレッドでJoinしてはいけません。
UIスレッドは,Workerスレッドに「何かやれ」とだけ伝えて,そのまま処理を終えるのが基本的な作法です。
さらに,UIスレッド以外からUIを操作してはいけません。
今回の場合,単にタイマーを使って100msごとに再描画させるだけで十分でしょう。
System.Windows.Forms.TimerはUIスレッドで動作しますから,クロススレッド呼び出しの問題も出てきません。
なお,スレッドを使うならばせめてasync/awaitを使うか,
TaskやBackgroundWorker,Delegate.BeginInvokeといったワーカースレッドを利用する物を使った方がよいでしょう。
Threadクラス自体を使ってスレッドを作成する必要は,.NET Framework 1.0の時代からほとんど存在しません。
ありがとうございます。
/*********************************************
画像をフェードインさせるための処理
*********************************************/
using System;
using System.IO;
using System.Windows.Forms;
using System.Drawing;
using System.Web;
using System.Net;
using System.Text;
using System.Threading;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Diagnostics;
public class MainClass{
public static void Main(string [] args){
NewForm formObj = new NewForm();
Application .Run(formObj);
}
}
public class NewForm : Form {
//インスタンス変数の宣言
public Graphics g ;
public Bitmap mapObj ;
public Image imageObj;
public ImageAttributes ia;
public ColorMatrix cm;
public Thread th ;
public ParameterizedThreadStart ts;
public PaintEventArgs eventE;
public Rectangle rec;
public int flag = 0;
public float splitTime = 0.0F;
//delegate
public delegate void TestDelegate();
public TestDelegate deleObj;
//指定秒間ごとにフェードさせるためのタイマーオブジェクト
public System.Windows.Forms.Timer timerObj ;
public NewForm(){
Button buttonObj = new Button();
buttonObj.Width=100;
buttonObj.Height = 30;
//フェードさせるためのイベント発行用ボタンの設置
buttonObj.Click += new EventHandler(this.SetMethod);
this.Controls.Add(buttonObj);
//タイマーオブジェクトの作成
this.timerObj = new System.Windows.Forms.Timer();
}
public void SetMethod(object sender , EventArgs e){
this.g = null;
this.splitTime = 0.0F;
this.Paint += new PaintEventHandler(this.SetTimerEventMthod);
//フォームコントロールの再描画を促す
this.Invalidate();
Console.WriteLine("SetMethod終了");
}
//描画が開始されたらセットされるタイマーイベント
public void SetTimerEventMthod(Object sender,PaintEventArgs e){
Console.WriteLine("SetTimerEventMthod開始");
this.Paint -= new PaintEventHandler(this.SetTimerEventMthod);
this.eventE =e;
//タイマーイベントの作成
this.timerObj.Tick += new EventHandler(this.RenderingMethod);
this.timerObj.Interval =100;
this.timerObj.Start();
Console.WriteLine("SetTimerEventMthod終了");
}
public void RenderingMethod(Object sender,EventArgs e){
Console.WriteLine("RenderingMethod開始");
try{
Console.WriteLine("paint メソッド発生");
this. g = this.CreateGraphics();
Console.WriteLine("gオブジェクト取得");
this.mapObj = new Bitmap(this.Width,this.Height);
this.imageObj = Image.FromFile("C:\\c#\\test.jpg");
this.cm = new ColorMatrix();
this.cm.Matrix00 = 1;
this.cm.Matrix11 = 1;
this.cm.Matrix22 = 1;
this.cm.Matrix33 = 0.0F;
this.cm.Matrix44 = 1;
this.ia = new ImageAttributes();
this.cm.Matrix33 = this.splitTime;
this.ia.SetColorMatrix(this.cm);
this.rec = new Rectangle(0, 0, this.Width, this.Height);
this.g.DrawImage(this.imageObj,rec,0,0,this.imageObj.Width,imageObj.Height,GraphicsUnit.Pixel,this.ia);
Console.WriteLine("最初描画終了");
this.splitTime += 0.001F;
Console.WriteLine(this.splitTime);
if(this.splitTime >1.0F){
this.timerObj.Tick -= new EventHandler(this.RenderingMethod);
this.timerObj.Stop();
}
}catch(Exception ex){
Console.WriteLine(ex.ToString());
Console.WriteLine(ex.StackTrace);
}
Console.WriteLine("paint end");
}
}
とりあえず上記のようなコードでフェードイン画像がうごきました。
ただ今回はタイマーで定期的に描画させましたが
これはやはり別スレッドよりタイマーのほうがいいのでしょうかね?
さしあたり、これをある程度クラス化してちょっとしたゲーム・・・・つまりはかまいたちの夜的なサウンドノベルゲーム用の
背景として流用したいのですが。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・人生のプチ美学を教えてください!!
- ・10秒目をつむったら…
- ・あなたの習慣について教えてください!!
- ・牛、豚、鶏、どれか一つ食べられなくなるとしたら?
- ・【大喜利】【投稿~9/18】 おとぎ話『桃太郎』の知られざるエピソード
- ・街中で見かけて「グッときた人」の思い出
- ・「一気に最後まで読んだ」本、教えて下さい!
- ・幼稚園時代「何組」でしたか?
- ・激凹みから立ち直る方法
- ・1つだけ過去を変えられるとしたら?
- ・【あるあるbot連動企画】あるあるbotに投稿したけど採用されなかったあるある募集
- ・【あるあるbot連動企画】フォロワー20万人のアカウントであなたのあるあるを披露してみませんか?
- ・映画のエンドロール観る派?観ない派?
- ・海外旅行から帰ってきたら、まず何を食べる?
- ・誕生日にもらった意外なもの
- ・天使と悪魔選手権
- ・ちょっと先の未来クイズ第2問
- ・【大喜利】【投稿~9/7】 ロボットの住む世界で流行ってる罰ゲームとは?
- ・推しミネラルウォーターはありますか?
- ・都道府県穴埋めゲーム
- ・この人頭いいなと思ったエピソード
- ・準・究極の選択
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
VB.NET フォーム上に描いたグ...
-
Labelコントロールの背景をグラ...
-
株価チャートソフトを作るのに
-
JAVAでの背景画像表示
-
VB.NETのSendMessageを教えてく...
-
Javascriptのチャートライブラ...
-
C# リストビューの特定のセルの...
-
画面のちらつきの原因が知りた...
-
共有お絵かきシステムの同期の...
-
「タイプ初期化子が例外をスロ...
-
エクセルVBAで、条件に一致する...
-
変数名の付け方
-
インスタンス参照でアクセスで...
-
配列の重複する値とその個数を...
-
private static という変数の修飾
-
複数の変数を宣言する時、同時...
-
オブジェクト参照がオブジェク...
-
Java初級 引数に適用できません
-
パワーポイントのVBAでテキスト...
-
String.containsの反対機能はあ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C# リストビューの特定のセルの...
-
VB.NETのSendMessageを教えてく...
-
C# DataGridView のCellPaintin...
-
PDF帳票を作成するためのフリー...
-
JAVAでの背景画像表示
-
VB.NET フォーム上に描いたグ...
-
SwingとEDT(イベントディスパッ...
-
アクセスで他アプリから復帰し...
-
Labelコントロールの背景をグラ...
-
Androidのフォントサイズ(SP)と...
-
画面のちらつきの原因が知りた...
-
Canvas等の図形を移動する時,直...
-
複数画像表示切り替え Visual c++
-
javaでクイズ
-
Androidで画像の中で指定した範...
-
C#のGraphicsクラスについてです。
-
C# テキストボックスについて
-
描画してもウィンドウをリサイ...
-
VS Treeview のデザイン変更
-
GDI+で描画した画像を消去する...
おすすめ情報