gpt4 book ai didi

c# - 异步更新 ObservableCollection 会导致挂起,并且不会更新 GUI

转载 作者:行者123 更新时间:2023-11-30 21:12:46 25 4
gpt4 key购买 nike

我正在 WPF 中实现 Tracert 的可视化版本(作为学习练习),其中结果进入列表框。问题是 (1) 绑定(bind)到 tracertDataView 的列表框没有更新,但 (2) 我的整个应用程序挂起。

我确定 #2 是线程问题,但我不确定如何(以正确的方式)更正它。此外,我不确定我更新/绑定(bind)“DoTrace”结果的技术是否正确。

这是我在 App.xaml

中的数据源
<Window.Resources>
<CollectionViewSource
Source="{Binding Source={x:Static Application.Current}, Path=TracertResultNodes}"
x:Key="tracertDataView" />

</Window.Resources>

App.xaml.cs

public partial class App : Application
{
private ObservableCollection<TracertNode> tracertResultNodes = new ObservableCollection<TracertNode>();

public void AppStartup(object sender, StartupEventArgs e)
{
// NOTE: Load sample data does work correctly.. and displays on the screen.
// subsequent updates do not display
LoadSampleData();
}

private void LoadSampleData()
{

TracertResultNodes = new ObservableCollection<TracertNode>();

TracertNode t = new TracertNode();
t.Address = new System.Net.IPAddress(0x2414188f);
t.RoundTripTime = 30;
t.Status = System.Net.NetworkInformation.IPStatus.BadRoute;

TracertResultNodes.Add(t);
}

public ObservableCollection<TracertNode> TracertResultNodes
{
get { return this.tracertResultNodes; }
set { this.tracertResultNodes = value; }
}
}

这是MainWindow代码

  public partial class MainWindow : Window
{
CollectionViewSource tracertDataView;
TraceWrapper _tracertWrapper = null;

public MainWindow()
{
InitializeComponent();
_tracertWrapper = new TraceWrapper();

tracertDataView = (CollectionViewSource)(this.Resources["tracertDataView"]);
}

private void DoTrace_Click(object sender, RoutedEventArgs e)
{
((App)Application.Current).TracertResultNodes = _tracertWrapper.Results;

_tracertWrapper.DoTrace("8.8.8.8", 30, 50);
}
}

仅供引用实例对象“traceWrapper.DoTrace”的内部实现细节

    /// <summary>
/// Trace a host. Note that this object internally calls the Async implementation of .NET's PING.
// It works perfectly fine in a CMD host, but not in WPF
/// </summary>
public ObservableCollection<TracertNode> DoTrace(string HostOrIP, int maxHops, int TimeOut)
{
tracert = new Tracert();

// The following is triggered for every host that is found, or upon timeout
// (up to 30 times by default)
AutoResetEvent wait = new AutoResetEvent(false);
tracert.waiter = wait;

tracert.HostNameOrAddress = HostOrIP;

tracert.Trace();

this.Results = tracert.NodeList;

while (tracert.IsDone == false)
{
wait.WaitOne();
IsDone = tracert.IsDone;
}
return tracert.NodeList;
}

最佳答案

我不明白你是如何使用 AutoResetEvent 的,我想它不应该以这种方式使用:)

但是由于 Trace 已经在另一个线程中运行,您确定您的 Tracert 类中没有事件“OnTracertComplete”或类似事件吗?

如果没有,为什么不将 DispatchTimer 放入您的应用程序中?该计时器会定期轮询,直到 tracert.IsDone 变为真。如果在操作完成之前阻止应用程序线程的执行,则会阻止窗口事件循环的执行,因此窗口将永远不会更新。

另一件重要的事情:你不能从另一个线程更新 ObservableCollections。请小心并确保 WPF 窗口中更新的所有内容都是从窗口的同一线程执行的。不知道您的 Trace 类到底做了什么,但您的问题似乎当然是等待循环,这在 GUI 应用程序中没有意义。

使用通知事件或定时器来轮询结果。对于这个特定的实现,分辨率为 1 秒的计时器对我来说似乎很好,而且对性能的影响绝对是最小的。

如果您能够修改 Tracert 类,这是一个可能的实现。

    public delegate void TracertCallbacHandler(Tracert sender, TracertNode newNode);

public class Tracert
{
public event TracertCallbacHandler NewNodeFound;
public event EventHandler TracertCompleted;

public void Trace()
{
....
}

// This function gets called in tracert thread\async method.
private void FunctionCalledInThreadWhenPingCompletes(TracertNode newNode)
{
var handler = this.NewNodeFound;
if (handler != null)
handler(this, newNode);
}

// This function gets called in tracert thread\async methods when everything ends.
private void FunctionCalledWhenEverythingDone()
{
var handler = this.TracertCompleted;
if (handler != null)
handler(this, EventArgs.Empty);
}

}

这是运行 tracert 的代码,这是 TracertWrapper。

    // Keep the observable collection as a field.
private ObservableCollection<TracertNode> pTracertNodes;

// Keep the instance of the running tracert as a field, we need it.
private Tracert pTracert;

public bool IsTracertRunning
{
get { return this.pTracert != null; }
}

public ObservableCollection<TracertNode> DoTrace(string hostOrIP, int maxHops, int timeOut)
{
// If we are not already running a tracert...
if (this.pTracert == null)
{
// Clear or creates the list of tracert nodes.
if (this.pTracertNodes == null)
this.pTracertNodes = new ObservableCollection<TracertNode>();
else
this.pTracertNodes.Clear();

var tracert = new Tracert();
tracert.HostNameOrAddress = hostOrIP;
tracert.MaxHops = maxHops;
tracert.TimeOut = timeOut;

tracert.NewNodeFound += delegate(Tracert sender, TracertNode newNode)
{
// This method is called inside Tracert thread.
// We need to use synchronization context to execute this method in our main window thread.

SynchronizationContext.Current.Post(delegate(object state)
{
// This method is called inside window thread.
this.OnTracertNodeFound(this.pTracertNodes, newNode);
}, null);
};

tracert.TracertCompleted += delegate(object sender, EventArgs e)
{
// This method is called inside Tracert thread.
// We need to use synchronization context to execute this method in our main window thread.

SynchronizationContext.Current.Post(delegate(object state)
{
// This method is called inside window thread.
this.OnTracertCompleted();
}, null);
};

tracert.Trace();

this.pTracert = tracert;
}

return this.pTracertNodes;
}

protected virtual void OnTracertCompleted()
{
// Remove tracert object,
// we need this to let the garbage collector being able to release that objects.
// We need also to allow another traceroute since the previous one completed.
this.pTracert = null;

System.Windows.MessageBox.Show("TraceRoute completed!");
}

protected virtual void OnTracertNodeFound(ObservableCollection<TracertNode> collection, TracertNode newNode)
{
// Add our tracert node.
collection.Add(newNode);
}

关于c# - 异步更新 ObservableCollection<T> 会导致挂起,并且不会更新 GUI,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7273226/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com