我已经尝试了几种让它工作的方法,后台工作者,Dispatcher.Invoke,在被调用类中的线程,似乎没有任何工作。到目前为止,最好的解决方案是一个Extension方法,它调用控件的调用。另外,我试过避免通过我的事件类传递标签的数据,只是在我的处理代码中调用,但是没有区别。
关于后台组件,我不断得到异常,说后台工作人员很忙,所以我多次实例化了这个类,但是只有整个操作完成后标签才会明显改变。
我删除了以前的代码,这里是相关的一切,因为它似乎很难解决。
正在调用的方法
private void TestUris()
{
string text = new TextRange(rtxturis.Document.ContentStart, rtxturis.Document.ContentEnd).Text;
string[] lines = Regex.Split(text.Remove(text.Length - 2), "\r\n");
foreach (string uri in lines)
{
SafeUpdateStatusText(uri);
bool result;
string modUri;
if (!uri.Contains("http://"))
{
modUri = uri;
result = StoreData.LinkUriExists(new Uri("http://" + modUri));
}
else
{
modUri = uri.Substring(7);
result = StoreData.LinkUriExists(new Uri(uri));
}
if (!result)
{
Yahoo yahoo = new Yahoo();
yahoo.Status.Sending += (StatusChange);
uint yahooResult = 0;
yahooResult = yahoo.ReturnLinkCount(modUri);
if (yahooResult > 1000 )
{ results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, 1000, "Will be processed", true)); }
else
{ results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, (int)yahooResult, "Insufficient backlinks", false)); }
}
else
{
results.Add(new ScrapeDetails(Guid.NewGuid(), modUri, 0, "Previously been processed", false));
}
}
foreach (var record in results)
{
dgvresults.Items.Add(record);
}
EnableStartButton();
}
Yahoo Class
public class Yahoo
{
/// <summary>
/// Returns the amount of links each Uri has.
/// </summary>
public uint ReturnLinkCount(string uri)
{
string html;
Status.Update(uri, false); //this is where the status is called
try
{
html = client.DownloadString(string.Format("http://siteexplorer.search.yahoo.com/search?p=http%3A%2F%2F{0}&fr=sfp&bwm=i", uri));
}
catch (WebException ex)
{
ProcessError(ex.ToString());
return 0;
}
return (LinkNumber(html));
}
状态类
public class StatusEventArgs : EventArgs
{
private string _message;
private bool _isidle;
public StatusEventArgs(string message, bool isidle)
{
this._message = message;
this._isidle = isidle;
}
public bool IsIdle
{
get { return _isidle; }
}
public string Message
{
get { return _message; }
}
}
public class Status
{
public Status()
{
}
// Declaring an event, with a custom event arguments class
public event EventHandler<StatusEventArgs> Sending;
// Some method to fire the event.
public void Update(string message, bool isIdle)
{
StatusEventArgs msg = new StatusEventArgs(message, isIdle);
OnUpdate(msg);
}
// The method that invokes the event.
protected virtual void OnUpdate(StatusEventArgs e)
{
EventHandler<StatusEventArgs> handler = Sending;
if (handler != null)
{
handler(this, e);
}
}
}
更改标签内容的方法
private void StatusChange(object sender, StatusEventArgs e)
{
if(!e.IsIdle)
{
lblstatus.Content = e.Message;
lblstatus.Foreground = StatusColors.Green;
lblstatus.Refresh();
}
else
{
lblstatus.Content = e.Message;
lblstatus.Foreground = StatusColors.Grey;
lblstatus.Refresh();
}
}
名为的刷新静态方法:
public static class ExtensionMethods
{
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(DispatcherPriority.Render , EmptyDelegate);
}
另一个编辑:我盯着我的代码一段时间,我已经意识到,foreach循环会很快执行,花费时间的操作是
yahooResult = yahoo.ReturnLinkCount(modUri);
因此,我已经声明了状态类(处理事件并调用标签等)并为其提供了子类。我得到了更好的结果,虽然它仍然感觉随机,有时我会看到一些标签更新,有时即使完全相同的URI传递,也很奇怪。
答案 0 :(得分:6)
我希望有某事。有用...
private void button1_Click(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem(o =>
{
int result = 0;
for (int i = 0; i < 9999999; i++)
{
result++;
Dispatcher.BeginInvoke(new Action(() =>
{
this.label1.Content = result;
}));
Thread.Sleep(1);
}
});
}
答案 1 :(得分:3)
解决了它WOOHOOOOOOOO 3天的测试,测试和测试。
我决定使用上面的扩展方法启动一个新项目,并使用一个简单的for循环来测试UI更新功能。我开始测试不同的DispatchPrioraties(全部测试过)。
奇怪的是,我发现最高优先级更差,例如使用Send根本没有更新标签,Render平均更新了两次。当我尝试不同的优先事项时,这是我遇到的奇怪行为。我发现了背景:
枚举值为4.在完成所有其他非空闲操作后处理操作。
现在这听起来正是我不想要的,显然标签应该在处理过程中更新,因此我从未尝试过它。我猜测,一旦我的方法之一完成,在下一次调用之前,UI就会更新。我发现猜测,但它在两个单独的操作中100%一致地正确更新。
谢谢大家。
答案 2 :(得分:3)
嗯,这听起来很愚蠢,但你可以只引用表单命名空间,然后你可以这样做
using System.Windows.Forms;
mylabel = "Start";
Application.doEvents();
myLabel = "update"
Application.doEvents();
现在使用它的问题是你使用的是wpf,但你仍然可以引用表单并使用这个命名空间。另一个问题是,que中的任何内容都会直接执行到ui。然而,这是我能想到的最简单的标签更新方式。我不确定为什么这会使用不好的其他原因,但它是OP的解决方案。如果你有任何好的理由,那就不好投票,只需通过发表评论告知自己和其他人。感谢。
答案 3 :(得分:1)
将状态信息添加为此对象的属性会更容易/更好吗,让它只是触发属性更改通知吗?
那样标签文本(或其他)可以绑定到属性而不是让异步工作尝试更新标签吗?
或添加这样的方法来更新状态,如果你需要更新它?
void SafeUpdateStatusText(string text)
{
// update status text on event thread if necessary
Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate
{
lblstatus.Content = text;
}, null);
}
否则,我认为我们还没有足够的细节来帮助......
答案 4 :(得分:0)
我希望这会有所帮助:
private delegate void UpdateLabelDelegate(DependencyProperty dp, object value);
public void UpdateLabelContent(Label label, string newContent)
{
Dispatcher.Invoke(new UpdateLabelDelegate(label.SetValue), DispatcherPriority.Background, ContentProperty, newContent);
}
用法:
while (true)
{
UpdateLabelContent(this.lblStatus, "Next random number: " + new Random().Next());
Thread.Sleep(1000);
}