在Silverlight 3中,使用SaveFileDialog时必须由用户发起对话框。

18
我正在使用C#开发一个Silverlight 3应用程序。我想允许用户从该应用程序中下载图像。我使用SaveFileDialog执行文件下载任务,流程如下:
  1. 用户在SL应用程序中单击下载按钮。
  2. 调用Web服务以从服务器获取图像。
  3. 在完成异步事件处理程序中调用Web方法并接收来自服务器的二进制图像。
  4. 在OnCompleted事件处理程序中,提示用户选择保存图像到计算机的位置。
  5. 将图像流式传输到用户硬盘上的文件中。
我在从OnCompleted事件处理程序调用的函数中使用以下代码来完成SaveFileDialog提示,然后将其流式传输到文件中。
            SaveFileDialog dialog = new SaveFileDialog();
            dialog.Filter = "JPG Files|*.jpg" + "|All Files|*.*";
            bool? dialogResult = dialog.ShowDialog();

            if (dialogResult == true)
            {
                using (Stream fs = (Stream)dialog.OpenFile())
                {
                    fs.Write(e.Result, 0, e.Result.Length);
                    fs.Close();
                }
            }

在上述代码中,如果调用ShowDialog方法,SaveFileDialog会抛出“对话框必须由用户启动”的错误。我可能错过了什么?如何解决这个问题?

5个回答

20
这条错误信息的含义是,你只能在用户发起的事件(例如按钮单击)的响应中展示SaveFileDialog。在你描述的例子中,你并没有在响应点击事件时展示SaveFileDialog,而是在完成http请求后展示它(这不被视为用户发起的事件)。因此,要使其正常工作,你需要在http请求的Completed事件中向用户展示一些UI,告诉他们“已下载完成,请点此保存文件到计算机”,当用户点击此消息时,再显示SaveFileDialog。

8
在Button_Click事件处理程序中尝试直接使用SaveFileDialog时,我收到了相同的错误消息,因为在调用new SaveFileDialog()之前,我在该方法中进行了一些验证。JumpingJezza下面的链接展示了一个很好的例子,但关键似乎是要将new SaveFileDialog()作为按钮事件处理程序中的第一行。之后,你似乎可以根据需要执行其他操作。 - Jedidja
“first”这一行真的意味着“第一”。我正在调试别人的代码,一个被注释掉的代码块在按钮单击事件处理程序中引起了上述异常。 - faester
3
我认为这更多是保存对话框打开的时间问题。如果在 dialog.ShowDialog() 之前设置断点,就会出现错误。请参见 http://dotnetslang.wordpress.com/2011/03/12/using-the-savefiledialog-dialogs-must-be-user-initiated-exception/。 - maxence51

5

在下载之前先问一下怎么样?从错误信息中看来,这似乎是Silverlight希望你提示的方式,以确保它知道用户请求了该操作,而不是通过弹出窗口向用户发送垃圾邮件。

除了Silverlight安全模型之外,我宁愿在下载完成之前被问到要把文件放在哪里!


1
如Keith所述,这是按设计来的。这个教程提供了一个很好的例子,使用代码下载文件的“正确”方式。(在Silverlight 4中也适用)

1
您也可以在按钮事件处理程序中创建SaveFileDialog。 关键是确保构造函数是方法的第一行。 - Jedidja

1

我刚开始接触Silverlight 4,也遇到了同样的问题。似乎如果你手动创建事件处理程序,即使事件处理程序使用正确的参数处理按钮点击事件,也会抛出安全异常,但是如果你在Xaml中的按钮下使用“创建新的事件处理程序”选项来处理点击事件,则新的事件处理程序将具有相同的代码和参数,现在可以正常工作...这是自从从WPF转换到Silverlight以来我遇到的许多“奇怪”的事情之一。


1
Private _syncContext As SynchronizationContext
Private mBigStream As Stream

 Private Sub btnSave_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles btnSave.Click
    Try
        Dim saveDialog As New SaveFileDialog

        saveDialog.Filter = "Word |*.doc"
        saveDialog.DefaultExt = ".doc"

        If saveDialog.ShowDialog() Then
            Try
                mBigStream = saveDialog.OpenFile()

                _syncContext = SynchronizationContext.Current

                oWebService.GetReportAsync(Params, ... , _syncContext)
            Catch ex As Exception
                MessageBox.Show("File busy.")
            End Try
        End If
    Catch ex As Exception
        LogError((New System.Diagnostics.StackTrace()).GetFrame(0).GetMethod().Name.ToString, Err.Description)
    End Try
End Sub

Private Sub oWebService_GetReportCompleted(sender As Object, e As MainReference.GetReportCompletedEventArgs) Handles oWebService.GetReportCompleted
    Try
        ' e.Result is byte()

        If e.Result IsNot Nothing Then
            If e.Result.Count > 0 Then
                _syncContext.Post(Sub()
                                      Try
                                          mBigStream.Write(e.Result, 0, e.Result.Length)

                                          mBigStream.Flush()
                                          mBigStream.Close()

                                          mBigStream.Dispose()

                                          mBigStream = Nothing
                                      Catch ex As Exception
                                          LogError((New System.Diagnostics.StackTrace()).GetFrame(0).GetMethod().Name.ToString, Err.Description)
                                      End Try
                                  End Sub, Nothing)

                _syncContext = Nothing
            End If
        End If
    Catch ex As Exception
        LogError((New System.Diagnostics.StackTrace()).GetFrame(0).GetMethod().Name.ToString, Err.Description)
    End Try
End Sub

2
请您能否详细解释一下您的答案?回答应该提供指导,而不仅仅是复制粘贴代码。 - CodeMouse92

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接