Змінюй хід війни! Допомагай ЗСУ!

C# не легкая это работа....

🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #61
неплохой пример, как не надо писать код :)

Буду признателен если укажите чего делать не стоит! я за правильный код!!:)

страшный код! Кстати к интерфейсов ты как я понял не изучал еще (из того что ты написал), но юзаешь их вовсю

интерфейсы еще не изучал, так ото знаю, что "множественное наследование" которого нет, решается через интерфейсы
 
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #62
а что тот код делает? я скопипастил, выдало ошибки... using ESRI... этто ещё откуда?

2Kvest
Проверка, существует ли форма... И как это правильно сделать? Во первых объект полностью уничтожается GC которого ждать охота отпала. Во 2-ых, если поток выполняет действия связанные с формой, то есть опаность, что сразу после проверки форма закроется а поток будет пытаться что-то делать ===> ошибка.
 
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #63
Буду признателен если укажите чего делать не стоит! я за правильный код!!:)

проще рассказать как рекомендуется делать, но для этого нужно понимать что вы вообще делали :)
Кроме того, тут не все от вас зависит, т.к. библиотека которую вы используете написана безобразным образом - одни только имена нэймспесов сплошная мусорка

Проверка, существует ли форма... И как это правильно сделать?

есть свойство Form.Created

Во первых объект полностью уничтожается GC которого ждать охота отпала.

чтоб ты лучше понимал что такое GC и финализатор: пока у тебя есть хоть одна ссылка на объект, ты можешь через нее обратиться к объекту и поэтому GC не будет этот объект чистить. Когда у тебя не останется ссылок на объект, ты не сможешь уже обратиться к объекту, поэтому GC его почистит.

Во 2-ых, если поток выполняет действия связанные с формой, то есть опаность, что сразу после проверки форма закроется а поток будет пытаться что-то делать ===> ошибка.

действия связанные с формой можно выполнять только из потока который эту форму создал, т.к. цикл выборки сообщений работает в контексте этого потока. Т.е. из дополнительных потоков нельзя обращаться к свойствам и методам формы или каких-либо контролов которые на ней находятся... ;)
 
Останнє редагування:
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #64
Даже когда не осталось ссылок на обьект, GC может не чистить неопределённое время. Очень долго... Разве что принудительно его запускать, но мне это ненравится.
У меня форма создаёт поток (при нажатии кнопки), а не наоборот.
Form.Created не годится. Годится Form.Disposing.
Но в любом случае, не годится определять что либо в цикле потока, потому что если он будет приостановлен (например тоё же кнопкой), а форма закрыта, то он так и останется висеть...
 
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #65
Даже когда не осталось ссылок на обьект, GC может не чистить неопределённое время. Очень долго... Разве что принудительно его запускать, но мне это ненравится.
У меня форма создаёт поток (при нажатии кнопки), а не наоборот.
Form.Created не годится. Годится Form.Disposing.
Но в любом случае, не годится определять что либо в цикле потока, потому что если он будет приостановлен (например тоё же кнопкой), а форма закрыта, то он так и останется висеть...

почему он останется висеть к примеру после такого...
while (true)
{
if (Form.Created)
{
//поток...
}
else
{
//очистка ресурсов, используемых потоком
break;
}
}

P.S:
А вообще, попробуй в обработчике close формы приравнять свой поток (объект thread) к null, GC должен будет его очистить.
 
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #66
я смотрю ты так ничего и не понял. До тех пор пока код обращается к объекту или имеет потенциальную возможность обратиться к нему, GC не станет его чистить! (Хотя спецификация допускает в некоторых случаях такое, но это исключительные ситуации).
Что тебе в этом не нравится? ты хочешь обращаться к объекту которого уже нет и получать Access violation at address ...? :confused:

p.s.: я тебе поэтому и сказал - не используй финализаторы и все, объяснять почему - гиблое дело, всеравно не поймешь... Сколько объяснял, а ты все за свое...

p.p.s.: лучше бы объяснил что тебе сделать надо, чтото у меня такое впечатление сложилось что тебе там и потоки не нужны
 
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #67
я смотрю ты так ничего и не понял. До тех пор пока код обращается к объекту или имеет потенциальную возможность обратиться к нему, GC не станет его чистить!
Это к чему? Спасибо, за то что сочли меня идиотом. Я писал выше о другом: что GC НЕ СТАНЕТ ЕГО чистить СРАЗУ, даже если уже нет НИКАКИХ ссылок и код НИКАК не может быть вызван. Могут пройти МИНУТЫ пока он решит что-то чистить.

Что тебе в этом не нравится? ты хочешь обращаться к объекту которого уже нет и получать Access violation at address ...? :confused:
Мне не нравится что Вы не понимаете меня. Где я писал, что хочу обращаться к несуществующему обьекту???

p.s.: я тебе поэтому и сказал - не используй финализаторы и все, объяснять почему - гиблое дело, всеравно не поймешь... Сколько объяснял, а ты все за свое...
Я понял уже много, а Вы всё за своё... Вы что пытаетесь обьяснить? Что нежелательно их использовать? Я УЖЕ САМ к этому пришёл, а Вы всё за своё :D
p.p.s.: лучше бы объяснил что тебе сделать надо, чтото у меня такое впечатление сложилось что тебе там и потоки не нужны
Мне нужно исследовать взаимодействие всей этой кухни. Досконально. Интересно, как я пойму работу потоков их не используя? :D

Kvest сказав(ла):
почему он останется висеть к примеру после такого...
попытаюсь обьяснить...

while (true)
{
if (Form.Created) //проверка пройдена
{

//поток...
//В этот момент поток приостанавливается кнопкой на форме... поток стоит.
//Юзер закрывает форму. Всё, поток разбудить некому. Он остался в памяти.
//код.
//код.
//код.
//код.
}
else
{
//очистка ресурсов, используемых потоком
break;
}
}

Это я уже проверил. Странно что вы этого не увидели, вы же опытный программист?
 
Останнє редагування:
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #68
Я писал выше о другом: что GC НЕ СТАНЕТ ЕГО чистить СРАЗУ, даже если уже нет НИКАКИХ ссылок и код НИКАК не может быть вызван. Могут пройти МИНУТЫ пока он решит что-то чистить.

если объект содержит финализатор, то его чистка действительно откладывается. Объекты не содержащие финализатора чистятся практически мгновенно. Поэтому финализатор, пусть даже пустой, не рекомендуется реализовывать - это ухудшает сборку мусора...

//В этот момент поток приостанавливается кнопкой на форме... поток стоит.
//Юзер закрывает форму. Всё, поток разбудить некому. Он остался в памяти.

во первых неясно, что ты понимаешь под "приостанавливается кнопкой"? вызов метода WaitOne() для объекта синхронизации? Если так, то ему можно передать таймаут и он не будет вечно ждать, после просыпания можно проверить что привело к просыпанию - сигнал из другого потока или таймаут.
Во вторых, если ты сделал поток так что он зависает, что-же ты от него ждешь?

Использование блокирующих вызовов из потока который подразумевает возможность остановки в любой момент неправильно. Нужно использовать асинхронные операции. Потому что некоторые блокирующие вызовы невозможно ничем прервать.

По поводу многопоточности почитай литературу про организацию многопоточности и механизмы синхронизации потоков. Это очень сложная тема и она не зависит от языка, механизмы синхронизации потоков везде практически одинаковы.
Если коротко - добавляя поток ты зачастую будешь вынужден довольно сильно усложнить код. Не всякую задачу можно вынести в отдельный поток, некоторые типы задач требуют сложнейших механизмов синхронизации. Поэтому пока в этом нет необходимости, потоков лучше не создавать, дабы не усложнять себе (и тем кто будет читать твой код) жизнь...
 
Останнє редагування:
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #69
//В этот момент поток приостанавливается кнопкой на форме... поток стоит.
Это не рационально! Поток должен завершаться сам. И все таки попробуй в close добавить такое:

currentThread.Start(); (или как он там активируется)

в таком случае по закрытию формы если поток даже будет приостановлен ты его опять активируешь, форма закрывается, условие на ее существование не выполняется и поток самозавершается. Правда не факт что при проверке условия на существование формы не вывалится исключение.

Или же попробуй добавить это в close:
currentThread = null;

Этим ты убираешь с ссылку с потока, что дает право GC убрать поток. Попробуй так!!!
 
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #70
Это не рационально! Поток должен завершаться сам.
Он и завершится сам когда надо, но есть кнопка "пауза".
А в CLose() или в dispose я поставил Thread.Abort() и он закрывается, вот только если поток использует ресурсы как быть незнаю.
Kvest сказав(ла):
Или же попробуй добавить это в close:
currentThread = null;

Этим ты убираешь с ссылку с потока, что дает право GC убрать поток. Попробуй так!!!
проверил но нифига! Не убирает его GC, я незнаю почему. Ну в принципе это и не важно.

во первых неясно, что ты понимаешь под "приостанавливается кнопкой"? вызов метода WaitOne() для объекта синхронизации? Если так, то ему можно передать таймаут и он не будет вечно ждать, после просыпания можно проверить что привело к просыпанию - сигнал из другого потока или таймаут.
Во вторых, если ты сделал поток так что он зависает, что-же ты от него ждешь?

Использование блокирующих вызовов из потока который подразумевает возможность остановки в любой момент неправильно. Нужно использовать асинхронные операции. Потому что некоторые блокирующие вызовы невозможно ничем прервать.

По поводу многопоточности почитай литературу про организацию многопоточности и механизмы синхронизации потоков. Это очень сложная тема и она не зависит от языка, механизмы синхронизации потоков везде практически одинаковы.
Если коротко - добавляя поток ты зачастую будешь вынужден довольно сильно усложнить код. Не всякую задачу можно вынести в отдельный поток, некоторые типы задач требуют сложнейших механизмов синхронизации. Поэтому пока в этом нет необходимости, потоков лучше не создавать, дабы не усложнять себе (и тем кто будет читать твой код) жизнь...
Нет, пока что поток только один, я использовал Thread.Suspend() и Thread.Resume(), и пока никаких блокировок, хотя я сейчас про них как раз читаю. Но т.к. я дошёл до них, то надо пробовать, вот и вся необходимость.

Кстати вот код:

public partial class Form1 : Form
{
Random myrandom = new Random();
Thread thr1;
public Form1()
{
InitializeComponent();
thr1 = new Thread(new ThreadStart(dosomething));
}

void dosomething()
{
Graphics graph1_form1 = CreateGraphics();
SolidBrush brush_graph1 = new SolidBrush(Color.Coral);
int x_pr = 1, x = myrandom.Next(50,600), y_pr = 1, y = myrandom.Next(50,600);
while (true)
{
brush_graph1.Color = Color.FromArgb(myrandom.Next(255), myrandom.Next(255), myrandom.Next(255));
if (x < 50) x_pr = myrandom.Next(10);
if (x > 600) x_pr = -myrandom.Next(10);
if (y < 50) y_pr = myrandom.Next(10);
if (y > 600) y_pr = -myrandom.Next(10);
x += x_pr;
y += y_pr;
graph1_form1.FillEllipse(brush_graph1, x, y, 150, 200);
Thread.Sleep(1);
}
}

private void button1_Click(object sender, EventArgs e)
{
switch (thr1.ThreadState)
{
case ThreadState.Unstarted: thr1.Start(); break;
case ThreadState.Suspended: thr1.Resume(); break;
default: thr1.Suspend(); break;
}
}
}
можно ли сделать без потока? ;)
 
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #71
можно ли сделать без потока? ;)

можно заюзать таймер, в качестве времени одного тика указать 1-цу, получиться тоже самое имхо. Кстати может он будет нормально удаляться GC

А в CLose() или в dispose я поставил Thread.Abort() и он закрывается, вот только если поток использует ресурсы как быть незнаю.

Вот поэтому использовать стоповые функции не рационально! ;) Внутри потока должно быть какое-то условие его завершения, по выполнению которого он будет очищать ресурсы и делать break
 
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #72
И это пока самое логичное, однако если метод будет не такой простой? Что если в нём будет много кода, несколько длинных невложенных циклов? Использовать проверку условия становится неудобно прийдётся несколько раз делать в разных местах... гнило. Я думал есть каой-то хороший способ, предусмотренный классом Thread.

Про GC я вот что скажу: между закрытием формы и вызовом GC всегда будет проходить достаточно времени, чтобы поток успел вызвать FillEllipse. И на закрытой форме это даёт исключение. Так что пусть GC идёт пока что лесом.
 
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #73
Нет, пока что поток только один, я использовал Thread.Suspend() и Thread.Resume(), и пока никаких блокировок, хотя я сейчас про них как раз читаю.

эти методы лучше не использовать, начиная со второго фреймворка их даже убрали, теперь их вообще вызвать нельзя ;)
Поток дожен засыпать по своей воле, иначе это грозит нехорошими ошибками... ;)
Кстати метод Abort предназначен для аварийного завершения потока, в случае ошибок и т.п. В нормальных ситуациях поток обязан сам завершаться!


можно ли сделать без потока? ;)

не только можно, но и нужно, тут вообще поток прикручен как собаке пятая нога, да еще и с Suspend/Resume - это просто ужас!

если метод будет не такой простой? Что если в нём будет много кода

если в методе много кода значит это плохой код - его нужно переделывать так, чтобы в методе было мало кода ;)


гнило оттого что в методе много кода, его надо рефакторить

Я думал есть каой-то хороший способ, предусмотренный классом Thread.

Никаких хороших способов о которых ты думал ни в одном языке и в теории не предусмотрено. Просто ты не сталкивался еще с многопоточными алгоритмами. Для управления потоками используются специальные механизмы синхронизации потоков - семафоры, критические секции, мейлбоксы и т.п.
Когда ты ознакомишься с этим, поймешь добавляя всего один поток ты очень сильно усложнишь код, вплоть до того что простейшие конструкции прийдется обдумывать днями... Нужно тебе это? Иногда это выгодно с точки зрения производительности, но - иногда! Случаев когда нужно использовать потоки не так уж и много.

Про GC я вот что скажу: между закрытием формы и вызовом GC всегда будет проходить достаточно времени, чтобы поток успел вызвать FillEllipse. И на закрытой форме это даёт исключение. Так что пусть GC идёт пока что лесом.

у тебя изначально неправильный алгоритм рисования, во первых рисовать нужно из метода OnPaint, а не из другого потока. Во вторых рисовать нужно на Graphics который предается в OnPaint, а не создавать его с помощью CreateGraphics.
Если ты не будешь соблюдать два этих правила, то Windows не гарантирует, что изображение будет отображено корректно, более того оно будет неприятно мерцать и жрать кучу лишнего процессорного времени

Чтобы правильно рисовать тебе нужно создать некую логическую структуру, которая будет описывать что именно нужно рисовать, после обновления этой структуры нужно вызвать метод Invalidate() для объекта который нужно перерисовать. Это проинформирует операционную систему что часть окна нужно перерисовать, и если эта часть окна видима, то система вызовет событие OnPaint. В аргументах будет Graphics на котором нужно нарисовать содержимое окна, в обработчике этого события ты и отрисовываешь все что нужно согласно логической структуры.

Тут важно понять что решение когда нужно рисовать должна принимать ОС, не нужно рисовать когда ОС об этом не попросила ;)

Впринципе ты можешь обновлять логическую структуру из отдельного потока, синхронизируя доступ к ней через критическую секцию, но я думаю в твоем случае это не нужно и только добавит кучу лишнего кода, усложнив логику.

Возможно тебя удивит, например игры делают как правило в одном потоке - все операции производятся в одном потоке! ;) Сейчас появлись игры поддерживающие многопроцессорные системы, но дополнительные потоки используются только для специфических вещей - расчет физики, AI и т.п. Все остальное все равно в одном потоке происходит.
 
Останнє редагування:
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #74
Klez спасибо, как правильно рисовать я ещё незнаю, просто увидел команду и прикрутил. Если всунуть в OnPaint() то конечно всё будет работать в потоке самой формы. Но тут ведь цель стояла передо мной не научится правильно юзать Drawing а посмотреть как поток сделать и управлять им. А эллипсы - то просто для наглядности ;) Я могу придумать что-то другое.
Я знаю что игры делают(ли) с плохой поддержкой многопроцессорности, и всё что я могу сказать - лентяи :)

начиная со второго фреймворка их даже убрали, теперь их вообще вызвать нельзя
гггг а я вот как-то их вызвал, а стоит framework3.5 SP1 :) и код невыглядел усложнённым из-за потока :)

создать некую логическую структуру, которая будет описывать что именно нужно рисовать, после обновления этой структуры нужно вызвать метод Invalidate() для объекта который нужно перерисовать. Это проинформирует операционную систему что часть окна нужно перерисовать, и если эта часть окна видима, то система вызовет событие OnPaint.
Так вопрос сразу возникает, как эта структура будет обновляться? Вобщем, как организовать "непрерывный" но управляемый цикл чтобы он не повесил окно, притом не создавая потока??
 
Останнє редагування:
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #75
Я знаю что игры делают(ли) с плохой поддержкой многопроцессорности, и всё что я могу сказать - лентяи :)

ты не прав, проблема в другом - большинство алгоритмов крайне сложно, а зачастую вообще невозможно распаралелить - то что можно в одном потоке реализовать в несколько строк, в многопоточном приложении может занимать тысячи строк кода, при этом сложность кода ведет к большому числу ошибок, а отлаживать многопоточные приложения на порядок сложнее чем однопоточные...
В многопоточных приложениях большая часть кода нужна исключительно для синхронизации потоков, код самого алгоритма занимает незначительную часть. Следует понимать что из разных потоков нельзя обращаться к одним и тем-же переменным без синхронизации! Ко всему прочему синхронизация ведет к заметному снижению производительности (это одна из главных причин мешающих использовать многопоточность в играх).

Так вопрос сразу возникает, как эта структура будет обновляться? Вобщем, как организовать "непрерывный" но управляемый цикл чтобы он не повесил окно, притом не создавая потока??

ты опять не понял, логическая структура не должна иметь ни малейшего отношения к окну! Она должна содержать только информацию о том что нужно нарисовать. Никаких ссылок на окно или методы рисования в ней не должно быть!

В твоем примере рисовались элипсы в случайных местах. Так вот, применительно к этому случаю можно реализовать например такую структуру хранения информации об элипсах:
Код:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;


public class EllipseEntity
{
    private static Random _random = new Random();

    public Rectangle Rect;
    public Color Color;

    public EllipseEntity(Rectangle rect, Color color)
    {
        Rect = rect;
        Color = color;
    }

    
    public static EllipseEntity BuildRandomEllipse(Rectangle rect)
    {
        Rectangle erect = new Rectangle(_random.Next(rect.Left, rect.Right), _random.Next(rect.Top, rect.Bottom), _random.Next(rect.Left, rect.Right), _random.Next(rect.Top, rect.Bottom));
        Color ecolor = Color.FromArgb(_random.Next(255), _random.Next(255), _random.Next(255));
        return new EllipseEntity(erect, ecolor);
    }
}

public class ExampleForm : Form
{
    private List<EllipseEntity> _drawData = new List<EllipseEntity>();  //<= логическая структура для хранения что нужно нарисовать
    
    private Timer timerUpdate;
    private Button buttonStartStop;

    public ExampleForm()
    {
        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        this.DoubleBuffered = true;
        this.Paint += ExampleForm_Paint;

        this.buttonStartStop = new Button();
        this.buttonStartStop.Text = "Start";
        this.buttonStartStop.Location = new Point(10, 10);
        this.buttonStartStop.Click += new EventHandler(buttonStartStop_Click);
        this.Controls.Add(this.buttonStartStop);
        
        this.timerUpdate = new Timer();
        this.timerUpdate.Interval = 50;
        this.timerUpdate.Enabled = false;
        this.timerUpdate.Tick += new EventHandler(updateTimer_Tick);
    }

    private void buttonStartStop_Click(object sender, EventArgs e)
    {
        this.timerUpdate.Enabled = !this.timerUpdate.Enabled;
        buttonStartStop.Text = this.timerUpdate.Enabled ? "Stop" : "Start";
    }

    private void updateTimer_Tick(object sender, EventArgs e)
    {
        updateEllipses(_drawData, ClientRectangle);
        Invalidate();   // <== даем знать ОС, что изображение нужно перерисовать
    }

    protected void ExampleForm_Paint(object sender, PaintEventArgs e)
    {
        drawEllipses(e.Graphics, _drawData);
    }

    private void drawEllipses(Graphics g, List<EllipseEntity> data)         // метод рисования
    {
        // в этом методе нужно только рисовать то что задано, без всякой модификации данных!
        foreach (EllipseEntity entity in data)
            using (SolidBrush brush = new SolidBrush(entity.Color))
                g.FillEllipse(brush, entity.Rect);
    }

    private void updateEllipses(List<EllipseEntity> data, Rectangle rect)   // метод обновления данных для рисования
    {
        // в этом методе нужно только обновлять данные, рисовать нельзя!
        if(_drawData.Count < 50)
            _drawData.Add(EllipseEntity.BuildRandomEllipse(rect));
        else
            _drawData.Clear();
    }

    static void Main()
    {
        using (ExampleForm form = new ExampleForm())
            Application.Run(form);
    }
}

как видишь, потоки тут совершенно не нужны и будут как собаке пятая нога ;)
если тебе нужно рисовать кроме элипсов еще чтонить, делаешь напрмер так:
Код:
    protected void ExampleForm_Paint(object sender, PaintEventArgs e)
    {
        drawEllipses(e.Graphics, _drawData);
        drawSuperPuperData(e.Graphics, _superPuperData);   // <= добавляем вызов метода который будет рисовать другие данные
    }

вот и все... :) Могу сказать одно - рисование окон любой сложности не требует дополнительных потоков, наоборот рисовать нужно из одного потока - того в котором создается форма... ;)

И не забывай использование объектов с интерфейсом IDisposable заключать в using, это поможет избежать утечек системных ресурсов.
 
Останнє редагування:
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #76
Klez
Завязал на таймер, это ожидаемо. Но есть загвоздка. Твоя програмка не делает то, что моя. Твоя создаёт коллекцию случайных эллипсов, добавляя по 1му за тик таймера до 50ти. И каждый тик отрисовывает их все что накопились в по событию. ОДНАКО. Invalidate() очищает окно, и всё что было отрисовано.
Теперь что делает моя: Эллипсы случайно меняют цвет и, натыкаясь на заданные границы, меняют направление отрисовки (со случайным углом). Получается такая себе змея, и применять тут Invalidate() нет никакого резона. Они постоянно рисуются поверх того что было раньше.

По поводу необходимости потока повторю: поток жизненно необходим, т.к. я хочу сделать поток :). Эллипсы тут никаким боком - просто притулил что попроще и наглядно. А ты отвечаеш так, будто решил что моя основная цель - эллипсы нарисовать... :D ОК, Я вернусь к потокам позже, когда покурю книгу.
 
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #77
Invalidate() очищает окно, и всё что было отрисовано.

Invalidate не очищает, а дает указание перерисовать изображение в окне. Этот вызов информирует систему что содержимое окна устарело и его нужно обновить.



Теперь что делает моя: Эллипсы случайно меняют цвет и, натыкаясь на заданные границы, меняют направление отрисовки (со случайным углом).

что тебе мешает изменять цвет эллипсов их координаты, и их количество в коллекции? :confused:

применять тут Invalidate() нет никакого резона

еще раз объясняю, Invalidate нужно вызывать каждый раз, когда чтото изменилось и изображение нужно перерисовать. Пока изображение не меняется вызывать Invalidate не нужно, Windows сам вызовет когда надо.



поток жизненно необходим, т.к. я хочу сделать поток :).

приведи пример задачи которая требует дополнительного потока. Отрисовка окон не требует дополнительных потоков, поэтому поток тут как собаке пятая нога ;) Более того, работать с контролами из разных потоков нельзя, т.к. обращаться к контролам может только поток в котором он создан.

KlezЗавязал на таймер, это ожидаемо. Но есть загвоздка.

ты хочешь чтобы это выполнялось в отдельном потоке? хорошо, вот тебе вариант с потоком:
Код:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;


public class EllipseEntity
{
    private static Random _random = new Random();

    public Rectangle Rect;
    public Color Color;

    public EllipseEntity(Rectangle rect, Color color)
    {
        Rect = rect;
        Color = color;
    }

    
    public static EllipseEntity BuildRandomEllipse(Rectangle rect)
    {
        Rectangle erect = new Rectangle(_random.Next(rect.Left, rect.Right), _random.Next(rect.Top, rect.Bottom), _random.Next(rect.Left, rect.Right), _random.Next(rect.Top, rect.Bottom));
        Color ecolor = Color.FromArgb(_random.Next(255), _random.Next(255), _random.Next(255));
        return new EllipseEntity(erect, ecolor);
    }
}

public class ExampleForm : Form
{
    private List<EllipseEntity> _drawData = new List<EllipseEntity>();  //<= логическая структура для хранения что нужно нарисовать
    
    private Thread threadUpdate;
    private Button buttonStartStop;
    private volatile bool _running = false;
    private readonly object _drawDataSync = new object(); // используем для синхронизации доступа к _drawData


    public ExampleForm()
    {
        this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        this.DoubleBuffered = true;
        this.Paint += ExampleForm_Paint;

        this.buttonStartStop = new Button();
        this.buttonStartStop.Text = "Start";
        this.buttonStartStop.Location = new Point(10, 10);
        this.buttonStartStop.Click += new EventHandler(buttonStartStop_Click);
        this.Controls.Add(this.buttonStartStop);
    }

    private void buttonStartStop_Click(object sender, EventArgs e)
    {
        if (threadUpdate == null)
        {
            _running = true;
            this.threadUpdate = new Thread(new ThreadStart(threadUpdateProc));
            this.threadUpdate.Name = "Update thread";
            this.threadUpdate.Start();
            this.buttonStartStop.Text = "Stop";
        }
        else
        {
            _running = false;
            this.threadUpdate.Join();
            this.threadUpdate = null;
            this.buttonStartStop.Text = "Start";
        }
    }

    private void threadUpdateProc()
    {
        while (_running && Created)
        {
            Thread.Sleep(20);  // стараемся по максимуму делиться временем
            
            lock (_drawDataSync)
                updateEllipses(_drawData, ClientRectangle);
            Invalidate();   // <== даем знать ОС, что изображение нужно перерисовать
        }
    }

    protected void ExampleForm_Paint(object sender, PaintEventArgs e)
    {
        lock (_drawDataSync)
            drawEllipses(e.Graphics, _drawData);
    }

    private void drawEllipses(Graphics g, List<EllipseEntity> data)         // метод рисования
    {
        // в этом методе нужно только рисовать то что задано, без всякой модификации данных!
        foreach (EllipseEntity entity in data)
            using (SolidBrush brush = new SolidBrush(entity.Color))
                g.FillEllipse(brush, entity.Rect);
    }

    private void updateEllipses(List<EllipseEntity> data, Rectangle rect)   // метод обновления данных для рисования
    {
        // в этом методе нужно только обновлять данные, рисовать нельзя!
        if (_drawData.Count < 50)
            _drawData.Add(EllipseEntity.BuildRandomEllipse(rect));
        else
            _drawData.Clear();
    }

    static void Main()
    {
        using (ExampleForm form = new ExampleForm())
            Application.Run(form);
    }
}
 
Останнє редагування:
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #78
Invalidate не очищает, а дает указание перерисовать изображение в окне. Этот вызов информирует систему что содержимое окна устарело и его нужно обновить.
ну не сам метод очищает, но как результат обновления, окно будет очищено. Т.е. вызов Invalidate() приводит к очистке в конечном итоге?

что тебе мешает изменять цвет эллипсов их координаты, и их количество в коллекции? :confused:
Ну хотябы то, что кол-во частично видимых эллипсов легко достигает нескольких тысяч. А может и больше если уменьшить диаметр! Перерисовывать каждый раз всё коллекцию из нескольких (пары десятков) тысяч - попахивает бредом. Производительность на ветер?
 
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #79
Ну хотябы то, что кол-во частично видимых эллипсов легко достигает нескольких тысяч. А может и больше если уменьшить диаметр! Перерисовывать каждый раз всё коллекцию из нескольких (пары десятков) тысяч - попахивает бредом. Производительность на ветер?

ах вот ты о чем, в таких случаях логическая структура будет представлять собой Bitmap, в методе обновления ты можешь рисовать в битмапке (также как изменяется содержимое коллекции с эллипсами). А в OnPaint нужно будет скопировать Bitmap в Graphics как того попросит ОС... битмапка позволит сохранить результат последнего рисования, чтобы при следующем обновлении добавить в него новый эллипс не перерисовывая все остальные

По поводу Invalidate, ты просто не понял механизм рисования окон в Windows. Invalidate у тебя чистит окно потому что ты не реаллизовал OnPaint, а дефолтовый OnPaint чистит окно. Вот у тебя и получается так. ;)

Объясняю еще раз принцип рисования окон в Windows: ОС может в любой момент попросить нарисовать окно (например после того как окно было заслонено другим окном - изображение на нем пропадает), ты должен перерисовать все окно полностью с нуля. Не пологаясь на то что в окне осталось предыдущее изображение! Эта просьба ОС выглядит для формы как вызов события Paint.
Таким образом обработчик Paint обязан уметь нарисовать содержимое окна полностью с нуля, в любой момент времени. Т.е. в случае с элипсами Paint должен в любой момент времени перерисовать все до одного элипсы которые видны на экране!
Как это решается я уже объяснил - строится логическая структура дающая полную информацию для обработчика Paint что именно нужно нарисовать в текущий момент времени. Причем этой структуры достаточно чтобы нарисовать все пикселы на форме.
Важно понимать что событие Paint ничего не должно изменять,оно должно рисовать неизменную картинку для текущего состояния.

А вот изменять изображение нужно из другого метода - update, кто его будет вызывать и как - это уже твое дело, главное что этот метод ничего в окнах и контролах не рисует. Он только изменяет информацию о том что нужно рисовать.

Сам метод Invalidate говорит о том что окно пора перерисовать, т.к. ОС не будет вызывать метод Paint если содержимое окна не испортилось, то нужно дать както понять ей что окно всеже нужно перерисовать. Для этого служит метод Invalidate, когда ты его вызываешь система начинает считать что область которую ты передал (без параметров - все окно) содержит устаревшее изображение и его нужно перерисовать. Когда ОС найдет удобный подходящее время, она вызовет метод Paint чтобы обновить эту область окна.

Тут нужно понимать что содержимое окна в любой момент может быть испорчено, поэтому чтобы система должна уметь его восстановить - для этого она вызывает метод Paint у контролов или формы. Методы Paint система вызывает по умному для каждого окна, если окно не видно, метод не будет вызван, если изображение затерто другим окном, будет вызван. Причем Paint будет вызваться в такие моменты чтобы пользователь не успел увидеть что содержимое окна испорчено.

А теперь, если ты понял что написано выше, можешь провести эксперимент - попробуй в твоем первом примере с элипсами закрыть окно другим окном, а потом отведи его в сторону - ты увидишь что все элипсы которые были нарисованы затерлись! Это произошло потому что ты неправильно рисовал, как это исправить, надеюсь ясно из моего повествования :)

p.s: я уже устал об одном и томже рассказывать, обо всем этом прекрасно написано в книгах... ;)
 
Останнє редагування:
  • 🔴 19:58 Повітряна тривога в Харківський районСлідкуйте за подальшими повідомленнями.#Харківський_район
  • #80
таких случаях логическая структура будет представлять собой Bitmap, в методе обновления ты можешь рисовать в битмапке (также как изменяется содержимое коллекции с эллипсами). А в OnPaint нужно будет скопировать Bitmap в Graphics как того попросит ОС... битмапка позволит сохранить...........
Вот вот! Я к этому пришёл, только про Bitmap ещё ничего не знаю как там с ним работать. Опять же прочту да разберусь.
.... ты не реаллизовал OnPaint, а дефолтовый OnPaint чистит окно.
Но его и нужно очищать, если его перекроет другое окно! Проясни в чём будет разница: переопределить OnPaint() или написать в событие Paint ? Первое будет работать быстрее, не?
Объясняю еще раз принцип рисования окон в Windows......
Тут нужно понимать........... А теперь, если ты понял что написано выше, можешь провести эксперимент.........
p.s: я уже устал об одном и томже рассказывать, обо всем этом прекрасно написано в книгах... ;)
Вся беда в том, что ты слишком много пишеш об одном и том же, считая что я чего то не понимаю. Эксперимент? Я всё увидел сразу, как только запустил свой код. Я прекрасно видел что они затираются и прекрасно понимал почему. Invalidate() не применял именно потому что он приводил к очистке всего что нарисовалось, а не от того что я непонимаю что это и зачем ;)
Это произошло потому что ты неправильно рисовал, как это исправить, надеюсь ясно из моего повествования :)
Вот и всё, я просто не знал как правильно рисовать.
Отдельный класс для генерации элипсов я не сделал сразу потому что показалось что без него будет быстрее написать.

По поводу примера с потоком что ты привёл: спасибо конечно, но то же самое я уже успел сделать сам, кроме применения lock - я только до него добрался в книге. И где там особое усложнение кода? В упор не вижу :)
 
Назад
Зверху Знизу