C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[17]: 親フォームから子フォームのメソッドへアクセス


(過去ログ 130 を表示中)

[トピック内 23 記事 (1 - 20 表示)]  << 0 | 1 >>

■77125 / inTopicNo.1)  親フォームから子フォームのメソッドへアクセス
  
□投稿者/ ゆーきゃん (46回)-(2015/09/15(Tue) 16:15:15)

分類:[.NET 全般] 

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

C#/VisualStudio2013/windowsformアプリケーションでプログラムを組んでおります。

先日、子フォームから親フォームのメソッドへアクセスという内容で、質問させて頂きました。
http://bbs.wankuma.com/index.cgi?mode=al2&namber=77101

今度は親フォームから子フォームのメソッドへのアクセスが思うようにいかず、足が止まってしまいました。


プログラムの流れは以下の通りです。

Form1(親フォーム)には、picturebox1、button1
Form2(子フォーム)には、picturebox2、button2, textBox1~4

があります。

親フォームのbutton1を押すと、Form2が表示され、picturebox2に「test1.dat」に保存された座標値(x1(100),y1(100))~ (x2(150),y2(100))を元に線を描画します。
textBox1~4には、x1,y1,x2,y2の値が表示され、値を入力し直すと、線の長さも変更されるようになっております。
button2を押すと、textBox1~4の値を「test1.dat」に書き出し、データを保存、Form2を閉じるようになっております。

ここで質問なのですが、Form1が開いた際に、Form2の「yomikomiメソッド」へアクセスし、picturebox1にその座標値を元にした線を描画したいのですが、どのようにプログラムを組むことで可能でしょうか?

Form1側で、

((Form2)Application.OpenForms["Form2"]).yomikomi();
Form2.yomikomi();

など追加してみたのですが、静的関数でないなどの理由から、思い通りの結果を出力できませんでした。

何か良い方法・プログラムを省略できるポイントなどありましたら、ご指導頂けないでしょうか。



以下作成途中のプログラムを掲載させて頂きます。


● Form1 ==================================================================================

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


namespace test1
{
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            using (Form2 fm = new Form2(draw))
            {
                fm.ShowDialog();
            }

        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            // draw(sender, e);
        }

        public void draw(object sender, PaintEventArgs e)
        {
           // e.Graphics.DrawLine(Pens.Green, x1, y1, x2, y2);         
        }
     
    }
}



● Form2 ==================================================================================

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;


namespace test1
{
    public partial class Form2 : Form
    {

        float x1;
        float y1;
        float x2;
        float y2;

        public Form2(PaintEventHandler drawMethod)
        {

            InitializeComponent();

            if (drawMethod != null)
            {
                this.pictureBox2.Paint += drawMethod;
            }
        }

        public Form2(): this(null)
        {
        }


        private void Form2_Load(object sender, EventArgs e)
        {
            yomikomi(sender, e);
        }


        private void pictureBox2_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawLine(Pens.Green, x1, y1, x2, y2);    
        }

        private void textBox1_TextChanged_1(object sender, EventArgs e)
        {
            x1 = int.Parse(textBox1.Text);
        }

        private void textBox2_TextChanged_1(object sender, EventArgs e)
        {
            y1 = int.Parse(textBox2.Text);
        }

        private void textBox3_TextChanged_1(object sender, EventArgs e)
        {
            x2 = int.Parse(textBox3.Text);
        }

        private void textBox4_TextChanged_1(object sender, EventArgs e)
        {
            y2 = int.Parse(textBox4.Text);
            pictureBox2.Refresh();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            using (BinaryWriter w = new BinaryWriter(File.OpenWrite(@"c:\data\test1.dat"))) // ファイルの書き込み
            {
                w.Write(x1);
                w.Write(y1);
                w.Write(x2);
                w.Write(y2);

            }
            this.Close();
        }

       

        public void yomikomi(object sender, EventArgs e)
        {
            FileStream fs = new FileStream(@"c:\data\test1.dat", FileMode.Open, FileAccess.Read); // ファイルを開く
            BinaryReader br = new BinaryReader(fs); // ファイルの読み取り

            x1 = br.ReadSingle();  // 4バイト浮動小数点値を読み取り、4バイト進める
            y1 = br.ReadSingle(); //                     //
            x2 = br.ReadSingle(); //                     //
            y2 = br.ReadSingle(); //                     //

            textBox1.Text = x1.ToString(); // 読み取った値をtextboxに代入
            textBox2.Text = y1.ToString(); //             //
            textBox3.Text = x2.ToString(); //             //
            textBox4.Text = y2.ToString(); //             //

            fs.Close(); // ファイルを閉じる
        }
    }
}




引用返信 編集キー/
■77126 / inTopicNo.2)  Re[1]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ ザマス (1回)-(2015/09/15(Tue) 16:33:52)
Form2のyomikomiメソッドに引数がついていますが、
引数は不要なのでは?

public void yomikomi()

のように引数なしのメソッドにして、Form1のbutton1_Clickで、


using (Form2 fm = new Form2(draw))
{
    fm.yomikomi();
    fm.ShowDialog();
}

でいいのではないですか?

引用返信 編集キー/
■77127 / inTopicNo.3)  Re[2]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ ザマス (2回)-(2015/09/15(Tue) 16:47:34)
>ここで質問なのですが、Form1が開いた際に、

これを見落としていました。
でも、Form1が開いた時にはForm2のインスタンスはないので、
Form2の静的メンバを用意しないと無理ですね。

でも、Form2の操作をするタイミングは、

using (Form2 fm = new Form2(draw))

の後ではダメなのでしょうか?
Form2_Loadでも同じことをやっているようですが?
引用返信 編集キー/
■77128 / inTopicNo.4)  Re[3]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ ゆーきゃん (48回)-(2015/09/15(Tue) 17:39:15)
No77127 (ザマス さん) に返信
> >ここで質問なのですが、Form1が開いた際に、
> 
> これを見落としていました。
> でも、Form1が開いた時にはForm2のインスタンスはないので、
> Form2の静的メンバを用意しないと無理ですね。
> 
> でも、Form2の操作をするタイミングは、
> 
> using (Form2 fm = new Form2(draw))
> 
> の後ではダメなのでしょうか?
> Form2_Loadでも同じことをやっているようですが?


迅速なお答えありがとうございます。
using (Form2 fm = new Form2(draw))の後で行っても特に問題はないです。


やりたいこととしましては、

@ Form1が開かれる。
A 「test1.dat」の座標値データから、picturebox1に線が引かれる。
B button1を押すと、Form2が開かれる。
C 「test1.dat」の座標値データから、picturebox2に線が引かれる。
D 線の長さを変えたい場合は、textBox1~4の座標値を変更する。
E buton2を押すと、現状の座標値データが保存され、Form2が閉じられる。
F picturebox1の線の長さが更新される

といった流れにしたいと思っております。

やはりこの流れに沿う場合、静的メンバで行わなければいけないのでしょうか。

引用返信 編集キー/
■77130 / inTopicNo.5)  Re[4]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ ゆーきゃん (50回)-(2015/09/15(Tue) 18:20:12)
ザマス様のアドバイスを元に少し修正を加えさせて頂きました。

なんとかあと一歩のところまで来ることができました。

@ Form1が開かれる。
A 「test1.dat」の座標値データから、picturebox1に線が引かれる。
B button1を押すと、Form2が開かれる。
C 「test1.dat」の座標値データから、picturebox2に線が引かれる。
D 線の長さを変えたい場合は、textBox1~4の座標値を変更する。
E buton2を押すと、現状の座標値データが保存され、Form2が閉じられる。

まではクリアできたのですが、

F picturebox1の線の長さが更新される

がクリアできません。

どのような更新方法がありますでしょうか。

どなた様かお答えアドバイスの程よろしくお願い致します。


以下プログラムソースです。



● Form1 ==================================================================================

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;


namespace test1
{
    public partial class Form1 : Form
    {
        Form2 fm = new Form2();

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {              
            fm.ShowDialog();
        }

        public void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            fm.yomikomi();
            draw(sender, e);
        }

        public void draw(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawLine(Pens.Green, fm.x1, fm.y1, fm.x2, fm.y2);         
        }
       
    }
}


● Form2 ==================================================================================

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;


namespace test1
{
    public partial class Form2 : Form
    {

        public float x1;
        public float y1;
        public float x2;
        public float y2;

        public Form2()
        {
            InitializeComponent();           
        }

        private void pictureBox2_Paint(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawLine(Pens.Green, x1, y1, x2, y2);    
        }

        private void textBox1_TextChanged_1(object sender, EventArgs e)
        {
            x1 = int.Parse(textBox1.Text);
        }

        private void textBox2_TextChanged_1(object sender, EventArgs e)
        {
            y1 = int.Parse(textBox2.Text);
        }

        private void textBox3_TextChanged_1(object sender, EventArgs e)
        {
            x2 = int.Parse(textBox3.Text);
        }

        private void textBox4_TextChanged_1(object sender, EventArgs e)
        {
            y2 = int.Parse(textBox4.Text);
            pictureBox2.Refresh();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            using (BinaryWriter w = new BinaryWriter(File.OpenWrite(@"c:\data\test1.dat"))) // ファイルの書き込み
            {
                w.Write(x1);
                w.Write(y1);
                w.Write(x2);
                w.Write(y2);

            }

            this.Close();
        }
     
        private void Form2_Load(object sender, EventArgs e)
        {
            yomikomi();
        }

        public void yomikomi()
        {
            FileStream fs = new FileStream(@"c:\data\test1.dat", FileMode.Open, FileAccess.Read); // ファイルを開く
            BinaryReader br = new BinaryReader(fs); // ファイルの読み取り

            x1 = br.ReadSingle();  // 4バイト浮動小数点値を読み取り、4バイト進める
            y1 = br.ReadSingle(); //                     //
            x2 = br.ReadSingle(); //                     //
            y2 = br.ReadSingle(); //                     //

            textBox1.Text = x1.ToString(); // 読み取った値をtextboxに代入
            textBox2.Text = y1.ToString(); //             //
            textBox3.Text = x2.ToString(); //             //
            textBox4.Text = y2.ToString(); //             //

            fs.Close(); // ファイルを閉じる
        }
    }
}

引用返信 編集キー/
■77131 / inTopicNo.6)  Re[5]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ 魔界の仮面弁士 (495回)-(2015/09/15(Tue) 22:58:34)
No77130 (ゆーきゃん さん) に返信
> F picturebox1の線の長さが更新される

子画面で操作した結果を親画面に反映させたいのであれば、
FontDialog クラスの Apply イベントの実装を真似ては如何でしょう?


Form2 側で更新したら、そのことを Form1 に「イベント」で通知するということです。
Form1 側はその通知を受けて、pictureBox1.Invalidate(); すれば実現できるかと思います。

あるいはその通知イベントで、描画結果の Bitmap を渡すようにしておけば、
Form1 側は Paint イベントを実装する必要がなくなります。
(Form1 側では、イベント通知内で pictureBox1.Image = e.NewImage; のようにするだけで良い)



そもそも今回の場合、データの読み込みや更新を担当しているのは Form2 側のようなので、
描画機能を Form1 側で担当するのは不自然かと思います。描画機能は Form2 側に持たせておき、
Form1 はそれを使って描画するようにするか、または、描画担当のクラスを別に作っておき、
Form1 と Form2 の双方が、そのクラスを利用して、自身の PictureBox に描くようにするとか。
引用返信 編集キー/
■77132 / inTopicNo.7)  Re[6]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ kiku (61回)-(2015/09/16(Wed) 09:27:55)
ShowDialogはモーダル形式で表示するメソッドであるため、
Form2が閉じられるまでブロックします。
なので、単純にForm2が閉じられた後に、
pictureBox1.Invalidateを実行してはいかがでしょうか?

        private void button1_Click(object sender, EventArgs e)
        {
            using (Form2 fm = new Form2(Draw))
            {
                fm.ShowDialog();
            }
            pictureBox1.Invalidate();
        }

引用返信 編集キー/
■77134 / inTopicNo.8)  Re[6]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ ゆーきゃん (51回)-(2015/09/16(Wed) 11:18:54)
No77131 (魔界の仮面弁士 さん) に返信
> ■No77130 (ゆーきゃん さん) に返信
>>F picturebox1の線の長さが更新される
>
> 子画面で操作した結果を親画面に反映させたいのであれば、
> FontDialog クラスの Apply イベントの実装を真似ては如何でしょう?
>
>
> Form2 側で更新したら、そのことを Form1 に「イベント」で通知するということです。
> Form1 側はその通知を受けて、pictureBox1.Invalidate(); すれば実現できるかと思います。
>
> あるいはその通知イベントで、描画結果の Bitmap を渡すようにしておけば、
> Form1 側は Paint イベントを実装する必要がなくなります。
> (Form1 側では、イベント通知内で pictureBox1.Image = e.NewImage; のようにするだけで良い)
>
> そもそも今回の場合、データの読み込みや更新を担当しているのは Form2 側のようなので、
> 描画機能を Form1 側で担当するのは不自然かと思います。描画機能は Form2 側に持たせておき、
> Form1 はそれを使って描画するようにするか、または、描画担当のクラスを別に作っておき、
> Form1 と Form2 の双方が、そのクラスを利用して、自身の PictureBox に描くようにするとか。


お答えありがとうございます。
FontDialog クラスの Apply イベントを調べているのですが、調べ方が悪いのか、なかなか参考になりそうな記述を見つけられずにいます。もう少し粘って頑張ってみようと思います。

プログラムの組み方の考え方も教えて頂きありがとうございます。ちょっと整理できていない面もあるので考え直してみようと思います。


引用返信 編集キー/
■77135 / inTopicNo.9)  Re[7]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ 魔界の仮面弁士 (496回)-(2015/09/16(Wed) 12:14:18)
No77134 (ゆーきゃん さん) に返信
> FontDialog クラスの Apply イベントを調べているのですが、

呼びだし側としては:

private void fontDialog1_Apply(object sender, EventArgs e)
{
  this.textBox1.Font = this.fontDialog1.Font;
}

ということです。fontDialog1.ShowApply を true にしておいた上で、
ダイアログの「適用」ボタンを押すと、ダイアログを閉じることなく、
親フォームの textBox1 のフォントが書き換わります。



> なかなか参考になりそうな記述を見つけられずにいます。
利用側の話なら、ヘルプというか MSDN ライブラリで
FontDialog.Apply イベント の項を見ると、実際の使用例が載っています。


子画面側でイベントを実装する手順が分からないという事であれば、
このあたりは如何でしょうか。

https://msdn.microsoft.com/ja-jp/library/5z57dxz2.aspx
http://yohshiy.blog.fc2.com/blog-entry-263.html
http://ufcpp.net/study/csharp/sp_event.html
http://dobon.net/vb/dotnet/vb2cs/event.html
引用返信 編集キー/
■77136 / inTopicNo.10)  Re[7]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ 774RR (313回)-(2015/09/16(Wed) 12:44:13)
2015/09/16(Wed) 12:45:22 編集(投稿者)
そもそも論として
「親フォームが子フォームの中に手を突っ込む(=想定しないタイミングでメンバを呼ぶとか)」
「子フォームが親フォームの中に手を突っ込む」
ってのは設計上の責任分担ができていない証拠であって、「オブジェクト指向の本来の姿ぢゃない」わけだ。
そういう「お互いに強依存する」フォームって流用ができないぢゃないか。

オブジェクト指向的に間違った設計をしてしまっているから、実装が簡単でないんだよ。
(間違ったコードを容易に実装できないように言語仕様が作られている)

既に指摘があるように Form2.ShowDialog() するってことはモーダルな動作をさせようとしている
モーダルってのはよくある [OK] [キャンセル] 形式の操作をするものだ
設定変更モーダルダイアログを
- キャンセル終了したら、ダイアログ上での操作は全て捨てられて元データは一切変化しないべきだし
- OK終了したら元データが変化すべき
なのであって Form.ShowDialog() ってそういう使い方をするように作られている。
(この際 [適用] Apply はちょっとおいておく)

この件、オイラなら次のように設計する
- test1.dat からデータをもらったり書き戻すのは Form1 の仕事であって Form2 にあるべきではない。
- Form2 は Form1 からもらったデータを表示し、変更を受付け、OK終了したらデータを返せる」ように作る
- Form1 は Form2 がOK終了したら Form2 からデータをもらって更新する

Form1 の void button1_Click(object sender, EventArgs e)
{
    using (Form2 fm2 = new Form2())
    {
        fm2.なんとかプロパティ = <form1 の持ってるデータ1>;
        fm2.かんとかプロパティ = <form1 の持ってるデータ2>;
        if (fm2.ShowDialog() == DialogResult.OK)
        {
            <form1 の持ってるデータ1> = fm2.なんとかプロパティ;
            <form1 の持ってるデータ2> = fm2.かんとかプロパティ;
            Invalidate(); // Form1 自分自身の画面更新
        }
    }
}

Form.ShowDialog で using (Dispose) が必要なのは、
Form2 の画面がOKで閉じた後に なんとかプロパティ にアクセスできるようにするためだよ。
必要なデータを拾い上げ完了するまで Dispose させないための工夫。

引用返信 編集キー/
■77140 / inTopicNo.11)  Re[8]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ ゆーきゃん (53回)-(2015/09/16(Wed) 16:27:25)
kiku様 
 
> ShowDialogはモーダル形式で表示するメソッドであるため、
> Form2が閉じられるまでブロックします。
> なので、単純にForm2が閉じられた後に、
> pictureBox1.Invalidateを実行してはいかがでしょうか?
>
>        private void button1_Click(object sender, EventArgs e)
>        {
>            using (Form2 fm = new Form2(Draw))
>            {
>                fm.ShowDialog();
>            }
>            pictureBox1.Invalidate();
>        }
>

お答えありがとうございました。
kiku様の助言通り、プログラムを書き足すことで解決いたしましたありがとうございました。
 

魔界の仮面弁士様

 ■No77134 (ゆーきゃん さん) に返信
> FontDialog クラスの Apply イベントを調べているのですが、

>呼びだし側としては:

>private void fontDialog1_Apply(object sender, EventArgs e)
>{
>  this.textBox1.Font = this.fontDialog1.Font;
>}

>ということです。fontDialog1.ShowApply を true にしておいた上で、
>ダイアログの「適用」ボタンを押すと、ダイアログを閉じることなく、
>親フォームの textBox1 のフォントが書き換わります。

>> なかなか参考になりそうな記述を見つけられずにいます。
>利用側の話なら、ヘルプというか MSDN ライブラリで
>FontDialog.Apply イベント の項を見ると、実際の使用例が載っています。

>子画面側でイベントを実装する手順が分からないという事であれば、
>このあたりは如何でしょうか。

>https://msdn.microsoft.com/ja-jp/library/5z57dxz2.aspx
>http://yohshiy.blog.fc2.com/blog-entry-263.html
>http://ufcpp.net/study/csharp/sp_event.html
>http://dobon.net/vb/dotnet/vb2cs/event.html
 

わざわざ参考サイトを提示して頂きありがとうございます。
色々な情報で頭の中がいっぱいになってしまっていますが、何とか理解できるよう、しっかり読み込んでいこうと思います。




引用返信 編集キー/
■77141 / inTopicNo.12)  Re[9]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ ゆーきゃん (54回)-(2015/09/16(Wed) 16:53:33)
No77136 (774RR さん) に返信
> 2015/09/16(Wed) 12:45:22 編集(投稿者)
> 
> そもそも論として
> 「親フォームが子フォームの中に手を突っ込む(=想定しないタイミングでメンバを呼ぶとか)」
> 「子フォームが親フォームの中に手を突っ込む」
> ってのは設計上の責任分担ができていない証拠であって、「オブジェクト指向の本来の姿ぢゃない」わけだ。
> そういう「お互いに強依存する」フォームって流用ができないぢゃないか。
> 
> オブジェクト指向的に間違った設計をしてしまっているから、実装が簡単でないんだよ。
> (間違ったコードを容易に実装できないように言語仕様が作られている)
> 
> 既に指摘があるように Form2.ShowDialog() するってことはモーダルな動作をさせようとしている
> モーダルってのはよくある [OK] [キャンセル] 形式の操作をするものだ
> 設定変更モーダルダイアログを
> - キャンセル終了したら、ダイアログ上での操作は全て捨てられて元データは一切変化しないべきだし
> - OK終了したら元データが変化すべき
> なのであって Form.ShowDialog() ってそういう使い方をするように作られている。
> (この際 [適用] Apply はちょっとおいておく)
> 
> この件、オイラなら次のように設計する
> - test1.dat からデータをもらったり書き戻すのは Form1 の仕事であって Form2 にあるべきではない。
> - Form2 は Form1 からもらったデータを表示し、変更を受付け、OK終了したらデータを返せる」ように作る
> - Form1 は Form2 がOK終了したら Form2 からデータをもらって更新する
> 
> Form1 の void button1_Click(object sender, EventArgs e)
> {
>     using (Form2 fm2 = new Form2())
>     {
>         fm2.なんとかプロパティ = <form1 の持ってるデータ1>;
>         fm2.かんとかプロパティ = <form1 の持ってるデータ2>;
>         if (fm2.ShowDialog() == DialogResult.OK)
>         {
>             <form1 の持ってるデータ1> = fm2.なんとかプロパティ;
>             <form1 の持ってるデータ2> = fm2.かんとかプロパティ;
>             Invalidate(); // Form1 自分自身の画面更新
>         }
>     }
> }
> 
> Form.ShowDialog で using (Dispose) が必要なのは、
> Form2 の画面がOKで閉じた後に なんとかプロパティ にアクセスできるようにするためだよ。
> 必要なデータを拾い上げ完了するまで Dispose させないための工夫。
> 


オブジェクト指向の考え方を含めたお答えありがとうございます。
774RR様のお答えを元に少しプログラムを書き換えてみました。

しかし、自分で見ても無駄の多いプログラムとわかるぐらい正直言ってひどい出来だと思います。

1.test1.dat からデータをもらったり書き戻すのは Form1 の仕事であって Form2 にあるべきではない。

とのことから、yomikomiメソッドをForm1へ移動させました。
データを書き込む際は、form2のボタンを押した際なので、そのまま維持しました。

2.Form2 は Form1 からもらったデータを表示し、変更を受付け、OK終了したらデータを返せる」ように作る

Form1 ←→ Form2 のデータの受け渡し方法が上手くできませんでした。
お手数おかけして申し訳ないのですが、お答えアドバイス願えませんでしょうか。

以下のプログラムですと、Form2からForm1の変数などをもらいに行くとき、Form1を初期化してしまっていると思います。初期化せずに受け取りに行きたいのですが、調べて試行錯誤してみたのですが、思い通りの結果を出すことができませんでした。

●Form1

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;


namespace test1
{
    public partial class Form1 : Form
    {
        public float x1;
        public float y1;
        public float x2;
        public float y2;

        public Form1()
        {
            InitializeComponent();
        }


        private void Form1_Load(object sender, EventArgs e)
        {
            yomikomi();
        }


        public void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            draw(sender, e);
        }


        public void yomikomi()
        {
            FileStream fs = new FileStream(@"c:\data\test1.dat", FileMode.Open, FileAccess.Read); // ファイルを開く
            BinaryReader br = new BinaryReader(fs); // ファイルの読み取り

            x1 = br.ReadSingle();  // 4バイト浮動小数点値を読み取り、4バイト進める
            y1 = br.ReadSingle(); //                     //
            x2 = br.ReadSingle(); //                     //
            y2 = br.ReadSingle(); //                     //

            fs.Close(); // ファイルを閉じる
        }  
        
        
        public void draw(object sender, PaintEventArgs e)
        {
            e.Graphics.DrawLine(Pens.Green, x1, y1, x2, y2);         
        }


        private void button1_Click(object sender, EventArgs e)
        {
            using (Form2 fm2 = new Form2())
            {
                fm2.ShowDialog();
                yomikomi();
                pictureBox1.Invalidate();          
            }

        }       
    }
}


● Form2

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;


namespace test1
{
    public partial class Form2 : Form
    {

        Form1 fm1 = new Form1();  //  form1の変数が初期化されてしまう・・・    form1を初期化せずアクセスする方法を模索中
        

        public Form2()
        {
            InitializeComponent();           
        }

        private void textBox1_TextChanged_1(object sender, EventArgs e)
        {
            fm1.x1 = int.Parse(textBox1.Text);
            pictureBox2.Refresh();
        }

        private void textBox2_TextChanged_1(object sender, EventArgs e)
        {
            fm1.y1 = int.Parse(textBox2.Text);
            pictureBox2.Refresh();
        }

        private void textBox3_TextChanged_1(object sender, EventArgs e)
        {
            fm1.x2 = int.Parse(textBox3.Text);
            pictureBox2.Refresh();
        }

        private void textBox4_TextChanged_1(object sender, EventArgs e)
        {
            fm1.y2 = int.Parse(textBox4.Text);
            pictureBox2.Refresh();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            using (BinaryWriter w = new BinaryWriter(File.OpenWrite(@"c:\data\test1.dat"))) // ファイルの書き込み
            {
                w.Write(fm1.x1);
                w.Write(fm1.y1);
                w.Write(fm1.x2);
                w.Write(fm1.y2);

            }

            this.Close();
        }

        private void pictureBox2_Paint(object sender, PaintEventArgs e)
        {
            fm1.draw(sender,e);
        }

        private void Form2_Load(object sender, EventArgs e)
        {
            fm1.yomikomi();
            
            textBox1.Text = fm1.x1.ToString(); // 読み取った値をtextboxに代入
            textBox2.Text = fm1.y1.ToString(); //             //
            textBox3.Text = fm1.x2.ToString(); //             //
            textBox4.Text = fm1.y2.ToString(); //             //
        }
    }
}


皆様何度も何度も本当に申し訳ないですが、お答えアドバイスの程よろしくお願い致します。





引用返信 編集キー/
■77146 / inTopicNo.13)  Re[10]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ 我龍院 (1回)-(2015/09/17(Thu) 09:40:25)
暗中模索の感あり。
>Form1 ←→ Form2 のデータの受け渡し方法が上手くできませんでした。
>Form1 fm1 = new Form1(); // form1の変数が初期化されてしまう・・・
>form1を初期化せずアクセスする方法を模索中

前の投稿も同じですが、要するに「クラスとインスタンス」と言うことが
全く理解されていないように思います。

急がば回れで、Google で 「c# form1からform2」などと検索して、
少なくても下のコードが何をしているのかを理解してから、コードを書いた方が早いと思います。
Form2 f = new Form2();
f.ShowDialog(this);

老婆心ながら...
引用返信 編集キー/
■77147 / inTopicNo.14)  Re[10]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ 774RR (314回)-(2015/09/17(Thu) 09:40:35)
だから 相互依存しないように 組んだらいいぢゃん。
- Form1 がその中で Form2 を使っている (これは設計意図通りで正しい)
- Form2 がその中で Form1 を使っている (おかしい)

そもそも Form2 の中で new Form1() したら、それは既にある Form1 とは別の Form1 であることはわかっている?

Form2 の中でファイルに書き戻しているのもオイラのサンプルと違うぢゃん。

Form2 に実装すべきは
- 呼び出し元から Form2.TextBox1...Form2.TextBox4 に格納すべき値を得る関数(プロパティ)
- 呼び出し元へ Form2.TextBox1...TextBox4 に入っていた値を返却する関数(プロパティ)
※この [呼び出し元] が、今はたまたま Form1 なんだけど、別に Form3 であってもいいんだ。
そうゆうふうに作っておけば流用が利くだろ。

Form1 がすべき仕事は既にサンプルで提示した。
ファイルに書くのは [Save] [SaveAs] のタイミングですべきだと思うぞ。

引用返信 編集キー/
■77149 / inTopicNo.15)  Re[11]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ ゆーきゃん (56回)-(2015/09/17(Thu) 10:48:06)
No77146 (我龍院 さん) に返信
> 暗中模索の感あり。
> >Form1 ←→ Form2 のデータの受け渡し方法が上手くできませんでした。
> >Form1 fm1 = new Form1(); // form1の変数が初期化されてしまう・・・
> >form1を初期化せずアクセスする方法を模索中
>
> 前の投稿も同じですが、要するに「クラスとインスタンス」と言うことが
> 全く理解されていないように思います。
>
> 急がば回れで、Google で 「c# form1からform2」などと検索して、
> 少なくても下のコードが何をしているのかを理解してから、コードを書いた方が早いと思います。
> Form2 f = new Form2();
> f.ShowDialog(this);
>
> 老婆心ながら...

お答えありがとうございます。
確かに性質を理解せずにプログラムを書き込んでる感がすごいあります。
「急がば回れ」、確かにそうですね。もう一度参考書読み返してきます!
貴重なご意見ありがとうございました。



No77147 (774RR さん) に返信
> だから 相互依存しないように 組んだらいいぢゃん。
> - Form1 がその中で Form2 を使っている (これは設計意図通りで正しい)
> - Form2 がその中で Form1 を使っている (おかしい)
>
> そもそも Form2 の中で new Form1() したら、それは既にある Form1 とは別の Form1 であることはわかっている?
>
> Form2 の中でファイルに書き戻しているのもオイラのサンプルと違うぢゃん。
>
> Form2 に実装すべきは
> - 呼び出し元から Form2.TextBox1...Form2.TextBox4 に格納すべき値を得る関数(プロパティ)
> - 呼び出し元へ Form2.TextBox1...TextBox4 に入っていた値を返却する関数(プロパティ)
> ※この [呼び出し元] が、今はたまたま Form1 なんだけど、別に Form3 であってもいいんだ。
> そうゆうふうに作っておけば流用が利くだろ。
>
> Form1 がすべき仕事は既にサンプルで提示した。
> ファイルに書くのは [Save] [SaveAs] のタイミングですべきだと思うぞ。
>

お答えありがとうございます。
「相互依存しない」、「流用が利くようにForm1で指示をする」これが大切だということが分かりました。
どのように設計すればよいのか見えてきましたので、もう一度プログラムを書いてみようと思います。

これ以上皆様にご迷惑はかけられないので、ここからは皆様のお答えを元にプログラムを組んでいこうと思います。

完成しましたら、誰かの役に立つかわかりませんが、もう一度プログラムソースをここに貼らせて頂きたいと思います。

皆様、長々とありがとうございました。
大変助かりました。




解決済み
引用返信 編集キー/
■77150 / inTopicNo.16)  Re[10]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ 魔界の仮面弁士 (498回)-(2015/09/17(Thu) 11:06:09)
2015/09/17(Thu) 11:23:17 編集(投稿者)

# 解決済みマークついてたか…どうしよう。
# とりあえず解決マーク付け直しておきます。

No77141 (ゆーきゃん さん) に返信
> pictureBox1.Invalidate();
> pictureBox2.Refresh();

Invalidate と Refresh の違いは把握されていますか?
(個人的には、TextChanged した際は Invalidate の方が良いと思います)



> private void textBox1_TextChanged_1(object sender, EventArgs e)
> {
>   fm1.x1 = int.Parse(textBox1.Text);
現在の実装だと、「Text を空欄にした場合」や「非数値が入力された場合」にエラーになりませんか?

.Parse メソッドの代わりに .TryParse を使うことを検討してみて下さい。
これなら、変換できない文字列であったとしてもエラーにはなりません。


また、ユーザーが常に数値を入力するのだとしても、入力途中の値は
必ずしも .Parse できない可能性があることに注意して下さい。たとえば、
「-123.4」という値をキーボードから入力する場合、Text の内容は通常、
 "" → "-" → "-1" → "-12" → "-123" → "-123." → "-123.4"
と変化していきますよね。"" や "-" は数値に変換できませんので、
現状のコードだと、変換エラーで止まってしまうと思います。
(キーボードから値を入力するたびに TextChanged が発生します)


なお、入力途中の値は使用せず、入力後の値を使用したいのであれば、
TextChanged イベントを使うかわりに
Validating / Validated イベントの利用を検討してみて下さい。


それと、数値入力専用ということであれば、TextBox の代わりに
NumericUpDown コントロールを使うこともできます。
これを使えば非数値を入力できなくなるので、
上記のような変換エラーに悩まされずに済みますよ。
(NumericUpDown の場合は、Text プロパティではなく Value プロパティを使います)

NumericUpDown の場合、最大値・最小値を指定したり、
小数点以下の桁数を指定するといったこともできます。
今回の要件に合うかどうかは分かりませんけど。

> public void draw(object sender, PaintEventArgs e)

Form1 の pictureBox1 と
Form2 の pictureBox2 の役割の違いが良く分かりませんでした。

もし、『pictureBox1 は、子画面が閉じられるまでは描き変わらない』としたいなら、
Form2 はモーダルにし、OK / Cancel ボタンを用意しておくのが良いと思います。

一方、『TextBox で編集すると、両方の PictureBox がリアルタイムに更新される』
という動作を望むなら、Form2 をモードレスにしておくのが良いのでは無いでしょうか。



> Form2からForm1の変数などをもらいに行くとき、
その考えがそもそも間違ってます。

「親画面が、子画面の値を読み書きする」のは OK ですが、
「子画面が、親画面の値を読みに行く」のは、基本的に NG です。

「親画面側が、自身の値を子画面に渡す」とか
「親画面側が、子画面側から値を受け取る」形に書き換えてみましょう。


たとえば、MessageBox.Show するとき、this.textBox1.Text を引数に渡すことはありますが、
MessageBox 側が、呼び出し元画面の TextBox の値を読み書きしたりはしないですよね。



>  fm1.x1 = int.Parse(textBox1.Text);
上記の指摘と関連しますが、
「子画面が、親画面のコントロールやプロパティ、変数等を直接操作すること」
は避けてください。

ちなみに「親画面が、子画面のコントロールを直接操作すること」も NG です。
ただし、「親画面が、子画面のプロパティやメソッドを呼ぶこと」は OK です。
※そのプロパティが public (あるいは internal であること)が前提ですが。


> fm1.y1 = int.Parse(textBox2.Text);
元データが float 型であるのにもかかわらず、
int 型に変換している点に違和感を覚えます。

元データが float なら、編集も float に統一するべきだと
思うのですが、意図的にデータ型を変えているのでしょうか?

データ形式が float なだけで、実データは int 値しか扱わないのなら、
あえてそういう実装にすることもあるとは思いますが、ちょっと気になりました。
解決済み
引用返信 編集キー/
■77169 / inTopicNo.17)  Re[11]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ ゆーきゃん (57回)-(2015/09/18(Fri) 15:57:55)
No77150 (魔界の仮面弁士 さん) に返信
> 2015/09/17(Thu) 11:23:17 編集(投稿者)

お答え頂いていたにも関わらず、お返事が遅くなってしまい申し訳ございませんでした。

> Invalidate と Refresh の違いは把握されていますか?
> (個人的には、TextChanged した際は Invalidate の方が良いと思います)

http://hpcgi1.nifty.com/MADIA/VBBBS2/wwwlng.cgi?print+200909/09090024.txt

魔界の仮面弁士様が解説しているページを拝見させて頂きました。
大変わかりやすくまとめてくださっていたので、理解できました。
このプログラムですと、「Invalidate」がよさそうですね。

>>fm1.x1 = int.Parse(textBox1.Text);
> 現在の実装だと、「Text を空欄にした場合」や「非数値が入力された場合」にエラーになりませんか?
> 
> .Parse メソッドの代わりに .TryParse を使うことを検討してみて下さい。
> これなら、変換できない文字列であったとしてもエラーにはなりません。


.TryParse を使用することで、自動で判断してくれるのはありがたいですね。
しかし、

send_x1 = float.Parse(textBox1.Text); から
send_x1 = float.TryParse(textBox1.Text); と変えたところ、

「error CS1501: 引数を 1 個指定できる、メソッド 'TryParse' のオーバーロードはありません」というエラーが発生してしまいました。


> なお、入力途中の値は使用せず、入力後の値を使用したいのであれば、
> TextChanged イベントを使うかわりに
> Validating / Validated イベントの利用を検討してみて下さい。

入力後に値を使用したほうが、今回の場合、よさそうですね。

Leave : 入力のフォーカスがコントロールを離れた場合に発生する。
Validating : 入力値の検査。
Validated : 入力値の検証終了後に発生する。

Validated・Validating の場合、form2を表示させた際に線が表示されなくなってしまいました、textbox1~4を選択しないと線が表示されなかったので、
今回はそのままにしてあります。



> それと、数値入力専用ということであれば、TextBox の代わりに
> NumericUpDown コントロールを使うこともできます。
> これを使えば非数値を入力できなくなるので、
> 上記のような変換エラーに悩まされずに済みますよ。
> (NumericUpDown の場合は、Text プロパティではなく Value プロパティを使います)


今回のプログラムはサンプルで作ったものなので、このままtextboxで進めようと思います。数字しか入りませんが、空欄・文字などの場合は、if分の条件付けで弾こうと思います。


>>Form2からForm1の変数などをもらいに行くとき、
> その考えがそもそも間違ってます。
> 
> 「親画面が、子画面の値を読み書きする」のは OK ですが、
> 「子画面が、親画面の値を読みに行く」のは、基本的に NG です。
> 
> 「親画面側が、自身の値を子画面に渡す」とか
> 「親画面側が、子画面側から値を受け取る」形に書き換えてみましょう。
> 

この考えを知ることができてよかったです。
今後これを頭にプログラムを組んでいこうと思います。


774RR様に教えて頂いた形式とは、少し変わってしまったのですがいかがでしょうか?

何かお気づきの点など御座いましたらご教授願えませんでしょうか。


以下修正したプログラムとなります。


● Form1

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;


namespace test1
{
    public partial class Form1 : Form
    {
        // Form2の宣言
        Form2 fm2;

        public float x1;
        public float y1;
        public float x2;
        public float y2;

        public Form1()
        {
            // Form2にインスタンスを作成してForm1のインスタンスを渡す
            fm2 = new Form2(this);

            InitializeComponent();           
        }

        public void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            draw(sender, e);
        }

        public void draw(object sender, PaintEventArgs e)
        {
            yomikomi();
            e.Graphics.DrawLine(Pens.Green, x1, y1, x2, y2);
        }
        
        public void yomikomi()
        {
            FileStream fs = new FileStream(@"c:\data\test1.dat", FileMode.Open, FileAccess.Read); // ファイルを開く
            BinaryReader br = new BinaryReader(fs); // ファイルの読み取り

            x1 = br.ReadSingle();  // 4バイト浮動小数点値を読み取り、4バイト進める
            y1 = br.ReadSingle(); //                     //
            x2 = br.ReadSingle(); //                     //
            y2 = br.ReadSingle(); //                     //

            fs.Close(); // ファイルを閉じる
        }


        private void button1_Click(object sender, EventArgs e)
        {
            fm2.set_textbox_form2(x1,y1,x2,y2);

            if (fm2.ShowDialog() == DialogResult.OK)
            {
                pictureBox1.Invalidate();
            }
        }       

        public void send_textbox_form2(float _send_x1, float _send_y1, float _send_x2, float _send_y2)
        {
            using (BinaryWriter w = new BinaryWriter(File.OpenWrite(@"c:\data\test1.dat"))) // ファイルの書き込み
            {
                w.Write(_send_x1);
                w.Write(_send_y1);
                w.Write(_send_x2);
                w.Write(_send_y2);
            }

        }
   
    }
}


● Form2

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Collections;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;


namespace test1
{
    public partial class Form2 : Form
    {
        //Form1の宣言
        Form1 fm1;

        public float send_x1;
        public float send_y1;
        public float send_x2;
        public float send_y2;
        

        public Form2(Form1 _fm1)
        {
            InitializeComponent();

            //Form1のインスタンスを代入
            fm1 = _fm1;
        }

        public void set_textbox_form2(float _x1 , float _y1, float _x2, float _y2)
        {
            textBox1.Text = _x1.ToString();
            textBox2.Text = _y1.ToString();
            textBox3.Text = _x2.ToString();
            textBox4.Text = _y2.ToString();
        }

        private void pictureBox2_Paint(object sender, PaintEventArgs e)
        {
            // textbox1~4の値をform1へ渡す
            fm1.send_textbox_form2(send_x1, send_y1, send_x2, send_y2);
            fm1.draw(sender, e);
        }

        private void button2_Click(object sender, EventArgs e)
        {          
            this.Close();
        }



       // private void textBox1_Validating(object sender, CancelEventArgs e) 更新かからず・・・
        
        private void textBox1_TextChanged(object sender, EventArgs e)
        {
            send_x1 = float.Parse(textBox1.Text);
            pictureBox2.Invalidate();
        }

        private void textBox2_TextChanged(object sender, EventArgs e)
        {
            send_y1 = float.Parse(textBox2.Text);
            pictureBox2.Invalidate();
        }

        private void textBox3_TextChanged(object sender, EventArgs e)
        {
            send_x2 = float.Parse(textBox3.Text);
            pictureBox2.Invalidate();
        }

        private void textBox4_TextChanged(object sender, EventArgs e)
        {
             send_y2 = float.Parse(textBox4.Text);
            pictureBox2.Invalidate();
        }

       
    }
}




引用返信 編集キー/
■77171 / inTopicNo.18)  Re[12]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ 魔界の仮面弁士 (504回)-(2015/09/18(Fri) 17:13:37)
No77169 (ゆーきゃん さん) に返信
> というエラーが発生してしまいました。

float val = float.Parse(文字列);

という構文では、解析に失敗するとエラーとなります。
これを TryParse に書き換える場合、下記のようにします。

float val;
if(float.TryParse(文字列, out val))
{
 // ここで val を使う
}


TryParse が true を返した場合には、変換が成功したという事です。
数値化できないような文字列が渡された場合、TryParse は false を返します。
この場合、val には既定値である 0.0 がセットされます。




> send_x1 = float.TryParse(textBox1.Text);
>「error CS1501: 引数を 1 個指定できる、メソッド 'TryParse' のオーバーロードはありません」

float の TryParse メソッドが持つオーバーロードには、以下の 2 種類があります。
 引数 2 個の TryParse( string型, out float型) と
 引数 4 個の TryParse( string型, NumberStyles型, IFormatProvider型, out float型)


しかし、引数 1 個で呼び出せるオーバーロードは存在しないため、
float.TryParse(textBox1.Text) だとエラーになったということです。



> // Form2にインスタンスを作成してForm1のインスタンスを渡す
> fm2 = new Form2(this);

そのような書き方にすると、Form2 は「Form1 からしか呼び出せない画面」になるため、
使い回しが効かなくなります。個人的にはあまりお奨めしません。

たとえば、Form1 からしか呼び出せないメッセージボックス画面と
Form2 からしか呼び出せないメッセージボックス画面とがあるよりは、
「どの画面からでも呼び出せる MessageBox.Show」の方が汎用性は高いわけで。


> ● Form2
> fm1.send_textbox_form2(send_x1, send_y1, send_x2, send_y2);
これは結局のところ
 「子画面が、親画面のメソッドを直接操作する」
という動作になりますよね。
先の回答にも書きましたが、それはあまり良い実装ではありません。


---
(A) test1.dat を読み取るのは、Form1 側の役目? Form2 側の役目?
→ 今回は、アプリ起動時に Form1 に表示しておきたいのですから、
 Form1 側で管理するのが良いでしょう。
 (もしくは、test1 用の管理クラスを作り、それを Form1 で呼び出すとか)


(B) 座標データを編集するのは、Form1 側の役目? Form2 側の役目?
→ 今回は Form2 側(のTextBox)ですよね。


(C) B で編集した結果を test1.dat に保存させるのは、Form1 側? Form2 側?
→ どちらでも良いですが、読み込みを Form1 で行わせているのなら、
 書き込みも Form1 で行ったほうがわかりやすいでしょう。


(D) B で編集している内容を、Form2 の PictureBox に描画するのは、どちらの役目?
→ Form2 の PictureBox を操作するのですから、Form2 側で行うべきです。


(E) B で編集した結果を Form1 の PictureBox に描画するのは、どちらの役目?
→ Form1 の PictureBox を操作するのですから、Form1 側で行うべきです。


(F) D と E は同じ描画処理なので、共通化できないか?
→ (案0) 共通化せずに、描画処理を個別に再実装して凌ぐ
→ (案1) 描画処理を行うためのクラスを別途用意し、それを Form1 と Form2 の両方から利用する
→ (案2) Form1 側に描画メソッドを用意しておき、そのデリゲートインスタンスを Form2 に渡して利用させる。
→ (案3) Form2 側に「描画結果」の Bitmap を返却するプロパティを用意しておき、
   ShowDialog された Form2 が閉じられた後に、Form1 がそのプロパティの画像を
   Form1.pictureBox1 の Image / Background にセットする


(G) test1.dat を Form1 で読むなら、その座標情報を Form2 に渡すにはどうすれば?
→ (案1) Form2 に座標情報を表すプロパティを用意し、それを Form1 からセットする
→ (案2) Form2 のコンストラクタで、座標情報を渡せるようにしておく
→ (案3) 座標情報を管理するクラスを別途用意し、そのインスタンスを Form1 と Form2 の両方で参照する


(H) Form2 で編集した座標情報を、Form1 側に伝えるにはどうすれば?
→ Form2 側に座標情報を表すプロパティを用意し、それを Form1 が読み取る

引用返信 編集キー/
■77173 / inTopicNo.19)  Re[13]: 親フォームから子フォームのメソッドへアクセス
□投稿者/ ゆーきゃん (58回)-(2015/09/18(Fri) 19:31:22)
No77171 (魔界の仮面弁士 さん) に返信

お答えありがとうございます。

動作の動きのイメージはできているのですが、それを実現させるための引き出しが乏しいので、なかなかうまくいかないですね。
こんなにもお答え頂いているのに成長できず申し訳ないです。

エラーに関しては追々見直させて頂きます。


>>// Form2にインスタンスを作成してForm1のインスタンスを渡す
>>fm2 = new Form2(this);
>
> そのような書き方にすると、Form2 は「Form1 からしか呼び出せない画面」になるため、
> 使い回しが効かなくなります。個人的にはあまりお奨めしません。


using (Form2 fm2 = new Form2()){}だと使い回しできるのでしょうか?
Form2(this)で、thisをForm2へ渡しているのがいけないのでしょうか?
※ this = Form1のデータと解釈しております。


>>fm1.send_textbox_form2(send_x1, send_y1, send_x2, send_y2);
> これは結局のところ
>  「子画面が、親画面のメソッドを直接操作する」
> という動作になりますよね。
> 先の回答にも書きましたが、それはあまり良い実装ではありません。

やはりそうですよね・・・。
自分はこのようにしないと、Form2からForm1へテキストボックス入力された値(変数)を渡せないのですが、他にどのような方法があるのでしょうか。
自分のこのプログラムは引数で無理やりデータを渡している形という解釈であっていますでしょうか。




> (A) test1.dat を読み取るのは、Form1 側の役目? Form2 側の役目?
> → 今回は、アプリ起動時に Form1 に表示しておきたいのですから、
>  Form1 側で管理するのが良いでしょう。
>  (もしくは、test1 用の管理クラスを作り、それを Form1 で呼び出すとか)

Form1内の
「public void yomikomi()」


> (B) 座標データを編集するのは、Form1 側の役目? Form2 側の役目?
> → 今回は Form2 側(のTextBox)ですよね。


Form2内の
「private void textBox1_TextChanged(object sender, EventArgs e)」


> (C) B で編集した結果を test1.dat に保存させるのは、Form1 側? Form2 側?
> (H) Form2 で編集した座標情報を、Form1 側に伝えるにはどうすれば?
> → Form2 側に座標情報を表すプロパティを用意し、それを Form1 が読み取る


Form1内の
「 public void send_textbox_form2(float _send_x1, float _send_y1, float _send_x2, float _send_y2)」

しかし、Form2からForm1のメソッドを直接操作しているため正しくない。
それを解消するためプロパティを用意。

// プロパティ
public double atai
{
set
{

}
get
{

}

プロパティとはこちらの形式であっていますでしょうか?



> (D) B で編集している内容を、Form2 の PictureBox に描画するのは、どちらの役目?
> → Form2 の PictureBox を操作するのですから、Form2 側で行うべきです。
>
>
> (E) B で編集した結果を Form1 の PictureBox に描画するのは、どちらの役目?
> → Form1 の PictureBox を操作するのですから、Form1 側で行うべきです。
>
>
> (F) D と E は同じ描画処理なので、共通化できないか?
> → (案0) 共通化せずに、描画処理を個別に再実装して凌ぐ
> → (案1) 描画処理を行うためのクラスを別途用意し、それを Form1 と Form2 の両方から利用する
> → (案2) Form1 側に描画メソッドを用意しておき、そのデリゲートインスタンスを Form2 に渡して利用させる。
> → (案3) Form2 側に「描画結果」の Bitmap を返却するプロパティを用意しておき、
>    ShowDialog された Form2 が閉じられた後に、Form1 がそのプロパティの画像を
>    Form1.pictureBox1 の Image / Background にセットする

(案2)の感覚で

public void draw(object sender, PaintEventArgs e)
{
yomikomi();
e.Graphics.DrawLine(Pens.Green, x1, y1, x2, y2);
}

を使用しているのですが、考え方が違いますでしょうか?

(案1)を実際使用していきたいのですが、クラス呼び出し指示方法が理解することができませんでした。


> (G) test1.dat を Form1 で読むなら、その座標情報を Form2 に渡すにはどうすれば?
> → (案1) Form2 に座標情報を表すプロパティを用意し、それを Form1 からセットする
> → (案2) Form2 のコンストラクタで、座標情報を渡せるようにしておく
> → (案3) 座標情報を管理するクラスを別途用意し、そのインスタンスを Form1 と Form2 の両方で参照する


プログラミングをデバッグなどで追って、データがどのように受け渡しされているのか知りたいのですが、
何か参考サイト・サンプル等御座いませんでしょうか。


引用返信 編集キー/
■77175 / inTopicNo.20)  Re[14]: 親フォームから子フォームのメソッドへアクセス
 
□投稿者/ 魔界の仮面弁士 (506回)-(2015/09/18(Fri) 20:49:41)
No77173 (ゆーきゃん さん) に返信
> 、他にどのような方法があるのでしょうか。

No77136 で、774RR さんが回答されていた方法が一般的かと思いますよ。


たとえば「ファイル選択画面」を呼び出す場合、
このようなコードを書きます。

  // 子画面を new して…
  using (var dlg = new OpenFileDialog())
  {
    // 表示前に各種プロパティをセットして…
    dlg.Filter = "Text|*.txt|All|*.*";
    dlg.FilterIndex = 1;
    dlg.FileName = "NewFile.txt";

    // 子画面をモーダルで表示!
    var result = dlg.ShowDialog(this);
    if (result == DialogResult.OK)
    {
      // 子画面で処理された結果をプロパティで受け取る
      var fileName = dlg.FileName;
      MessageBox.Show(fileName);
    }
    else
    {
      MessageBox.Show("キャンセルされた!");
    }
  }


今回の場合、OpenFileDialog の代わりに Form2 を使うという事です。

OpenFileDialog はファイル選択の画面なので、
FileName というプロパティが用意されていますね。

Form2 の場合は、「座標情報を返すプロパティ(あるいはメソッド)」を
用意してみては如何でしょう?


> Form2(this)で、thisをForm2へ渡しているのがいけないのでしょうか?

そもそも今回の作りであれば、this を渡す必要は無い気もしますが、
別に this を渡すのが問題だと言っているわけではありません。
Form2 クラスが、Form1 に依存しているが問題だということです。


上記で例としてあげた OpenFileDialog は、どんなフォームからでも呼べますよね。
Form1 が OpenFileDialog を必要とすることはあっても、
OpenFileDialog が Form1 を必要とすることはありません。

それと同じで、Form1 が Form2 の機能を呼び出すのは OK ですが、
Form2 が Form1 の機能を呼び出すのは、原則として NG です。


> ※ this = Form1のデータと解釈しております。
仮に、this を渡すにしても、
 public Form2(Form1 _fm1)
などと、「Form1」という限定された型として受け渡すのではなく、
 public Form2(IHoge _fm1)
のように、共通インターフェイスを渡すとか、
 fm2.Owner = this;
のように、「Form」などの抽象度の高い型を使うようにすることで、
依存度を軽減する手はあります。…今回は使う必要の無い話ですけれども。



>>>fm1.send_textbox_form2(send_x1, send_y1, send_x2, send_y2);
>>先の回答にも書きましたが、それはあまり良い実装ではありません。
> 自分はこのようにしないと、Form2からForm1へテキストボックス入力された値(変数)を渡せないのですが、他にどのような方法があるのでしょうか。

今回の OpenFileDialog の例のように、「プロパティ」を用意しましょう。
たとえば、Form1 側は、

 if (fm2.ShowDialog() == DialogResult.OK)
 {
  this.x1 = fm2.x1;
  this.x2 = fm2.x2;
  this.y1 = fm2.y1;
  this.y2 = fm2.y2;
  pictureBox1.Invalidate();
 }

などです。

実際には 4 つも受け渡しするのは冗長なので、
「4 つの float 型メンバーを持った構造体」などを用意し、
それを受け渡すなどした方が良いでしょうね。


> 自分のこのプログラムは引数で無理やりデータを渡している形という解釈であっていますでしょうか。
メソッドやコンストラクタの引数であれ、プロパティであれ、イベント引数であれ、
データの受け渡し方法は自由に決めてしまって構いません。問題はデータを流す方向です。

「必要なデータを、親が子に渡す」のは OK ですが、
「子が親に、必要なデータを問い合わせる」のは避けるべきです。

「子画面で編集したデータを、親が子に問い合わせる」のも OK ですね。


> それを解消するためプロパティを用意。
> public double atai
必要な座標データは、(4 つの) float 値なのですから、
double 型にしてしまっては駄目でしょう。

> public double atai
> {
> set
> {
>
> }
> get
> {
>
> }


> プロパティとはこちらの形式であっていますでしょうか?
あっていますが、get / set ブロックの中身が空っぽなので、このままでは動作しません。

public double atai { get; set; }

による「自動実装プロパティ」形式にするか、

private double _atai = 0;
public double atai
{
 get { return _atai; }
 set { _atai = value; }
}

形式の実装にします。


そしてそれらのプロパティから、
TextBox で編集された結果が得られるようにしておけば、
Form1 側は、Form2 の編集結果を
 fm2.ShowDialig();  // 編集画面を表示
 x1 = fm2.atai;  // 編集結果を受信
などのようにして取得することができるというわけです。
引用返信 編集キー/

次の20件>
トピック内ページ移動 / << 0 | 1 >>

管理者用

- Child Tree -