visual c#のウィンドウプログラミングの質問なんですが

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

visual c#のウィンドウプログラミングの質問なんですが

#1

投稿記事 by » 15年前

初投稿失礼します。
大学講義のアシスタントとして参加することとなったんですが、現在下級生はこの先大学で学ぶことないC#の練習を
受け持ち教授の趣味でやってます。趣味なので、当然私を含めた上級生全員習ってません。
講義本番に向け皆で集まりいろいろと頑張ったんですが、私たちにはどうしても解けない問題が1つ残ってしまいました。

以下のプログラムのMyCircleを中心に改造して同じ感じで三角形が表示できるように
したいんですが、まったく歯が立ちませんでした。
同じサークルの子に「頼りにしてます」などと言われかなり切羽つまってます。
どなたか助けてください。

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

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

private void Form1_Paint(object sender, PaintEventArgs e)
{
MyCircle mc = new MyCircle();
mc.AddCircles(100, 100, 100, 7);
mc.Draw(e.Graphics);
}
}
class MyCircle
{
Random _rnd;
MyCircle[] _Child;
SolidBrush _opaqueBrush;
int _radius;
double _x;
double _y;
public MyCircle()
{
_rnd = new Random();
}
public void AddCircles( double x, double y, int radius, int num)
{
_x = x;
_y = y;
_radius = radius;
int value = 126 * num / 6;
_opaqueBrush = new SolidBrush(Color.FromArgb(value, 0, 0, 153));
if (num > 1)
{
int branches = _rnd.Next(2, 6);
_Child = new MyCircle[branches];
for (int j = 0; j < branches; j++)
{
double a = _rnd.NextDouble() * 2.0 * Math.PI;
double newx = x + Math.Cos(a) * 6.0 * (num - 1);
double newy = y + Math.Sin(a) * 6.0 * (num - 1);
_Child[j] = new MyCircle();
_Child[j].AddCircles(newx, newy, radius / 2, (num - 1));
}
}
}
public void Draw(Graphics g)
{
g.FillEllipse(_opaqueBrush, (int)_x, (int)_y, _radius * 2, _radius * 2);
if (_Child != null)
{
foreach (MyCircle mc in _Child)
{
mc.Draw(g);
}
}
}
}
}

たろ

Re: visual c#のウィンドウプログラミングの質問なんですが

#2

投稿記事 by たろ » 15年前

「同じ感じで三角形」というのは、三角形なら何でもいいんでしょうか?
例えば、g.FillEllipse() をこんな↓感じの処理に差し替えれば、三角形にはなります。

コード:

// 三角形の頂点座標
Point[] points =
{
	new Point((int)_x, (int)_y - _radius),
	new Point((int)_x - _radius, (int)_y + _radius),
	new Point((int)_x + _radius, (int)_y + _radius),
};
// 三角形描画
g.FillPolygon(_opaqueBrush, points);
ただし全て同じ向きの単純な二等辺三角形です。
正三角形にする、ランダムに回転させる、などはさらに工夫が必要です。

Re: visual c#のウィンドウプログラミングの質問なんですが

#3

投稿記事 by » 15年前

解答ありがとうございます。おかげで三角形は何とかなりそうです。
ついでと言ってはなんですが、もう1つ教えていただけませんか?
このプログラム(元のほう)、実行してみたんですが、
ウィンドウサイズを変えていると円が途切れたりして不自然な形になってこまってます。
最初の一番大きな円に不具合はおきないのでおそらくAddCircles()の中に原因がありそう
だとは思うんですが……

たろ

Re: visual c#のウィンドウプログラミングの質問なんですが

#4

投稿記事 by たろ » 15年前

「円が途切れたり・・」とは、ウィンドウサイズを小さくした後で大きくした時に、
ずっと見えていた部分の表示内容が、更新されずに残ってしまう現象ですか?

よい回避方法なのかちょっと自信がないですが、ウィンドウサイズ変更完了の
イベントハンドラで、Invalidate() を実行すれば回避できるようです。
※VisualStudioのIDEでイベントハンドラを作って中身を記述。

コード:

private void Form1_ResizeEnd(object sender, EventArgs e)
{
	Invalidate();
}
これでウィンドウサイズを変更した場合に、ウィンドウの内容全体が更新されます。

以下はちょっと余計な世話焼きかもしれませんが・・
同様の問題は他にも残っていて、例えばウィンドウを半分だけ別のウィンドウで隠した後、
元に戻してみると、同じように、ずっと見えていた部分は更新されずに残ってしまうと思います。

これらの挙動は、Windows的な仕様であり、「更新リージョン」「無効リージョン」等と呼ばれる
機能からくるものです。

個人的には、表示が乱れないようにちゃんと(?)するには、
・Form1_Paint() でいつも「円のランダム生成と描画」を行うのをやめる。
・表示する画像をメモリ上に作っておき、必要な時にその画像を画面に転送(表示)する。
という実装に変更した方がいいんじゃないかなーと思います。

Re: visual c#のウィンドウプログラミングの質問なんですが

#5

投稿記事 by » 15年前

みなさん解答ありがとうございます。
おかげで完成した、と思ったんですが、教授に確認のため見せたところ、違うと一蹴されました。
なんでも一度表示されたものを維持しろとのことで、この走り書きだけくれました。

コード:

using System.Collections.Generic;

List<MyCircle> a = new List a<MyCircle>
a.Add(new MyCircle)(/*コンストラクタ引数*/));
a.Add(new MyCircle)(/*コンストラクタ引数*/));
MyCircle x = a[10];
お手上げです。 この方法で行くなら、こちら原題ですが、

コード:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
 
namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        Random rnd = new Random();
        public Form1()
        {
            InitializeComponent();
        }
 
        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            DrawCircles(e.Graphics, 100, 100, 100, 7);
        }
        private void DrawCircles(Graphics g, double x, double y, int radius, int num)
        {
            int value = 126 * num / 6;
            SolidBrush opaqueBrush = new SolidBrush(Color.FromArgb(value, 0, 0, 153));
            g.FillEllipse(opaqueBrush, (int)x, (int)y, radius * 2, radius * 2);
            if (num > 1)
            {
                int branches = rnd.Next(2, 6);
                for (int j = 0; j < branches; j++)
                {
                    double a = rnd.NextDouble() * 2.0 * Math.PI;
                    double newx = x + Math.Cos(a) * 6.0 * (num - 1);
                    double newy = y + Math.Sin(a) * 6.0 * (num - 1);
                    DrawCircles(g, newx, newy, radius / 2, (num - 1));
                }
            }
        }
    }
}
①-1 プログラムから描写に必要な情報を考えて、円に対応するクラスMyCircleを作成せよ。
このクラスには適切なメンバ変数とコンストラクタを設定すること。

①-2 プログラムの中のDrawCircleをよく読み、MyCircleクラスにグラフィックオブジェクトを引数にとって描写を行うメソッドDrawを追加。
また、Drawメソッドを呼び出してすべての円が描写できるようDrawCircleを書き直しなさい。

② 完成した①を改造し、三角形を描写できるようにする。

このどのあたりからやり直せばいいですか?
もう日がないんで協力お願いします。

たろ

Re: visual c#のウィンドウプログラミングの質問なんですが

#6

投稿記事 by たろ » 15年前

この原題なら、咲さんのプログラムはそんなに間違ってもいないと思うのですが・・。
原題からは「一度表示したものを維持」や走り書きのコードは読み取れません・・。

まず走り書きのコードからわかるのは以下ですね。
・MyCircleクラスは「円1つだけ」の情報を持つ。
・ジェネリックのリストを使ってMyCircleインスタンスを管理。

MyCircleクラスは例えばこんな感じかな?

コード:

class MyCircle
{
	int		x, y, radius;
	SolidBrush	brush;

	public MyCircle(引数)
	{
		// メンバ変数x,y,radius,brushの値を決定
	}

	// ①-2 描写を行うメソッドDraw
	public void Draw( Graphics g )
	{
		// 円を描写
		g.FillEllipse(brush, x, y, radius * 2, radius * 2);
	}
}
原題の「Drawメソッドを呼び出してすべての円を描写するようにDrawCirclesを書き直す」点と、
「一度表示したものを維持する」点から、DrawCirclesはこんな↓簡単な内容に書き換えましょう。
MyCircleインスタンスリスト(変数名listとします)をループで回して、Drawを実行するだけ。

コード:

private void DrawCircles(Graphics g)
{
	foreach (MyCircle c in list)
	{
		c.Draw(g);
	}
}
残った大きな問題は、「MyCircleのリスト(list変数)を、いつどうやって作るか?」ですね。
「一度表示したものを維持する」ためのやり方はいろいろありますが、まずよくあるのは
プログラム起動時に1回だけリスト生成処理を行う方式でしょうか。
Form1 の Load イベントを使うなら、IDEからForm1_Loadメソッドを作ります。

コード:

private void Form1_Load(object sender, EventArgs e)
{
	// MyCircleインスタンスリスト(list)を作る。
}
MyCircleインスタンスリスト(list)は、以上の使い方から、Form1クラスのメンバ変数でよいかなと思います。

コード:

List<MyCircle> list = new List<MyCircle>();
あとは、MyCircleインスタンスリスト(list)の中身を作れば、動くと思います。
円の個数・位置・大きさを決めるアルゴリズムは、原題のDrawCircles()をそのまま使えばいいんですかね?
再帰を使っているので、こんな感じのメソッドを作る必要があるでしょう。(これをForm1_Loadで呼び出す)

コード:

private void CreateCircles(double x, double y, int radius, int num)
{
	// MyCircleインスタンスリストを作る。
	// アルゴリズムはDrawCirclesと同じで、描写(FillEllipse)はやめて、代わりにリスト登録(list.Add)する。
}
CreateCircles()の中身は、作ってみてください。原題のDrawCircles()と数行異なる程度です。

あ、MyCircleのコンストラクタもまだ残ってました。例えばこんな引数かな。

コード:

public MyCircle(int x, int y, int radius, int num)
なお、私の考え方が先生のご希望に沿うかどうかはわかりません・・悪しからず・・。

Re: visual c#のウィンドウプログラミングの質問なんですが

#7

投稿記事 by » 15年前

みなさんありがとうございます。おかげで今度こそ完成しました。
本当に心から感謝してます。

閉鎖

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