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

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

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

Q&A

解決済

2回答

3622閲覧

Windowsフォームのピクチャーボックスへの図の描画を動的に行いたいです

ata_rashi

総合スコア4

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Windows Forms

Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

1グッド

0クリップ

投稿2020/07/29 05:27

編集2020/07/29 06:55

言語はC#です.現在あるシステムを,Visual Studio のWindows Form アプリケーションを利用して開発しています.その中で,あるクラス(Mainとは別)において,ある変数の値によって,ピクチャーボックスに適当な図(簡単な長方形で良い)を表示させたいと考えています.例えば変数の値が0であれば図は表示していない状態,変数の値が1であれば図が表示されている状態という風にしようと考えています.
つまり,プログラムの処理が進む中で,ピクチャーボックスが動的に変化するようにしたいです.

どのようなコードを組むことで,ピクチャーボックスへの図の描画を動的に行うことができるのでしょうか.
また,Mainとは別のクラスからFormに関する処理を行う上で,Main Formのインスタンスを作成する必要があるということを調べて学びましたが,その方法も分かりません.

現在,MainのFormのあるボタンをクリックすると,別にForm2を表示させるコードを組んでおり,そのForm2のピクチャーボックスに図を描画しようと考えています.


以下,追記内容です.
上記の処理を検証するために,開発中のシステムとは別に,簡単なプログラムを組んでいます.

????MainFormでは,ボタンをクリックすると新しいForm2を表示させます.

private void button1_Click(object sender, EventArgs e) { Form2 form2 = new Form2(); form2.Show(); }

????Form2では,ピクチャーボックスに長方形を描画するメソッドと,ピクチャーボックスを消去するメソッドを用意しています.

   public void DrawRec() { //ピクチャーボックスと同じ大きさのビットマップを作成する Bitmap canvas = new Bitmap(pictureBox1.Width, pictureBox1.Height); Graphics g = Graphics.FromImage(canvas); Pen p = new Pen(Color.Black, 1); g.DrawRectangle(p, 5, 5, 400, 400); p.Dispose(); g.Dispose(); pictureBox1.Image = canvas; } public void DrawNothing() { pictureBox1.Image = null; }

????ここで,Mainフォームのあるボタンをクリックすると,Class1のPracticeメソッドが実行されるようにしています.
ピクチャーボックスに長方形が描画された後,2秒後にそれが消去されるプログラムにしようと考えました.しかし,ピクチャーボックスには何も表示されないという結果になってしまいます.
どこか誤っている箇所があれば,ご指摘いただけないでしょうか.

また,Form2のインスタンスを生成する部分で上手くいっていないことが判明しました.
その部分で具体的な改善策などご教授して頂ければ幸いです.

class Class1 { public void Practice() { int a = 1; int b = 2; int c = 3; if (b == 2) { Form2 form = new Form2(); form.DrawRec(); } System.Threading.Thread.Sleep(2000); if (a == 1) { Form2 form2 = new Form2(); form2.DrawNothing(); } } }
TN8001👍を押しています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/07/29 05:34

どこまで自分で考えてコード化したのですか? それを書かないと丸投げで低評価対象となってしまいますけど・・・
ata_rashi

2020/07/29 05:39

質問の修正を行います.ご指摘ありがとうございます.
YAmaGNZ

2020/07/29 05:51

質問タイトルをそのまま検索すればよろしいかと思います。
退会済みユーザー

退会済みユーザー

2020/07/29 06:43

コードは ``` と ``` で囲ってください(``` はバッククォート 3 つ)。インデントされて読みやすくなるので。インデントされてないコードは質問者さん自身も読む気がしないのでは?
ata_rashi

2020/07/29 06:49

このサイトに慣れていないため,ご迷惑をおかけしました.訂正させていただきます.
退会済みユーザー

退会済みユーザー

2020/07/29 07:20

対応をありがとうございます。すでに回答が出ていますので、それに対するフィードバックを返してください。
退会済みユーザー

退会済みユーザー

2020/07/29 07:59 編集

質問とは関係ない部分ですが、Bitmapオブジェクトは自動では解放されないので、 提示されているソースのDrawRecメソッドの、 Bitmap作成→pictureBox1.ImageにBitmapを設定 を繰り返すとリソースリークします。 また、フォームクローズ時も、使用したBitmapの解放処理が必要です。 http://atsukanrock.hatenablog.com/entry/20110421/1303381271
ata_rashi

2020/07/29 08:35

radianさん ご指摘ありがとうございます。 その点に関しても勉強します。
guest

回答2

0

ベストアンサー

あるクラス(Mainとは別)において,ある変数の値によって,ピクチャーボックスに適当な図(簡単な長方形で良い)を表示させたいと考えています.

「あるクラス(Mainとは別)」ここが肝なのかなと思いました。
具体的な使用イメージがわからないのですが、時間のかかる計算やリアルタイムのデータとかなんでしょうか。

.Netには変数(正確にはプロパティ)の変更を、監視者に通知する仕組みが既にあります。
通知する側がINotifyPropertyChangedを実装し、監視側はPropertyChangedを購読します。
難しそうに感じますが定型コードなのであまり気にせず、「プロパティが変更されると、イベントが来る」と思っておけば十分です。

Form1Form2の連携も、入門者の方が必ず引っかかるポイントですね。
今回はClass1をやり取りすることになります。
文字で説明すると無駄に長くなってしまうので、回答コードを試していただいたほうが早そうです^^;

cs:Form1.cs

1using System; 2using System.Drawing; 3using System.Threading.Tasks; 4using System.Windows.Forms; 5 6namespace Questions281063 7{ 8 public partial class Form1 : Form 9 { 10 private Button button1 = new Button(); 11 private Button button2 = new Button(); 12 private Class1 class1 = new Class1(); 13 14 public Form1() 15 { 16 InitializeComponent(); 17 18 button1.Text = "show"; 19 button1.Click += Button1_Click; 20 Controls.Add(button1); 21 22 button2.Text = "run"; 23 button2.Location = new Point(100, 0); 24 button2.Click += Button2_Click; 25 Controls.Add(button2); 26 } 27 28 private void Button1_Click(object sender, EventArgs e) 29 { 30 // Class1のインスタンスclass1を渡します 31 new Form2(class1).Show(); 32 } 33 34 private async void Button2_Click(object sender, EventArgs e) 35 { 36 button2.Enabled = false; 37 // 別スレッドでclass1のRunを実行し、非同期で終了を待ちます 38 await Task.Run(() => class1.Run()); 39 button2.Enabled = true; 40 } 41 } 42}

cs:Form2.cs

1using System.ComponentModel; 2using System.Drawing; 3using System.Windows.Forms; 4 5namespace Questions281063 6{ 7 public partial class Form2 : Form 8 { 9 private PictureBox pictureBox1 = new PictureBox(); 10 private Class1 class1; 11 12 public Form2(Class1 class1) 13 { 14 // Class1のインスタンスを受け取り、class1変数に入れておきます 15 this.class1 = class1; 16 17 InitializeComponent(); 18 19 pictureBox1.Dock = DockStyle.Fill; 20 pictureBox1.Paint += PictureBox1_Paint; 21 Controls.Add(pictureBox1); 22 23 // 変更通知イベントを購読します 24 class1.PropertyChanged += Class1_PropertyChanged; 25 FormClosed += Form2_FormClosed; 26 } 27 28 private void PictureBox1_Paint(object sender, PaintEventArgs e) 29 { 30 // class1の状態によって何か描きます 31 if(class1.A == 1) e.Graphics.DrawRectangle(Pens.Black, 5, 5, 400, 400); 32 if(class1.B == 1) e.Graphics.FillRectangle(Brushes.Blue, 50, 50, 200, 100); 33 if(class1.C == 1) e.Graphics.FillEllipse(Brushes.Pink, 200, 300, 50, 50); 34 } 35 36 private void Class1_PropertyChanged(object sender, PropertyChangedEventArgs e) 37 { 38 // なにか変更されたのでpictureBox1を描き直します 39 pictureBox1.Invalidate(); 40 } 41 42 private void Form2_FormClosed(object sender, FormClosedEventArgs e) 43 { 44 // 変更通知イベントを購読解除します(忘れるとメモリーリークします) 45 class1.PropertyChanged -= Class1_PropertyChanged; 46 } 47 } 48}

cs:Class1.cs

1using System.ComponentModel; 2using System.Runtime.CompilerServices; 3 4namespace Questions281063 5{ 6 public class Class1 : INotifyPropertyChanged 7 { 8 // ちょっと冗長なのですが、通知する変数はこのようにする必要があります 9 public int A { get => _A; set => Set(ref _A, value); } 10 private int _A; 11 12 public int B { get => _B; set => Set(ref _B, value); } 13 private int _B; 14 15 public int C { get => _C; set => Set(ref _C, value); } 16 private int _C; 17 18 19 public void Run() 20 { 21 A = 1; // AをON 22 System.Threading.Thread.Sleep(1000); 23 A = 0; // AをOFF 24 B = 1; 25 C = 1; 26 System.Threading.Thread.Sleep(1000); 27 B = 0; 28 System.Threading.Thread.Sleep(1000); 29 C = 0; 30 } 31 32 // 定型コードなので気にしないで結構です 33 #region INotifyPropertyChanged 34 public event PropertyChangedEventHandler PropertyChanged; 35 protected void Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) 36 { 37 if(Equals(storage, value)) return; 38 storage = value; 39 OnPropertyChanged(propertyName); 40 } 41 protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 42 #endregion 43 } 44}

Designer.csが行数をとるのでコードでButton等を作っていますが、もちろんデザイナでやってもらっても結構です。

投稿2020/07/29 09:08

編集2023/07/22 10:02
TN8001

総合スコア9862

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ata_rashi

2020/07/29 16:46

ご親切な解答ありがとうございます. コードをコピペすることで無事に実装することができ,非常に助かりました. 追加で少し質問があるのですが... 私がピクチャーボックスを変化させるために見る"ある変数"は,静的な値として定義するものです. そこでClass1のRunメソッドは,一定時間ごとに"ある変数"をチェックし,それに応じてAやBの値を変化させるように改良しました. 以下にその際のコードを示します.("ある変数"をStaticDataクラスのExistanceOfAGVとします) ********************************************************************** public void Run() { while (true) { if (StaticData.ExistenceOfAGV == 1) { A = 1; } if (StaticData.ExistenceOfAGV == 0) { A = 0; } } } ************************************************************************ このコードだと,あるスレッドにて常にStaticData.ExistanceOfAGVの値をチェックしていることになるのですが,CPUの使用率が格段に上がってしまいます. これは仕方のないことなのでしょうか.何か良い方法があれば,追加でご教授願いたいです.
TN8001

2020/07/29 17:07

さすがにウェイトなしで無限ループですとそうなると思います。 Thread.Sleep(1);を入れるだけでもだいぶ軽くなると思います。 もちろんもっと間隔を開けれるならよりいいですが。
TN8001

2020/07/29 17:32

StaticDataクラスをいじれるのであれば、そちらで変更通知したいところです。 static event PropertyChangedEventHandler PropertyChanged のようにするかシングルトン化で対応できそうです。 いじれないのであればしょうがないですね^^;
ata_rashi

2020/07/30 04:36

ウエイトを入れれば多少ましになりました。ありがとうございます^^
guest

0

Class1の

C#

1Form2 form = new Form2();

は新しくForm2を作成します。
この為、現在表示しているForm2とは別のForm2を作成しそのDrawRecを呼んでいますので、現在表示しているForm2には反映されません。

インスタンスについて学習され、正しく理解すべきです。

また、

C#

1System.Threading.Thread.Sleep(2000);

こちらで2秒待っていますが、UIスレッドで実行されるため画面が全部停止することになります。
なので期待している動作は行われないかと思います。
定期的な処理に関してはタイマーを利用されるのが簡単かと思います。

投稿2020/07/29 06:09

YAmaGNZ

総合スコア10489

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ata_rashi

2020/07/29 07:31

解答ありがとうございます.クラスのインスタンスを作成する時と同じようにするのではダメなんですね. このコードだと,別のForm2を作成してしまうことは理解できました. 現在表示しているForm2のメソッドを呼び出すにはどのようなコードを組めばよろしいのでしょうか. ご迷惑でなければ,ご教授頂けると助かります.
YAmaGNZ

2020/07/29 07:34

表示する為に作成したForm2のインスタンスを渡せばいいです。 渡す方法についてはいろいろあります。 関数の引数として渡すなり、インスタンスを受け取るプロパティをクラスに用意して、それにセットするなり。
ata_rashi

2020/07/29 08:38

ご説明ありがとうございます。 もう少し自分で勉強してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問