[C#]コードを短くしたい

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
piece

[C#]コードを短くしたい

#1

投稿記事 by piece » 11年前

はじめまして。
独学でC#の学習を進めております、pieceと申します。

現在、フォームに複数のピクチャーボックスを配置し、それをクリックすると
2枚の画像が切り替えられる、というものを作っております。
すべて異なる画像です。

一応できたのはできたのですが、ピクチャーボックス1つにイベントがそれぞれあるので、
ダラダラと長いコードになってしまっています。
検索して、sender(?)を使うらしいことはわかったのですが、これだと結局、
画像毎に処理を書かないといけなくなってしまい、あまり短くなりません。

なにか良い方法はありませんでしょうか。
初心者にもわかりやすいサイトも教えていただけると助かります。

よろしくお願いします。

YuO
記事: 947
登録日時: 13年前
住所: 東京都世田谷区

Re: [C#]コードを短くしたい

#2

投稿記事 by YuO » 11年前

とりあえず,言葉で書くとこんな感じでしょうか。
  • ピクチャーボックスごとの画像とどちらの画像を表示するのかを処理するクラス (A) を作成する
  • 各ピクチャーボックスと対応するAのインスタンスを組にしてDictionary (B)に登録する
  • ピクチャーボックス (C) のClickイベントでBからピクチャーボックスCに対応するAのインスタンス (D) を取得する
  • Dの状態を遷移させて,表示する画像の情報をアップデートし,その情報を元にCの画像を切り替える
単純に考え方だけです。
状態遷移があるならクラス化してしまえばよいですし,場合によってはyield returnによる継続が使えるでしょう。

アバター
バグ
記事: 130
登録日時: 13年前
住所: 愛媛県
連絡を取る:

Re: [C#]コードを短くしたい

#3

投稿記事 by バグ » 11年前

どのPictureBoxも同じ動きをするのならば、PictureBoxを継承した独自のクラスを作成するってのはいかがでしょうか?

追加する機能としては・・・

1:2枚の画像をプロパティで設定できるようにする
2:クリックされたら表示されてある画像を切り替える

の2つでいいんですよね?
これが完成したら、表示したいフォームにペタペタ貼り付けて、画像を登録してやるだけでいいはずです。

piece

Re: [C#]コードを短くしたい

#4

投稿記事 by piece » 11年前

YuOさん、バグさん返信ありがとうございます。

お二方の返信共、クラスを作成するとのことでしたが、今まで「Form1.cs」にしか
処理を書いたことがなく???です。

Visual Studioのプロジェクトメニューからクラスの追加というのはしてみたのですが、
どう書けばプロパティを設定できるのか?画像が切り替えられるのか?と?だらけです。

検索してみてPictureBoxを継承するというのは、

コード:

namespace System.Windows.Forms
{
    public class PictureBox2 : System.Windows.Forms.PictureBox
    {
        public int Hoge;
        public string HogeHoge;

        public int hoge
        {
            get
            {

            }
            set
            {

            }
    }
}
↑これでいけていると思うのですが、その後がさっぱりです。。。

get,setにどのような処理を書けば良いのでしょうか。
またそもそもこんな書き方でOKなのでしょうか。

わかりにくい質問かとは思いますが、よろしくお願いします。

アバター
バグ
記事: 130
登録日時: 13年前
住所: 愛媛県
連絡を取る:

Re: [C#]コードを短くしたい

#5

投稿記事 by バグ » 11年前

とりあえず、現状のソースコードを掲載することは可能でしょうか?
そうすることで、pieceさんのやりたいことがわかりやすいと思いますので、よろしくお願いします。

piece

Re: [C#]コードを短くしたい

#6

投稿記事 by piece » 11年前

バグさん早速のご回答ありがとうございます。

やはり伝わりにくかったですかね。。。
以下ソースです。

コード:

namespace 画像切り替え
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            this.Text = "画像切り替え";

            pictureBoxA.ImageLocation = @"C:\Data\a1.jpg";
            pictureBoxA.SizeMode = PictureBoxSizeMode.CenterImage;
            pictureBoxB.ImageLocation = @"C:\Data\b1.jpg";
            pictureBoxB.SizeMode = PictureBoxSizeMode.CenterImage;
            pictureBoxC.ImageLocation = @"C:\Data\c1.jpg";
            pictureBoxC.SizeMode = PictureBoxSizeMode.CenterImage;
     //☆☆☆以下ずらずらと、ピクチャーボックス分あります。☆☆☆
        }

        private void Form1_DoubleClick(object sender, EventArgs e)
        {
      //フォルダの画像が変更されたらダブルクリックで反映できるようにしたい。
        }

        private void pictureBoxA_Click(object sender, EventArgs e)
        {
            if (pictureBoxA.ImageLocation == @"C:\Data\Button\a1.jpg")
            {
                pictureBoxA.ImageLocation = @"C:\Data\Button\a2.jpg";
            }
            else
            {
                pictureBoxA.ImageLocation = @"C:\Data\Button\a1.jpg";
            }
        }

        private void pictureBoxB_Click(object sender, EventArgs e)
        {
            if (pictureBoxB.ImageLocation == @"C:\Data\Button\b1.jpg")
            {
                pictureBoxB.ImageLocation = @"C:\Data\Button\b2.jpg";
            }
            else
            {
                pictureBoxB.ImageLocation = @"C:\Data\Button\b1.jpg";
            }
        }
     //☆☆☆以下ずらずらと、ピクチャーボックス分あります。☆☆☆
    }
}
以上のようになっております。
動くには動くのですが、もう少し読みやすいコードにならないかと質問しました。

よろしくお願いします。

Ryo

Re: [C#]コードを短くしたい

#7

投稿記事 by Ryo » 11年前

減らすためには
A.そっくりな関数をまとめる
B.ファイルパスで管理している画像の管理方法をなんとかする
の2つの方向があると思います。

Aについてだけ
C#でも、複数のコントロール(今回はピクチャーボックス)のクリックイベントから、同じ関数を呼び出すように設定できます

作り方
1.とりあえず一つ(ここで言えばpictureBoxA_Click)つくる。
2.B以降のピクチャーボックスは
 「フォームデザイナーのプロパティウィンドウ>イベント>Click」に
 pictureBoxA_Clickを登録(コンボボックスの候補に入ってる)

こうすれば、ピクチャーボックスAだけに限らず、すべてのピクチャーボックスのクリックイベントはpictureBoxA_Clickを呼び出せる。
#どのピクチャーボックスから呼び出されたかは、senderで判断。継承クラス利用するならそもそも判断すらいらないが

piece

Re: [C#]コードを短くしたい

#8

投稿記事 by piece » 11年前

Ryoさんご回答ありがとうございます。

検索していて「sender」でまとめられると言う情報は見つけ、
以下のコードまではわかったのですが、それ以降に書く画像の表示の部分を
どのようにまとめると良いのかわかりません。

コード:

private void Images_Click(object sender, EventArgs e)
        {
                
            PictureBox pb = (PictureBox)sender;


    }
なにか良い方法はあるのでしょうか。

よろしくお願いします。

アバター
バグ
記事: 130
登録日時: 13年前
住所: 愛媛県
連絡を取る:

Re: [C#]コードを短くしたい

#9

投稿記事 by バグ » 11年前

最初に紹介した方法とは違いますが、こんな方法はいかがでしょうか?

まず、下記のようなクラスを作成します。

コード:

using System;
using System.Windows.Forms;

public class ImageLocationSelector
{
	/// <summary>
	/// 画像ファイル1のパス
	/// </summary>
	public string ImageLocation1
	{
		get;
		set;
	}

	/// <summary>
	/// 画像ファイル2のパス
	/// </summary>
	public string ImageLocation2
	{
		get;
		set;
	}

	/// <summary>
	/// ピクチャボックスがクリックされた際の処理
	/// </summary>
	/// <param name="sender">イベント発生元のピクチャボックス</param>
	public void PictureBox_Click(object sender, EventArgs e)
	{
		PictureBox pict = (PictureBox)sender;
		pict.ImageLocation =
			(pict.ImageLocation != ImageLocation1) ? ImageLocation1 : ImageLocation2;
	}
}

下記をフォームへ追加します。

コード:

public List<ImageLocationSelector> m_selector = new List<ImageLocationSelector>();

private void Form1_Load(object sender, EventArgs e)
{
	// フォームへ貼り付けてあるピクチャボックスで初期化してください
	PictureBox[] pict =
	{
		pictureBoxA,
		pictureBoxB,
		pictureBoxC,
		pictureBoxD,
		pictureBoxE
	};

	// 画像選択用クラス
	// ※ImageLocation1とImageLocation2の値を任意の画像へのパスで初期化してください。
	//   ピクチャボックスの数だけ必要です。
	ImageLocationSelector[] select =
	{
		new ImageLocationSelector() { ImageLocation1 = string.Empty, ImageLocation2 = string.Empty },
		new ImageLocationSelector() { ImageLocation1 = string.Empty, ImageLocation2 = string.Empty },
		new ImageLocationSelector() { ImageLocation1 = string.Empty, ImageLocation2 = string.Empty },
		new ImageLocationSelector() { ImageLocation1 = string.Empty, ImageLocation2 = string.Empty },
		new ImageLocationSelector() { ImageLocation1 = string.Empty, ImageLocation2 = string.Empty },
	};

	for (int i = 0; i < pict.Length; ++i)
	{
		// ピクチャボックスのクリックイベントと画像選択クラスのメソッドを関連付けしています。
		pict[i].Click += select[i].PictureBox_Click;

		// リストへ追加
		m_selector.Add(select[i]);
	}
}
最後に編集したユーザー バグ on 2013年1月30日(水) 20:25 [ 編集 1 回目 ]

piece

Re: [C#]コードを短くしたい

#10

投稿記事 by piece » 11年前

もう1点質問したいところが出て来ました。

とりあえずまだなんとかなりそうな気がしますが、まとめる部分に関しては
だいぶ短縮できました。
以下が、コードです。

もう1点質問というのは、「フォルダの画像が変更されたらダブルクリックで反映できるようにしたい。」
というところです。
単純にFormのダブルクリックイベントに「Refresh」を書けば良いと思っていましたが、
それでは更新されないのでしょうか。

あと更新しても初期化はされないで、変更した部分だけ画像が変わるようにするには
どのようにすると出来るのでしょうか。

よろしくお願いします。

コード:

namespace xxxxx
{
    public partial class Form1 : Form
    {
        private PictureBox[] b_PictureBox = new PictureBox[25];
        private Image[] Image = new Image[50];
        private string[] imPass = {"a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", 
                                   "i1", "j1", "k1", "l1", "m1", "n1", "o1", "p1", 
                                   "q1", "r1", "s1", "t1", "u1", "v1", "w1", "x1", "y1", 
                                   "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2", 
                                   "i2", "j2", "k2", "l2", "m2", "n2", "o2", "p2", 
                                   "q2", "r2", "s2", "t2", "u2", "v2", "w2", "x2", "y2", };

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            b_PictureBox[0] = pictureBoxA;
            b_PictureBox[1] = pictureBoxB;
            b_PictureBox[2] = pictureBoxC;
            b_PictureBox[3] = pictureBoxD;
            b_PictureBox[4] = pictureBoxE;
            b_PictureBox[5] = pictureBoxF;
            b_PictureBox[6] = pictureBoxG;
            b_PictureBox[7] = pictureBoxH;
            b_PictureBox[8] = pictureBoxI;
            b_PictureBox[9] = pictureBoxJ;
            b_PictureBox[10] = pictureBoxK;
            b_PictureBox[11] = pictureBoxL;
            b_PictureBox[12] = pictureBoxM;
            b_PictureBox[13] = pictureBoxN;
            b_PictureBox[14] = pictureBoxO;
            b_PictureBox[15] = pictureBoxP;
            b_PictureBox[16] = pictureBoxQ;
            b_PictureBox[17] = pictureBoxR;
            b_PictureBox[18] = pictureBoxS;
            b_PictureBox[19] = pictureBoxT;
            b_PictureBox[20] = pictureBoxU;
            b_PictureBox[21] = pictureBoxV;
            b_PictureBox[22] = pictureBoxW;
            b_PictureBox[23] = pictureBoxX;
            b_PictureBox[24] = pictureBoxY;

            for (int i = 0; i < 25; i++)
            {
                FileStream fs;
                fs = new FileStream(@"C:\Data\" + imPass[i] + ".jpg", FileMode.Open, FileAccess.Read);
                b_PictureBox[i].Image = System.Drawing.Image.FromStream(fs);
                fs.Close();
                Image[i] = b_PictureBox[i].Image;
                fs = new FileStream(@"C:\Data\" + imPass[i + 25] + ".jpg", FileMode.Open, FileAccess.Read);
                b_PictureBox[i].Image = System.Drawing.Image.FromStream(fs);
                fs.Close(); 
                Image[i + 25] = b_PictureBox[i].Image;
            }
        }

        private void Images_Click(object sender, EventArgs e)
        {
            PictureBox pb = (PictureBox)sender;
            
            for (int i = 0; i < 25; i++)
            {
                if (pb.Image == Image[i])
                {
                    pb.Image = Image[i + 25];
                }
                else if (pb.Image == Image[i + 25])
                {
                    pb.Image = Image[i];
                }
            }
        }

アバター
バグ
記事: 130
登録日時: 13年前
住所: 愛媛県
連絡を取る:

Re: [C#]コードを短くしたい

#11

投稿記事 by バグ » 11年前

>>もう1点質問というのは、「フォルダの画像が変更されたらダブルクリックで反映できるようにしたい。」
というところです。
単純にFormのダブルクリックイベントに「Refresh」を書けば良いと思っていましたが、
それでは更新されないのでしょうか。

「フォルダの画像が変更されたら」というのは、(ペイントソフトのような)別のアプリで編集をして更新されたらという意味ですか?


>>あと更新しても初期化はされないで、変更した部分だけ画像が変わるようにするには
どのようにすると出来るのでしょうか。

初期化とは具体的にどのような状況を指しますか?

piece

Re: [C#]コードを短くしたい

#12

投稿記事 by piece » 11年前

バグさんご回答ありがとうございます。

すごいですね。。。
私のコードとはまとまりが全然違いますね。
もう少し待ってから自分のコードをあげたらよかったと後悔です。。。

-------
また質問が下手でしたね。
申し訳ありません。
>>「フォルダの画像が変更されたら」というのは、(ペイントソフトのような)別のアプリで編集をして更新されたらという意味ですか?

その通りです。

>>初期化とは具体的にどのような状況を指しますか?

例えばアプリで5つの画像を編集をして保存しFormをダブルクリックすると、編集していない他の表示されている画像はそのままで
5つの編集した画像だけが変わるような処理にしたいのです。
編集した画像でも表示されていなければ、クリックして表示された時点で更新されていればOKです。

よろしくお願いします。

教えていただいたバグさんのコードでも一度やってみようと思います。

ありがとうございます。

アバター
バグ
記事: 130
登録日時: 13年前
住所: 愛媛県
連絡を取る:

Re: [C#]コードを短くしたい

#13

投稿記事 by バグ » 11年前

ピクチャボックスへ表示されてあるイメージは、ファイルから読み込んだ時点の状態のものですので、再度読み込んでやることで解決できそうな気がします。
下記ソースは例外処理などを省いてますので、必要に応じて追加してください。

コード:

private void Form1_MouseDoubleClick(object sender, MouseEventArgs e)
{
	// フォームへ貼り付けてあるピクチャボックスで初期化してください
	PictureBox[] pict =
	{
		pictureBox1,
		pictureBox2,
		pictureBox3,
		pictureBox4,
		pictureBox5
	};

	// イメージを更新する
	foreach (PictureBox tmp in pict)
	{
		tmp.Image = new Bitmap(tmp.ImageLocation);
	}
}

Idra

Re: [C#]コードを短くしたい

#14

投稿記事 by Idra » 11年前

画像の更新の辺りは、ロード時にファイルの更新日時を取得しておいて、
ダブルクリック時に最新の更新日時と比較すればできます。

以下俺だったらこうするって感じのソース

コード:

    public partial class Form1 : Form {
        List<ToggleImage> ToggleImages = new List<ToggleImage>();

        public Form1() {
            InitializeComponent();
        }

        //イベントを登録する他にオーバーライドする方法もあります
        protected override void OnLoad(EventArgs e) {
            base.OnLoad(e);
            this.AddToggleImage(this.pictureBox1, "a");
            this.AddToggleImage(this.pictureBox2, "b");
            //this.AddToggleImage(this.pictureBox3, "c");
            //this.AddToggleImage(this.pictureBox4, "d");
            //this.AddToggleImage(this.pictureBox5, "e");
            foreach (var i in ToggleImages) {
                i.PictureBox.SizeMode = PictureBoxSizeMode.CenterImage;
                i.Load();
            }
        }

        void AddToggleImage(PictureBox pictureBox, string baseName) {
            //状況に合わせてパスや拡張子を変更して下さい
            var primary = string.Format(@"image\{0}1.png", baseName);
            var secondary = string.Format(@"image\{0}2.png", baseName);
            ToggleImages.Add(new ToggleImage(pictureBox, primary, secondary));
        }

        protected override void OnDoubleClick(EventArgs e) {
            base.OnDoubleClick(e);
            foreach (var i in ToggleImages) {
                i.Load();
            }
        }
    }

  //関係が深いものをクラスにまとめるとプログラミングがし易い
    public class ToggleImage {
        public bool Selected { get; private set; }
        public PictureBox PictureBox { get; private set; }
        public string PrimaryImageUrl { get; private set; }
        public string SecondaryImageUrl { get; private set; }
        //新規ロード時のためにnullを許容します
        DateTime? ImageLastWriteTime;

        public ToggleImage(PictureBox pictureBox, string primaryImageUrl, string secondaryImageUrl) {
            this.PictureBox = pictureBox;
            this.PrimaryImageUrl = primaryImageUrl;
            this.SecondaryImageUrl = secondaryImageUrl;
            this.PictureBox.Click += new EventHandler(PictureBox_Click);
        }

        void PictureBox_Click(object sender, EventArgs e) {
            Selected = !Selected;
            ImageLastWriteTime = null;
            Load();
        }

        public void Load() {
            var url = (Selected) ? SecondaryImageUrl : PrimaryImageUrl;
            var lastWriteTime = System.IO.File.GetLastWriteTime(url);
            //新規ロード時、または前回のロード時からファイルが更新されている場合にロードする
            if (this.ImageLastWriteTime == null || this.ImageLastWriteTime != lastWriteTime) {
                this.ImageLastWriteTime = lastWriteTime;
                this.PictureBox.Load(url);
            }
        }
    }

Ryo

Re: [C#]コードを短くしたい

#15

投稿記事 by Ryo » 11年前

私はこんな感じ

コード:

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        //////////////////////////////////////////////////
	//初回設定
        private void Form1_Load(object sender, EventArgs e)
        {
            pictureBoxEx1.Init(@"c:\test1.bmp", @"c:\test2.bmp");
            pictureBoxEx2.Init(@"c:\test3.bmp", @"c:\test4.bmp");
        }        
        
        //////////////////////////////////////////////////
	//ピクチャーボックスクリック
        private void pictureBoxEx_Click(object sender, EventArgs e)
        {
            PictureBoxEx pict = sender as PictureBoxEx;
            if (pict == null) return;
            pict.ChangeView();
        }

        //////////////////////////////////////////////////
	//フォームのダブルクリック
        private void Form1_DoubleClick(object sender, EventArgs e)
        {
            //左確認
            MouseEventArgs eve = e as MouseEventArgs;

            if (eve.Button != MouseButtons.Left) return;

            //全コントロール確認
            foreach (Control ctl in this.Controls)
            {
                PictureBoxEx pict = ctl as PictureBoxEx;
                if (pict == null) continue;
                pict.UpdateView();
            }
        }
    }

    // PictureBoxを継承した独自クラス
    public class PictureBoxEx : PictureBox  
    {
        // データ
        String[]      path = new String[2];
        DateTime[]    date = new DateTime[2];

        //////////////////////////////////////////////////
	//初期化
        public void Init(String path1, String path2)
        {
            path[0] = path1;
            path[1] = path2;
            date[0] = System.IO.File.GetLastWriteTime(path1);
            date[1] = System.IO.File.GetLastWriteTime(path2);
            ImageLocation = path[0];
        }
        
        //////////////////////////////////////////////////
        // 入れかえ
        public void ChangeView()
        {
            if (ImageLocation == path[0])
                ImageLocation = path[1];
            else
                ImageLocation = path[0];
        }

        //////////////////////////////////////////////////
        // 更新
        public void UpdateView()
        {
            int         index;
            DateTime    tmpdate;

            if (this.ImageLocation == path[0])
                index = 0;
            else
                index = 1;

            tmpdate = System.IO.File.GetLastAccessTime(path[index]);

            if (tmpdate == date[index]) return;

            //更新
            date[index] = tmpdate;
            ImageLocation = path[index];
        }
    }
フォームデザインするさいに自作のコントロールクラスを張り付けてますが

piece

Re: [C#]コードを短くしたい

#16

投稿記事 by piece » 11年前

みなさんご回答ありがとうございます。

しかもコード付きで頂いて感謝、感謝です。
まだ最後まで追えていないので、みなさんのコードで試して見ようと思います。

ありがとうございます。

今のところ出来ている私のコードが以下です。

コード:

namespace xxxxx
{
    public partial class Form1 : Form
    {
        private PictureBox[] b_PictureBox = new PictureBox[25];
        private Image[] Image = new Image[50];
        private string[] imPass = {"a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", 
                                   "i1", "j1", "k1", "l1", "m1", "n1", "o1", "p1", 
                                   "q1", "r1", "s1", "t1", "u1", "v1", "w1", "x1", "y1", 
                                   "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2", 
                                   "i2", "j2", "k2", "l2", "m2", "n2", "o2", "p2", 
                                   "q2", "r2", "s2", "t2", "u2", "v2", "w2", "x2", "y2", };

        public Form1()
        {
            InitializeComponent();
        }

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

        private void Images_Click(object sender, EventArgs e)
        {
            PictureBox pb = (PictureBox)sender;
            
            for (int i = 0; i < 25; i++)
            {
                if (pb.Image == Image[i])
                {
                    pb.Image = Image[i + 25];
                }
                else if (pb.Image == Image[i + 25])
                {
                    pb.Image = Image[i];
                }
            }
        }

        private void Form1_DoubleClick(object sender, EventArgs e)
        {
            pictures();
        }

        private void pictures()
        {
            PictureBox[] b_PictureBox = 
            {
                pictureBoxA, pictureBoxB, pictureBoxC, pictureBoxD, pictureBoxE,
                pictureBoxF, pictureBoxG, pictureBoxH, pictureBoxI, pictureBoxJ,
                pictureBoxK, pictureBoxL, pictureBoxM, pictureBoxN, pictureBoxO,
                pictureBoxP, pictureBoxQ, pictureBoxR, pictureBoxS, pictureBoxT,
                pictureBoxU, pictureBoxV, pictureBoxW, pictureBoxX, pictureBoxY
            };

            for (int i = 0; i < 25; i++)
            {
                FileStream fs;
                fs = new FileStream(@"C:\test\" + imPass[i] + ".jpg", FileMode.Open, FileAccess.Read);
                b_PictureBox[i].Image = System.Drawing.Image.FromStream(fs);
                fs.Close();
                Image[i] = b_PictureBox[i].Image;
                fs = new FileStream(@"C:\test\" + imPass[i + 25] + ".jpg", FileMode.Open, FileAccess.Read);
                b_PictureBox[i].Image = System.Drawing.Image.FromStream(fs);
                fs.Close();
                Image[i + 25] = b_PictureBox[i].Image;
            } 
        }
    }
}


閉鎖

“C言語何でも質問掲示板” へ戻る