Automatyzacja wywoływanego wzorca kodu
Boleśnie zdałem sobie sprawę z tego, jak często trzeba pisać następujący wzór kodu w sterowanym zdarzeniami kodzie GUI, gdzie
private void DoGUISwitch() {
// cruisin for a bruisin' through exception city
object1.Visible = true;
object2.Visible = false;
}
Staje się:
private void DoGUISwitch() {
if (object1.InvokeRequired) {
object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
} else {
object1.Visible = true;
object2.Visible = false;
}
}
Jest to niezręczny Wzór W C#, zarówno do zapamiętania, jak i do wpisania. Czy ktoś wymyślił jakiś skrót lub konstrukcję, która automatyzuje to do pewnego stopnia? Byłoby fajnie, gdyby istniał sposób na dołączenie funkcji do obiektów, która robi to sprawdzanie bez konieczności przechodzenia przez całą tę dodatkową pracę, jak typ object1.InvokeIfNecessary.visible = true
Skrót.
Poprzednie odpowiedzi omawiały niepraktyczność wywołania Invoke() za każdym razem, a nawet wtedy składnia Invoke() jest zarówno nieefektywna, jak i nadal niezręczna w radzeniu sobie.
Czy ktoś zna jakieś skróty?9 answers
Podejście Lee można jeszcze uprościć
public static void InvokeIfRequired(this Control control, MethodInvoker action)
{
// See Update 2 for edits Mike de Klerk suggests to insert here.
if (control.InvokeRequired) {
control.Invoke(action);
} else {
action();
}
}
I można tak nazwać
richEditControl1.InvokeIfRequired(() =>
{
// Do anything you want with the control here
richEditControl1.RtfText = value;
RtfHelpers.AddMissingStyles(richEditControl1);
});
Nie ma potrzeby przekazywania kontrolki jako parametru delegatowi. C # automatycznie tworzy zamknięcie .
UPDATE :
Według kilku innych plakatów {[4] } można uogólnić jako ISynchronizeInvoke
:
public static void InvokeIfRequired(this ISynchronizeInvoke obj,
MethodInvoker action)
{
if (obj.InvokeRequired) {
var args = new object[0];
obj.Invoke(action, args);
} else {
action();
}
}
DonBoitnott zwrócił uwagę, że w przeciwieństwie do Control
Interfejs ISynchronizeInvoke
wymaga tablicy obiektów dla metody Invoke
jako parametru lista action
.
UPDATE 2
Zmiany sugerowane przez Mike ' a de Klerka (zobacz komentarz w 1. fragmencie kodu dla Wstawienia punktu):
// When the form, thus the control, isn't visible yet, InvokeRequired returns false,
// resulting still in a cross-thread exception.
while (!control.Visible)
{
System.Threading.Thread.Sleep(50);
}
Zobacz komentarz ToolmakerSteve poniżej dla obaw dotyczących tej sugestii.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-02 13:04:48
Możesz napisać metodę rozszerzenia:
public static void InvokeIfRequired(this Control c, Action<Control> action)
{
if(c.InvokeRequired)
{
c.Invoke(new Action(() => action(c)));
}
else
{
action(c);
}
}
I użyj go w ten sposób:
object1.InvokeIfRequired(c => { c.Visible = true; });
EDIT: jak zaznacza Simpzon w komentarzach można też zmienić podpis na:
public static void InvokeIfRequired<T>(this T c, Action<T> action)
where T : Control
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-18 18:55:11
Oto formularz, którego używam w całym kodzie.
private void DoGUISwitch()
{
Invoke( ( MethodInvoker ) delegate {
object1.Visible = true;
object2.Visible = false;
});
}
Oparłem to na wpisie na blogu tutaj . Nie miałem takiego podejścia, więc nie widzę powodu, aby komplikować mój kod z sprawdzeniem właściwości InvokeRequired
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-13 08:05:12
Utwórz ThreadSafeInvoke.urywek pliku, a następnie możesz po prostu wybrać Instrukcje aktualizacji, kliknąć prawym przyciskiem myszy i wybrać " Surround With...'lub Ctrl-K + S:
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippet Format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<Header>
<Title>ThreadsafeInvoke</Title>
<Shortcut></Shortcut>
<Description>Wraps code in an anonymous method passed to Invoke for Thread safety.</Description>
<SnippetTypes>
<SnippetType>SurroundsWith</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Code Language="CSharp">
<![CDATA[
Invoke( (MethodInvoker) delegate
{
$selected$
});
]]>
</Code>
</Snippet>
</CodeSnippet>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-02-15 13:42:56
Oto ulepszona / połączona Wersja odpowiedzi Lee, Olivera i Stephana.
public delegate void InvokeIfRequiredDelegate<T>(T obj)
where T : ISynchronizeInvoke;
public static void InvokeIfRequired<T>(this T obj, InvokeIfRequiredDelegate<T> action)
where T : ISynchronizeInvoke
{
if (obj.InvokeRequired)
{
obj.Invoke(action, new object[] { obj });
}
else
{
action(obj);
}
}
Szablon pozwala na elastyczny i mniej rzucany kod, który jest znacznie bardziej czytelny, podczas gdy dedykowany delegat zapewnia wydajność.
progressBar1.InvokeIfRequired(o =>
{
o.Style = ProgressBarStyle.Marquee;
o.MarqueeAnimationSpeed = 40;
});
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-07 17:50:04
Wolałbym używać pojedynczej instancji delegata metody zamiast tworzyć nową instancję za każdym razem. W moim przypadku używałem do wyświetlania postępu i (info/error) wiadomości z Backroundworker kopiowania i odlewania dużych danych z instancji sql. Co jakiś czas po około 70000 postępach i komunikatach mój formularz przestał działać i pokazywał nowe wiadomości. Nie miało to miejsca, gdy zacząłem używać pojedynczego delegata instancji globalnej.
delegate void ShowMessageCallback(string message);
private void Form1_Load(object sender, EventArgs e)
{
ShowMessageCallback showMessageDelegate = new ShowMessageCallback(ShowMessage);
}
private void ShowMessage(string message)
{
if (this.InvokeRequired)
this.Invoke(showMessageDelegate, message);
else
labelMessage.Text = message;
}
void Message_OnMessage(object sender, Utilities.Message.MessageEventArgs e)
{
ShowMessage(e.Message);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-21 11:29:00
Użycie:
control.InvokeIfRequired(c => c.Visible = false);
return control.InvokeIfRequired(c => {
c.Visible = value
return c.Visible;
});
Kod:
using System;
using System.ComponentModel;
namespace Extensions
{
public static class SynchronizeInvokeExtensions
{
public static void InvokeIfRequired<T>(this T obj, Action<T> action)
where T : ISynchronizeInvoke
{
if (obj.InvokeRequired)
{
obj.Invoke(action, new object[] { obj });
}
else
{
action(obj);
}
}
public static TOut InvokeIfRequired<TIn, TOut>(this TIn obj, Func<TIn, TOut> func)
where TIn : ISynchronizeInvoke
{
return obj.InvokeRequired
? (TOut)obj.Invoke(func, new object[] { obj })
: func(obj);
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-26 11:17:13
Lubię robić to trochę inaczej, lubię nazywać się" sobą " w razie potrzeby za pomocą akcji,
private void AddRowToListView(ScannerRow row, bool suspend)
{
if (IsFormClosing)
return;
if (this.InvokeRequired)
{
var A = new Action(() => AddRowToListView(row, suspend));
this.Invoke(A);
return;
}
//as of here the Code is thread-safe
Jest to poręczny wzorzec, pole IsFormClosing jest polem, które ustawiam na True, gdy zamykam formularz, ponieważ mogą istnieć wątki tła, które nadal działają...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-21 22:07:11
Nigdy nie powinieneś pisać kodu, który wygląda tak:
private void DoGUISwitch() {
if (object1.InvokeRequired) {
object1.Invoke(new MethodInvoker(() => { DoGUISwitch(); }));
} else {
object1.Visible = true;
object2.Visible = false;
}
}
Jeśli masz kod, który wygląda tak, to Twoja aplikacja nie jest bezpieczna dla wątków. Oznacza to, że masz kod, który już wywołuje DoGUISwitch () z innego wątku. Za późno, żeby sprawdzić, czy jest w innym wątku. InvokeRequire musi zostać wywołany, zanim zadzwonisz do DoGUISwitch. Nie należy uzyskiwać dostępu do żadnej metody lub właściwości z innego wątku.
Odniesienie: Kontrola.InvokeRequired Property gdzie można przeczytać:
Oprócz właściwości InvokeRequired, istnieją cztery metody na kontrolka, która jest Bezpieczna do wywołania: Invoke, BeginInvoke, EndInvoke i CreateGraphics jeśli uchwyt do sterowania został już stworzony.
W jednej architekturze procesora nie ma problemu, ale w architekturze wielu procesorów można spowodować przypisanie części wątku interfejsu do procesora, gdzie kod był uruchomiony...a jeśli ten procesor jest inny od tego, gdzie działał wątek UI, to po zakończeniu wątku wywołującego Windows pomyśli, że wątek UI się zakończył i zabije proces aplikacji, tzn. Twoja aplikacja zakończy się bez błędu.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-29 13:10:30