Пользовательский элемент управления. Создание клиентского приложения. Развитие серверного проекта, страница 11

Вы должны помнить. что перед тем как создать делегата, надо объявить делегатный тип. Его сигнатура определяется соображениями удобства пользования. Я решил, что при вызове делегатной функции она должна нести информацию: индекс вычисленной точки графика (в массиве PointF[]) и координаты точки графика, то есть сама точка PointF. Поэтому напрашивается делегатный тип с такой сигнатурой.

public delegate void ProgressHandler (int id, PointF pt);

Теперь можно приступить к созданию класса, обслуживающего второй поток. В нем надо объявить событие Move. Событие, как следует из документации .NET, должно иметь делегатный тип, то есть его объявление должно выглядеть так:

private ProgressHandler Move;    // Event

Кроме события класс CalcField должен иметь метод (назовем его Recalc), который будет вычислять поле в рамках отдельного (рабочего) потока. Адрес этого метода должен быть задан в качестве параметра при конструировании объекта класса ThreadStart. Главная форма создает и запускает вспомогательный поток таким образом:

CalcField field = new CalcField (this, new ProgressHandler(OnProgress));

thread = new Thread (new ThreadStart (field.Recalc));

thread.Start();

Приведем код класса CalcField.

class CalcField

{

private ProgressHandler Move;   // Event

private Form1 form;

public CalcField (Form1 f, ProgressHandler cb)

{

form = f;

Move = cb;

}

public void Recalc()

{

Point2D step = (form.pLast - form.pFirst) * (1.0f / (form.nodes - 1));

Point2D pt = form.pFirst;

PointF p = new Point(0,0);

for (int i = 0; i < form.nodes; i++)

{

float

r1 = form.pPos.Dist (pt),

r2 = pt.Dist (form.pNeg),

r11 = r1 * r1,

r22 = r2 * r2,

cs = (pt - form.pPos).Cos (form.pNeg - pt),

d = r11 * r22;

p.Y = d > 0 ?

form.charge * (r11 + r22 - form.charge * (cs + cs)) / d :

p.Y = -1;

if (i > 0)

p.X += !step;

pt += step;

Thread.Sleep(2);

if (Move != null)

Move (i, p);

}

}

}

public class Point2D

{

public float x, y;  // Real coordinates

public Point2D() { x=0; y=0; }

public Point2D (float xx, float yy) { x = xx;  y = yy; }

public static Point2D operator+ (Point2D p1, Point2D p2)

{

return new Point2D (p1.x + p2.x, p1.y + p2.y);

}

public static Point2D operator- (Point2D p1, Point2D p2)

{

return new Point2D (p1.x - p2.x, p1.y - p2.y);

}

public static Point2D operator* (Point2D pt, float d)

{

return new Point2D (pt.x * d, pt.y * d);

}

public static float operator* (Point2D p1, Point2D p2)

{

return p1.x * p2.x + p1.y * p2.y;

}

//======= Convert to integer

public Point ToInt()

{

Point p = new Point();

p.X = (int)x; p.Y = (int)y;

return  p;

}

//====== Vector norm

public static float operator! (Point2D pt)

{

return (float)Math.Sqrt (pt.x*pt.x + pt.y*pt.y);

}

public float Dist (Point2D pt)

{

float

dx = x - pt.x,

dy = y - pt.y;

return (float)Math.Sqrt (dx*dx + dy*dy);

}

public float Cos (Point2D pt)

{

return this * pt / (!this * !pt);

}

}

public class MainForm : Form

{

private Container components = null;

private PictureBox pbPos;

private PictureBox pbNeg;

private Label lblPercent;

private ProgressBar progress;

private Plot.PlotControl plot;

public uint nodes;     // Количество узлов сетки

public float charge;   // Величина зарядов диполя

public Point2D pPos, pNeg, pFirst, pLast;

private PointF[] points; // Массив координат точек графика

private bool isDragging = false;

private bool isCalculating = false;

private int x0, y0, x, y;

private Point org;

private Pen penFixed = new Pen (Color.Black, 1);

private Pen penDrag = new Pen (Color.Tan, 0);

private Thread thread;

private string title = "Electrostatic field";

public uint Nodes

{

get { return nodes; }

set

{

if (value < 4 || 1000 < value)

MessageBox.Show ("Количество узлов должно быть в диапазоне (4, 1000)");

else

nodes = value;

}

}

private void SetDefaults()

{

nodes = 200;

charge = 1e-6f / (4 * (float)Math.PI * 8.85e-12f);

points = new PointF[nodes];

}

public MainForm()

{

InitializeComponent();