长时间处理后显示对话框

时间:2013-08-13 14:50:51

标签: c# multithreading

在下面的代码中,我有一个名为GetExcelData的长时间运行过程。完成后,我想显示一个对话框,将其内容保存到TXT文件中。

问题是,在调试时,我收到以下错误:

  

当前线程必须设置为单线程单元(STA)模式   在进行OLE调用之前。确保您的主要功能   STAThreadAttribute标记在上面。只有在a时才会引发此异常   调试器附加到进程。

这是我的代码。该错误发生在读取saveFileDialog1.ShowDialog();

的行上
FileInfo existingFile = new FileInfo("C:\\MyExcelFile.xlsx");

ConsoleApplication2.Program.ExcelData data = ConsoleApplication2.Program.GetExcelData(existingFile, _worker);

var json = new JavaScriptSerializer().Serialize(data);

SaveFileDialog saveFileDialog1 = new SaveFileDialog();

saveFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
saveFileDialog1.ShowDialog();

if (saveFileDialog1.FileName != "")
{
    File.WriteAllText(saveFileDialog1.FileName, json);
}

我尝试将[STAThread]属性添加到我调用它的方法中,但它似乎无法正常工作。

请让我提供更多代码,以便更清楚地说明我要做的事情:

WPF项目中存在以下内容,该项目引用了我的控制台项目:

private BackgroundWorker _backgroundWorker = new BackgroundWorker();

public MainWindow()
{
    InitializeComponent();

    // Set up the BackgroundWorker.
    this._backgroundWorker.WorkerReportsProgress = true;
    this._backgroundWorker.WorkerSupportsCancellation = true;
    this._backgroundWorker.DoWork += new DoWorkEventHandler(bw_DoWork);
    this._backgroundWorker.ProgressChanged +=
                              new ProgressChangedEventHandler(bw_ProgressChanged);
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    if (this._backgroundWorker.IsBusy == false)
    {
        this._backgroundWorker.RunWorkerAsync();
    }
    e.Handled = true;
}

void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // Set the Value porperty when porgress changed.
    this.progressBar1.Value = (double)e.ProgressPercentage;
}

void bw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker _worker = sender as BackgroundWorker;
    if (_worker != null)
    {
        FileInfo existingFile = new FileInfo("C:\\MyExcelFile.xlsx");

        ConsoleApplication2.Program.ExcelData data = ConsoleApplication2.Program.GetExcelData(existingFile, _worker);

        var json = new JavaScriptSerializer().Serialize(data);

        SaveFileDialog saveFileDialog1 = new SaveFileDialog();

        saveFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
        saveFileDialog1.ShowDialog();

        if (saveFileDialog1.FileName != "")
        {
            File.WriteAllText(saveFileDialog1.FileName, json);
        }
    }
}

3 个答案:

答案 0 :(得分:1)

将与UI交互的代码移动到处理UI元素的同一个线程。通过RunWorkerCompleted事件

执行此操作的最简单方法
  this._backgroundWorker.RunWorkerCompleted +=
                          new RunWorkerCompletedEventHandler(bw_WorkComplete);

  ....

void bw_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker _worker = sender as BackgroundWorker;
    if (_worker != null)
    {
        FileInfo existingFile = new FileInfo("C:\\MyExcelFile.xlsx");
        ConsoleApplication2.Program.ExcelData data = ConsoleApplication2.Program.GetExcelData(existingFile, _worker);

        e.Result = new JavaScriptSerializer().Serialize(data);
     }
 }

 private void bw_WorkComplete(object sender, RunWorkerCompletedEventArgs e)
 {
    SaveFileDialog saveFileDialog1 = new SaveFileDialog();
    saveFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
    saveFileDialog1.ShowDialog();

    if (saveFileDialog1.FileName != "")
    {
       string json = e.Result.ToString();
       File.WriteAllText(saveFileDialog1.FileName, json);
    }
}

在DoWork方法中,将json字符串保存在DoWorkEventArgs类的e.Result属性中,并使用相同名称从RunWorkerCompletedEventArgs属性中的RunWorkerCompleted事件中检索它。

答案 1 :(得分:1)

为什么?

基本上,您从saveFileDialog1.ShowDialog();致电bw_DoWork。这不对。 Dialog是UI控件,应该从UI线程运行,bw_DoWork方法在单独的线程(非UI)中执行。

如何解决这个问题?

将对话框显示代码移离bw_DoWork方法,然后传递所需的字符串。所以算法看起来像

  • 单击按钮或任何操作以显示对话框[UI主题]
  • 打开对话框[UI主题]
  • 确认您从对话框[UI主题]
  • 获取有效字符串
  • 启动后台工作程序并传递文件路径字符串[UI thread]
  • 写入文件[后台工作人员主题]

答案 2 :(得分:0)

修改您的Program.cs,以便Main方法的声明如下所示:

[STAThread]
static void Main()