C# 从多个后台线程更新标签
我正在做一个足球模拟器,我有9场比赛在不同的线程背景。在方法中,每个线程的核心都有一个事件。当这种情况发生时(当一个目标被“踢”了),我想用部分结果更新表单上的标签(名为goalLabel)。我写了一个代码…:C# 从多个后台线程更新标签,c#,label,invokerequired,background-thread,C#,Label,Invokerequired,Background Thread,我正在做一个足球模拟器,我有9场比赛在不同的线程背景。在方法中,每个线程的核心都有一个事件。当这种情况发生时(当一个目标被“踢”了),我想用部分结果更新表单上的标签(名为goalLabel)。我写了一个代码…: for (int i = 0; i < 6; i++) { if (i % 2 == 0) homeGoals++; else awawyGoals++; if (goal != null) goal(this); //(goal is an event)
for (int i = 0; i < 6; i++)
{
if (i % 2 == 0) homeGoals++;
else awawyGoals++;
if (goal != null) goal(this); //(goal is an event)
Thread.Sleep(1000);
} //this is the full method
以及UpdateGallabel方法:
public void UpdateGoalLabel(string update)
{
if (InvokeRequired)
{
MyDel del = new MyDel(UpdateGoalLabel); // yeah, I have a delegate for it: delegate void MyDel(string update);
Invoke(del, update);
}
else
{
lock (this) // if this lock isn't here, it works the same way
{
this.goalLabel.Text = update;
}
}
}
所以我可以接触并更改标签的文本,但我不知道为什么它不会更改54次。这就是我们的目标,在每一个目标之后都能得到通知
有什么想法吗
先谢谢你
更新#1:
我正在使用VS2010
下面是我启动线程的代码:
List<Thread> allMatches = new List<Thread>();
foreach (Match match in matches)
{
Thread newtmatch = new Thread(match.PlayMatch); //this is the first code block I wrote
allMatches.Add(newtmatch);
newtmatch.Start();
}
List allMatches=new List();
foreach(匹配中的匹配)
{
Thread newtmatch=new Thread(match.PlayMatch);//这是我写的第一个代码块
添加(newtmatch);
newtmatch.Start();
}
更新#2:
这里是我附加EventHandler的地方(这是在同一个方法中,在前面的代码块上方有几行):
matches=newlist();
foreach(Program.cm.nextMatches中的[]队对手)
{
比赛vmi=新比赛(对手);
匹配。添加(vmi);
vmi.goal+=新的Match.goalevent(GoalEventHandler);
}
//Program.cm.nextMatches是一个列表对象,其中包含下一场比赛的队对;
我将这些团队数组转换为Match对象,因为这个类有两个团队字段,并且有event和PlayMatch方法,它仍然是包含(仅)第一个代码块的方法。我在UI刷新方面也遇到了问题,并且直接使用“BackgroundWorker”线程,而不仅仅是“Thread”类。BackgroundWorker线程允许您显式地报告更改的进度,并在线程外调用某些方法(即:调用辅助线程的调用UI线程)。因此,我创建了一个从后台工作程序派生的自定义类,还创建了您所描述的“Match”类的我自己的版本
public class Match
{
public Match( string Home, string Away )
{
HomeTeam = Home;
HomeGoals = 0;
AwayTeam = Away;
AwayGoals = 0;
}
// simple properties with PROTECTED setters, yet readable by anyone
public string HomeTeam
{ get; protected set; }
public int HomeGoals
{ get; protected set; }
public string AwayTeam
{ get; protected set; }
public int AwayGoals
{ get; protected set; }
// just place-holder since I don't know rest of your declarations
public EventHandler goal;
public void PlayMatch()
{
for (int i = 0; i < 6; i++)
{
if (i % 2 == 0)
HomeGoals++;
else
AwayGoals++;
// Report to anyone listening that a score was made...
if (goal != null)
goal(this, null);
Thread.Sleep(1000);
}
}
}
// Now, the background worker
public class MatchBGW : BackgroundWorker
{
// each background worker preserves the "Match" instance it is responsible for.
// this so "ReportProgress" can make IT available for getting values.
public Match callingMatch
{ get; protected set; }
// require parameter of the match responsible for handling activity
public MatchBGW(Match m)
{
// preserve the match started by the background worker activity
callingMatch = m;
// tell background worker what method to call
// using lambda expression to cover required delegate parameters
// and just call your function ignoring them.
DoWork += (sender, e) => m.PlayMatch();
// identify we can report progress
WorkerReportsProgress = true;
// Attach to the match. When a goal is scored, notify ME (background worker)
m.goal += GoalScored;
}
// this is called from the Match.
public void GoalScored(object sender, EventArgs e)
{
// Now, tell this background worker to notify whoever called IT
// that something changed. Can be any percent, just something to
// trigger whoever called this background worker, so reported percent is 1
ReportProgress(1);
}
}
公共类匹配
{
公开赛(主场、客场)
{
主队=主场;
家庭目标=0;
AwayTeam=远离;
AwayGoals=0;
}
//具有受保护的setter的简单属性,但任何人都可以读取
公共字符串主队
{get;受保护集;}
公共目标
{get;受保护集;}
公共字符串自动转换
{get;受保护集;}
公共交通警署
{get;受保护集;}
//因为我不知道你们其余的声明,所以我只是占位符
公共目标;
公开竞技场(
{
对于(int i=0;i<6;i++)
{
如果(i%2==0)
家庭目标++;
其他的
AwayGoals++;
//向任何听到的人报告评分结果。。。
如果(目标!=null)
目标(this,null);
睡眠(1000);
}
}
}
//现在,背景工作者
公共类MatchBGW:BackgroundWorker
{
//每个后台工作人员保留其负责的“匹配”实例。
//这使得“ReportProgress”可以用于获取值。
公开比赛
{get;受保护集;}
//需要负责处理活动的匹配参数
公共匹配BGW(匹配m)
{
//保留由后台工作程序活动启动的匹配
callingMatch=m;
//告诉后台工作人员要调用什么方法
//使用lambda表达式覆盖所需的委托参数
//然后调用函数忽略它们。
DoWork+=(发送方,e)=>m.PlayMatch();
//确定我们可以报告进展情况
WorkerReportsProgress=true;
//连接到比赛。当进球时,通知我(后台工作人员)
m、 目标+=得分得分;
}
//这是比赛的结果。
public void GoalScored(对象发送方、事件参数)
{
//现在,告诉这个后台工作人员通知打电话的人
//有些东西改变了。可以是任何百分比,只是一些改变
//触发调用此后台工作程序的人,因此报告的百分比为1
报告进展(1);
}
}
现在,从带有标签的呼叫窗口,例如从按钮开始单击
private void button1_Click(object sender, RoutedEventArgs e)
{
// create your "Matches" between teams
matches = new List<Match>();
matches.Add(new Match("A", "B"));
matches.Add(new Match("C", "D"));
matches.Add(new Match("E", "F"));
foreach (Match m in matches)
{
// create an instance of background worker and pass in your "match"
MatchBGW bgw = new MatchBGW(m);
// tell the background worker that if it is notified to "
// report progress" to, to pass itself (background worker object)
// to this class's SomeoneScored method (same UI thread as textbox)
bgw.ProgressChanged += SomeoneScored;
// Now, start the background worker and start the next match
bgw.RunWorkerAsync();
}
}
// This is called from the background worker via "ReportProgress"
public void SomeoneScored(object sender, ProgressChangedEventArgs e)
{
// Just ensuring that the background worker IS that of what was customized
if (sender is MatchBGW)
{
// get whatever "match" associated with the background worker
Match m = ((MatchBGW)sender).callingMatch;
// add it's latest score with appropriate home/away team names
this.txtAllGoals.Text +=
string.Format("{0} {1} - {2} {3}\r\n",
m.HomeTeam, m.HomeGoals, m.AwayGoals, m.AwayTeam );
}
}
private void按钮1\u单击(对象发送者,路由目标)
{
//创建团队之间的“比赛”
匹配项=新列表();
匹配项。添加(新匹配项(“A”、“B”));
匹配。添加(新匹配(“C”、“D”);
匹配项。添加(新匹配项(“E”、“F”));
foreach(匹配中的匹配m)
{
//创建后台工作程序实例并传入“匹配”
MatchBGW-bgw=新的MatchBGW(m);
//告诉后台工作人员,如果它被通知“
//将进度报告给,以传递自身(后台工作对象)
//该类的SomeoneScored方法(与textbox相同的UI线程)
bgw.ProgressChanged+=某人得分;
//现在,启动后台工作程序并开始下一个匹配
bgw.RunWorkerAsync();
}
}
//这是通过“ReportProgress”从后台工作程序调用的
public void SomeoneScored(对象发送者,progresschangedventargs e)
{
//只是确保后台工作人员是定制的工作人员
如果(发送方为匹配BGW)
{
//获取与后台工作程序关联的任何“匹配”
匹配m=((MatchBGW)发送方)。调用匹配;
//用适当的主客场球队名称添加其最新得分
此文件为.txtallgools.Text+=
string.Format(“{0}{1}-{2}{3}\r\n”,
m、 主队,m.HomeGoals,m.AwayGoals,m.AwayTeam);
}
}
是的,它可能是更多的代码,但我明确地处理谁被召回,并报告什么在他们适当的thre
public class Match
{
public Match( string Home, string Away )
{
HomeTeam = Home;
HomeGoals = 0;
AwayTeam = Away;
AwayGoals = 0;
}
// simple properties with PROTECTED setters, yet readable by anyone
public string HomeTeam
{ get; protected set; }
public int HomeGoals
{ get; protected set; }
public string AwayTeam
{ get; protected set; }
public int AwayGoals
{ get; protected set; }
// just place-holder since I don't know rest of your declarations
public EventHandler goal;
public void PlayMatch()
{
for (int i = 0; i < 6; i++)
{
if (i % 2 == 0)
HomeGoals++;
else
AwayGoals++;
// Report to anyone listening that a score was made...
if (goal != null)
goal(this, null);
Thread.Sleep(1000);
}
}
}
// Now, the background worker
public class MatchBGW : BackgroundWorker
{
// each background worker preserves the "Match" instance it is responsible for.
// this so "ReportProgress" can make IT available for getting values.
public Match callingMatch
{ get; protected set; }
// require parameter of the match responsible for handling activity
public MatchBGW(Match m)
{
// preserve the match started by the background worker activity
callingMatch = m;
// tell background worker what method to call
// using lambda expression to cover required delegate parameters
// and just call your function ignoring them.
DoWork += (sender, e) => m.PlayMatch();
// identify we can report progress
WorkerReportsProgress = true;
// Attach to the match. When a goal is scored, notify ME (background worker)
m.goal += GoalScored;
}
// this is called from the Match.
public void GoalScored(object sender, EventArgs e)
{
// Now, tell this background worker to notify whoever called IT
// that something changed. Can be any percent, just something to
// trigger whoever called this background worker, so reported percent is 1
ReportProgress(1);
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
// create your "Matches" between teams
matches = new List<Match>();
matches.Add(new Match("A", "B"));
matches.Add(new Match("C", "D"));
matches.Add(new Match("E", "F"));
foreach (Match m in matches)
{
// create an instance of background worker and pass in your "match"
MatchBGW bgw = new MatchBGW(m);
// tell the background worker that if it is notified to "
// report progress" to, to pass itself (background worker object)
// to this class's SomeoneScored method (same UI thread as textbox)
bgw.ProgressChanged += SomeoneScored;
// Now, start the background worker and start the next match
bgw.RunWorkerAsync();
}
}
// This is called from the background worker via "ReportProgress"
public void SomeoneScored(object sender, ProgressChangedEventArgs e)
{
// Just ensuring that the background worker IS that of what was customized
if (sender is MatchBGW)
{
// get whatever "match" associated with the background worker
Match m = ((MatchBGW)sender).callingMatch;
// add it's latest score with appropriate home/away team names
this.txtAllGoals.Text +=
string.Format("{0} {1} - {2} {3}\r\n",
m.HomeTeam, m.HomeGoals, m.AwayGoals, m.AwayTeam );
}
}
public class MainWindowViewModel : DispatcherObject, INotifyPropertyChanged
{
public MainWindowViewModel()
{
Matches = new ObservableCollection<MatchViewModel>();
}
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<MatchViewModel> _matches;
public ObservableCollection<MatchViewModel> Matches
{
get { return _matches; }
set
{
_matches = value;
OnPropertyChanged("Matches");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class MatchViewModel : DispatcherObject, INotifyPropertyChanged
{
public MatchViewModel()
{
HomeTeam = new TeamViewModel();
AwayTeam = new TeamViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
private TeamViewModel _homeTeam;
public TeamViewModel HomeTeam
{
get { return _homeTeam; }
set
{
_homeTeam = value;
OnPropertyChanged("HomeTeam");
}
}
private TeamViewModel _awayTeam;
public TeamViewModel AwayTeam
{
get { return _awayTeam; }
set
{
_awayTeam = value;
OnPropertyChanged("AwayTeam");
}
}
public void PlayMatch()
{
for (int i = 0; i < 6; i++)
{
if (i % 2 == 0)
OnGoalScored(HomeTeam);
else
OnGoalScored(AwayTeam);
Thread.Sleep(1000);
}
}
private void OnGoalScored(TeamViewModel team)
{
if (!team.Dispatcher.CheckAccess())
{
team.Dispatcher.Invoke((Action<TeamViewModel>)OnGoalScored, team);
}
else
{
team.Score++; // do other stuff here
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class TeamViewModel : DispatcherObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
private int _score;
public int Score
{
get { return _score; }
set
{
_score = value;
OnPropertyChanged("Score");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindowViewModel mainWindow = new MainWindowViewModel();
List<Thread> matchThreads = new List<Thread>();
foreach (Team[] opponents in Program.cm.nextMatches)
{
MatchViewModel match = new MatchViewModel();
match.HomeTeam.Name = opponents[0].Name;
match.AwayTeam.Name = opponents[1].Name;
mainWindow.Matches.Add(match);
Thread matchThread = new Thread(match.PlayMatch);
matchThreads.Add(matchThread);
matchThread.Start();
}
MainWindow = new MainWindow();
MainWindow.DataContext = mainWindow;
MainWindow.Show();
}
<Window x:Class="TestMatch.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<ItemsControl ItemsSource="{Binding Matches}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} {1} - {2} {3}">
<Binding Path="HomeTeam.Name"/>
<Binding Path="HomeTeam.Score"/>
<Binding Path="AwayTeam.Name"/>
<Binding Path="AwayTeam.Score"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
public partial class Program
{
protected override void OnStartup(StartupEventArgs e)
{
...
}
private class Team
{
public string Name { get; set; }
}
private static class cm
{
static cm()
{
nextMatches =
new Team[][]
{
new[] { new Team { Name = "TeamA" }, new Team { Name = "TeamB" }},
new[] { new Team { Name = "TeamC" }, new Team { Name = "TeamD" }},
new[] { new Team { Name = "TeamE" }, new Team { Name = "TeamF" }},
new[] { new Team { Name = "TeamG" }, new Team { Name = "TeamH" }},
new[] { new Team { Name = "TeamI" }, new Team { Name = "TeamJ" }},
new[] { new Team { Name = "TeamK" }, new Team { Name = "TeamL" }},
new[] { new Team { Name = "TeamM" }, new Team { Name = "TeamN" }},
new[] { new Team { Name = "TeamO" }, new Team { Name = "TeamP" }},
new[] { new Team { Name = "TeamQ" }, new Team { Name = "TeamR" }},
};
}
public static Team[][] nextMatches { get; set; }
}
}