Для индикации прогресса в таске существует IProgress
.
А что если надо просто уведомить форму при возникновении в таске какого-то события? Тут ведь IProgress
уже не поможет
Прокинул в таску SynchronizationContext
от потока формы и, вроде, заработало
Но потом решил переписать по-другому.
Уже была же такая же тема с прогрессом.
Старые добрые колбеки никто не отменял.
какие колбеки?
При старте таски передаете в нее Action какой нибудь … и пусть она вызывает его когда ей надо и передает любые сообщи.
Но ведь Action
это просто делегат. Если вызвать его из таски без синхронизации - будет краш.
Для этого есть InvokeRequired , BeginInvoke
и тогда он будет вызываться в том потоке кто его создал.
Интересно теперь, как я это сделал Всего полгода прошло, чёт всё забыл уже Пост 21 марта 2022
- у меня уже ностальгия
Так, стоп, а как? В таску же анонимный метод запихан, типо такого:
return await Task.Run(() =>
{
if (File.Exists(fnDashTmp))
{
File.Delete(fnDashTmp);
}
IProgress<int> dashReporter = progressDash;
Stream fileStream = File.OpenWrite(fnDashTmp);
FileDownloader d = new FileDownloader();
d.Connecting += (s, url) =>
{
lblStatus.Text = $"Состояние: Подключение..."; //тут краш, естественно
lblProgress.Text = null;
lblStatus.Refresh();
};
........
Никто же не мешает сделать его функцией.
не понял
await Task.Run(() =>
{
for (int i = 0; i < 9999; ++i)
{
Action action = () =>
{
lblProgress.Text = i.ToString();
lblProgress.Refresh();
Thread.Sleep(3000);
};
Invoke(action);
}
});
вот так чтоли? Вроде работает
Тогда не понятно, зачем нужен IProgress
, если через Action
удобнее?
Чем удобнее?
А так IProgress
универсальнее. Invoke
это ж метод винформс контрола/формы. Его надо как-то передать коду потока, который может находиться совсем не в коде формы, а, например, в библиотеке. И библиотека может быть рассчитана не только на винформс.
А IProgress
он не только из кода формы? Откуда он знает, что надо именно с потоком формы синхронизироваться?
Progress
должен создаваться в основном потоке. Он использует SynchronizationContext.Current
.
А, понял. Синхронизируется с тем потоком, в котором был создан.
Тем, что можно написать вот так:
Action action = () =>
{
//делаем, что хотим
};
Invoke(action);
И не надо писать целый класс, чтобы, например, изменить текст в одном контроле. А если, например, что-то ещё надо сделать (кроме изменения текста), как это передать в IProgress
? Придётся же кучу флагов вводить, что и когда сделать. Или отдельный IProgress
и класс на каждое действие. Это же не удобно.
А можно вообще так:
Invoke((MethodInvoker)delegate
{
//
});
Так ещё короче.
Разве нельзя просто Invoke(() => ...)
?
По-разному можно, на что хватит фантазии. Хоть просто колбэк передавать в этот класс при создании.
Точнее стандартный Progress<T>
и есть же такой. https://learn.microsoft.com/en-us/dotnet/api/system.progress-1.-ctor?view=net-7.0#system-progress-1-ctor(system-action((-0)))
Так что даже свой класс не нужен.
Неа, нельзя.
У меня не хватает
А если таска создана не в классе формы
namespace WindowsFormsApp2
{
internal class MySubClass
{
public bool Active { get; private set; }
public void Start()
{
if (!Active)
{
Active = true;
Task.Run(() =>
{
int i = 0;
while (Active)
{
Thread.Sleep(1000);
SomethingHappened(i++);
}
});
}
}
protected virtual void SomethingHappened(int i)
{
System.Diagnostics.Debug.WriteLine($"Something is happened {i} times in subclass");
}
}
}
namespace WindowsFormsApp2
{
internal class MyClass : MySubClass
{
protected override void SomethingHappened(int i)
{
System.Diagnostics.Debug.WriteLine($"Something is happened {i} times in class");
//тут надо синхронизироваться и какой-нибудь делегат должен быть
}
}
}
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
MyClass myClass = new MyClass();
myClass.Start();
}
//Тут где-нибудь ещё Stop() должен быть
}
}
Не совсем понимаю, как сюда Action
прикрутить Обычно я в таких ситуациях прокидываю SynchronizationContext.Current
от потока с формой и всё работает. Ещё на гитхабе видел, там в конструктор тупо передаёт саму форму и от неё вызывают Invoke()
. Не знаю, насколько эти способы корявые.
Но что если
библиотека может быть рассчитана не только на винформс.
?