
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を探す
今、見られている記事はコレ!
-
弁護士が解説!あなたの声を行政に届ける「パブリックコメント」制度のすべて
社会に対する意見や不満、疑問。それを発信する場所は、SNSやブログ、そしてニュースサイトのコメント欄など多岐にわたる。教えて!gooでも「ヤフコメ民について」というタイトルのトピックがあり、この投稿の通り、...
-
弁護士が語る「合法と違法を分けるオンラインカジノのシンプルな線引き」
「お金を賭けたら違法です」ーーこう答えたのは富士見坂法律事務所の井上義之弁護士。オンラインカジノが違法となるかどうかの基準は、このように非常にシンプルである。しかし2025年にはいって、違法賭博事件が相次...
-
釣りと密漁の違いは?知らなかったでは済まされない?事前にできることは?
知らなかったでは済まされないのが法律の世界であるが、全てを知ってから何かをするには少々手間がかかるし、最悪始めることすらできずに終わってしまうこともあり得る。教えてgooでも「釣りと密漁の境目はどこです...
-
カスハラとクレームの違いは?カスハラの法的責任は?企業がとるべき対応は?
東京都が、客からの迷惑行為などを称した「カスタマーハラスメント」、いわゆる「カスハラ」の防止を目的とした条例を、全国で初めて成立させた。条例に罰則はなく、2025年4月1日から施行される。 この動きは自治体...
-
なぜ批判コメントをするの?その心理と向き合い方をカウンセラーにきいた!
今や生活に必要不可欠となったインターネット。手軽に情報を得られるだけでなく、ネットを介したコミュニケーションも一般的となった。それと同時に顕在化しているのが、他者に対する辛らつな意見だ。ネットニュース...
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
Androidで画像の中で指定した範...
-
ドラッグして矩形を描くには
-
SwingとEDT(イベントディスパッ...
-
GDI+で描画した画像を消去する...
-
VB.NET フォーム上に描いたグ...
-
Canvas等の図形を移動する時,直...
-
C#のGraphicsクラスについてです。
-
Java Script 色を塗れるプログ...
-
Java、jPanelに描画する
-
C# でパネルのマウスイベントが...
-
JAVAでの背景画像表示
-
C# DataGridView のCellPaintin...
-
音声再生/SourceDataLineの遅延
-
変数名の付け方
-
「タイプ初期化子が例外をスロ...
-
private static という変数の修飾
-
エクセルVBAで、条件に一致する...
-
レコード件数の表示
-
「インスタンス」の意味をわか...
-
【Java】入力した西暦→和暦に変...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
VB.NET フォーム上に描いたグ...
-
Java、jPanelに描画する
-
VB.NETのSendMessageを教えてく...
-
SwingとEDT(イベントディスパッ...
-
C# DataGridView のCellPaintin...
-
Javaで文字の角度を変えて表示...
-
JAVAでの背景画像表示
-
複数画像表示切り替え Visual c++
-
イベントディスパッチングスレ...
-
C# リストビューの特定のセルの...
-
iアプリ作成 画像を描画できない
-
BMP画像を画像処理して連続に表...
-
SwingWorkerに関して
-
canvasで表示されてる画像を1...
-
アクセスで他アプリから復帰し...
-
GDI+で描画した画像を消去する...
-
重なった要素上でのイベントで...
-
ドラッグして矩形を描くには
-
Androidで画像の中で指定した範...
-
requestanimationframeを一斉に...
おすすめ情報