2013-12-16 33 views
2

用例
我正在开发一个C#中的小应用程序,它由另一个应用程序调用以从Internet检索数据。它作为一个独立的进程运行,但几乎所有与它的交互都由调用应用程序管理。因此它没有GUI。不过,我想添加一个使用WPF的进度条,在某些数据检索过程中显示,可能需要一分钟。估计完成了多少工作以及剩下多少工作很容易,因此我找到了适合的进度条。来自主线程的WPF窗口中的调用方法

研究做
我有螺纹的公平理解阅读阿尔巴哈利的PDF的大部分穿线(http://www.albahari.info/threading/threading.pdf)之后。我在这个问题上也通过了SO和MSDN上的很多帖子。大多数帖子都建议使用后台工作人员进行耗时的数据检索,同时将GUI保留在主线程中,因此可以使用后台工作人员提出解决方案。然而,在这种情况下,这感觉很尴尬,主要任务是数据检索而不是GUI交互。

我花了大量的时间尝试理解不同的教程和论坛帖子,同时尝试使它们符合我的问题,但我没有成功,现在我已经回到了原点。基本上我想有以下所列出的两个类来结束:

ProgressBarWindow

public partial class ProgressBarWindow : Window 
{ 
    public ProgressBarWindow() 
    { 
     InitializeComponent(); 
    } 

    public void setValue(int value) 
    { 
     // This function should be available from the main thread 
    } 
} 

查询器

Public class Querier 
{ 



    public List<Item> getItems() 
    { 
     // call ProgressBarWindow.setValue(0); 
     ... 
     // call ProgressBarWindow.setValue(100); 
     // call ProgressBarWindow.Close(); 
    } 
} 

这是我的理解是UI必须在单个线程运行因此我的ProgressBarWindow对象不能在新线程中实例化,而同时可用于主线程(类型)。

Dispatcher.BeginInvoke似乎是我的救世主,但到目前为止我还没有弄清楚应该进入Querier课程以及ProgressBarWindow课程中的内容。我怎样才能使这两个线程与ProgressBarWindow的同一个实例进行交互?

请问你是否需要更多细节,我会尽力澄清。

+0

请避免'的setXXX()'和'的getXXX()'句法。它看起来非常类似于我即将吐出的Java。相反,使用类似C#的代码并创建适当的属性。 –

+0

采取点!我通常尝试。 – Anders

回答

1

您可以使用Progress类来使用长时间运行操作的当前进度更新UI。

首先在你的UI创建Progress一个实例:

Progress<int> progress = new Progress<int>(currentProgress => 
{ 
    progressBar.Value = currentProgress; 
    //todo do other stuff 
}); 

然后把它传递给长时间运行的过程:

public List<Item> getItems(IProgress<int> progress) 
{ 
    progress.Report(0); 
    //todo do something 
    progress.Report(100); 
} 
+0

我还没有尝试过,但我试图在我的脑海中做一些简单的工作。我没有看到应该创建新线程的位置。这是否会在“长期运行过程”中实施? – Anders

+0

@Anders我的印象是,您在后台线程中运行某些代码时没有问题,而是将进度返回到UI线程。您可以简单地使用Task.Run(()=> whatever.GetItems(progress))'在非UI线程中运行'getItems',尽管还有许多其他技术都可以运行。这是使用'Progress'的主要优势,它不关心你如何在另一个线程中运行代码。 – Servy

+0

我想反过来。进度条应该在一个后台线程中运行,这让我留下了一些类似于:'Task.Run(()=> new ProgressBarDialog()。showDialog()'。但是我猜想可能在匿名函数中交换委托作为以便能够通过它们传输状态更改? – Anders

0

这里是我通常使用一个通用的功能:

public static void Invoke(this UIElement element,Action action) 
    { 
     element.Dispatcher.Invoke(action, null); 
    } 

,并使用它,只需拨打:

this.Invoke(() => ProgressBarWindow.SetValue(0)); 

所以,在getItems()函数,你会沿着有什么行:

public List<Item> getItems() 
{ 
    ProgressBarWindow wnd; 
    MainWindow.Invoke(() => wnd = new ProgressBarWindow()) 
    MainWindow.Invoke(() => wnd.SetValue(0)) 
    ... 
    MainWindow.Invoke(() => wnd.SetValue(100)) 
    MainWindow.Invoke(() => wnd.Close()) 
} 

确保你总是有办法到达主窗口是什么(一个r从App.xml或App.Run(...)统一起来。然后,您可以通过它发出任何GUI操作(即使您必须创建新的Loader窗口,例如只要在主线程内完成)

+0

谢谢你的回答。但我不确定如何处理Invoke方法中的第一个形式参数。您的示例没有声明发送的内容,因为实际的参数和Visual Studio抱怨参数的数量。 – Anders

0

App。XAML

public partial class App : Application 
{ 
    private void Application_Startup_1(object sender, StartupEventArgs e) 
    { 
     Task.Factory.StartNew<List<int>>(() => Querier.GetItems()); 
    } 
} 

ProgressBarWindow.xaml.cs

public partial class ProgressWindow : Window 
{ 
    public ProgressWindow() 
    { 
     InitializeComponent(); 
     Querier.Start +=()=> Visibility = Visibility.Visible; 
     Querier.Stop +=() => Visibility = Visibility.Collapsed; 
     Querier.ReportProgress +=OnReportProgress; 
    } 

    public void OnReportProgress(int value) 
    { 
     txtBox.Text = value.ToString(); 
    } 
} 

ProgressBarWindow.xaml

<Grid> 
    <TextBox x:Name="txtBox"></TextBox> 
</Grid> 

查询器

public class Querier 
{ 
    public static event Action Start; 

    public static event Action Stop; 

    public static event Action<int> ReportProgress; 

    public static List<int> GetItems() 
    { 
     if (Start != null) 
      App.Current.Dispatcher.BeginInvoke(Start,null); 

     for (int index = 0; index <= 10; index++) 
     { 
      Thread.Sleep(200); 
      if (ReportProgress != null) 
       App.Current.Dispatcher.BeginInvoke(ReportProgress, index*10); 
     } 


     if (Stop != null) 
      App.Current.Dispatcher.BeginInvoke(Stop, null); 

     return Enumerable.Range(1, 100).ToList(); 

    } 

} 

我只是想给一个想法,希望这将有助于。

+0

我的项目并不是一个真正的WPF项目,所以我缺少一个app.xaml。我刚刚添加了一个WPF窗口。因此,我有一个Main方法作为入口点。这似乎让我无法调用“App.Current ....”或相等。 – Anders

+0

那么[STAThread]属性在哪里?在你的MainMethod上... – ethicallogics

+0

是的,这是正确的。我目前有一个名为'ThreadTest'的类,并且声明了[STAThread]属性。 Visual Studio无法识别'ThreadTest'上的'Current' – Anders

相关问题