在SilverLight浏览器外应用程序中显示长时间运行的保存进程的进度

在SilverLight浏览器外应用程序中显示长时间运行的保存进程的进度,silverlight,progress-bar,savefiledialog,Silverlight,Progress Bar,Savefiledialog,我有一个小小的SilverLight浏览器应用程序,它可以从网络摄像头到本地存储捕获一系列图像。然后,我希望通过Zip文件将它们从本地存储导出到用户指定的位置 到目前为止,如果所有事情都发生在主UI线程上,而没有其他方法,那么这是很平常的 但是,对于足够大的文件系列,zip文件的创建需要相当长的时间,因此我希望在后台工作线程或类似线程上执行此操作,并向用户报告进度 我的问题是: 如果我尝试在主UI线程上执行所有操作,则在保存完成之前,ProgressBar不会更新 尝试在后台工作线程上打开Sav

我有一个小小的SilverLight浏览器应用程序,它可以从网络摄像头到本地存储捕获一系列图像。然后,我希望通过Zip文件将它们从本地存储导出到用户指定的位置

到目前为止,如果所有事情都发生在主UI线程上,而没有其他方法,那么这是很平常的

但是,对于足够大的文件系列,zip文件的创建需要相当长的时间,因此我希望在后台工作线程或类似线程上执行此操作,并向用户报告进度

我的问题是:

如果我尝试在主UI线程上执行所有操作,则在保存完成之前,ProgressBar不会更新

尝试在后台工作线程上打开SaveFileDialog将不起作用,因为它是一个后台线程,也将被视为“非用户启动”

无论我如何将在SaveFileDialog中打开的流作为后台工作程序委托的一部分传递给该方法,它总是更改为
CanWrite==false
,我不能再使用它了


有没有人能举一个简单的例子,在SilverLight中保存一个大文件并报告进度?

我不能说自己对SilverLight中的文件处理有任何具体的了解,但下面是我在WPF应用程序的工作线程上执行长任务时所使用的模式。在快速测试Silverlight项目中,它似乎工作正常

我会避免尝试在线程之间传递流。相反,计算出后台任务所需的一组参数,并创建一个对象将其传递给线程。让后台线程打开文件。因此,如果您需要一个文件夹来搜索要压缩的文件,并需要一个输出位置来放入压缩文件,您可以声明:

class TaskStartupInfo
{
    public string SourceFolder { get; set; }
    public string TargetFile { get; set; }
}
然后可以创建此类的实例,并将其传递到后台任务:

private void startTaskButton_Click(object sender, RoutedEventArgs e)
{
    TaskStartupInfo tsi = new TaskStartupInfo()
    {
        SourceFolder = @"C:\Some\Folder\",
        TargetFile = @"C:\AnotherFolder\data.zip" 
    };

    ThreadPool.QueueUserWorkItem(o => longRunningProcess(tsi));
}
在您的情况下,路径可以来自在主UI线程上运行的SaveFileDialog,因为该线程不会与运行大部分工作绑定。然后,您的
longRunningProcess()
方法可以获取数据并使用它:

private void longRunningProcess(object o)
{
    TaskStartupInfo tsi = o as TaskStartupInfo;

    int taskLength = calculateTaskLength()

    // open any files required

    this.Dispatcher.BeginInvoke(() => { progressBar1.Value = 0; progressBar1.Maximum = taskLength; });

    for (int i = 0; i < taskLength; i++ )
    {
        doSomethingSlow();
        this.Dispatcher.BeginInvoke(() => progressBar1.Value += 1);
    }

    // close / dispose files
}
为了节省时间,您似乎可以使用

mySaveFileDialog.SafeFileName
相反

因此,以下代码可以在提升的浏览器外应用程序中工作:

private void start_Click(object sender, RoutedEventArgs e)
{
    SaveFileDialog sfd = new SaveFileDialog();

    if (sfd.ShowDialog() != true)
    {
        return;
    }

    TaskStartupInfo tsi = new TaskStartupInfo()
    {
        SourceFolder = @"C:\Users\MyUser\Documents\Information",
        TargetFile = sfd.SafeFileName
    };

    ThreadPool.QueueUserWorkItem(o => longRunningProcess(tsi));
}

private void longRunningProcess(object o)
{
    TaskStartupInfo tsi = o as TaskStartupInfo;

    var files = Directory.EnumerateFiles(tsi.SourceFolder);

    int taskLength = files.Count();

    this.Dispatcher.BeginInvoke(() => { progressBar1.Value = 0; progressBar1.Maximum = taskLength; });

    using (StreamWriter fs = new StreamWriter(tsi.TargetFile))
    {
        foreach(string file in files)
        {
            fs.WriteLine(file);
            doSomethingSlow();
            this.Dispatcher.BeginInvoke(() => progressBar1.Value += 1);
        }
    }
}

这为您提供了文件访问和正确更新的进度条,用于处理后台文件。

我不能声称对Silverlight中的文件处理有任何具体的了解,但以下是我在WPF应用程序的工作线程上执行长任务时使用的模式。在快速测试Silverlight项目中,它似乎工作正常

我会避免尝试在线程之间传递流。相反,计算出后台任务所需的一组参数,并创建一个对象将其传递给线程。让后台线程打开文件。因此,如果您需要一个文件夹来搜索要压缩的文件,并需要一个输出位置来放入压缩文件,您可以声明:

class TaskStartupInfo
{
    public string SourceFolder { get; set; }
    public string TargetFile { get; set; }
}
然后可以创建此类的实例,并将其传递到后台任务:

private void startTaskButton_Click(object sender, RoutedEventArgs e)
{
    TaskStartupInfo tsi = new TaskStartupInfo()
    {
        SourceFolder = @"C:\Some\Folder\",
        TargetFile = @"C:\AnotherFolder\data.zip" 
    };

    ThreadPool.QueueUserWorkItem(o => longRunningProcess(tsi));
}
在您的情况下,路径可以来自在主UI线程上运行的SaveFileDialog,因为该线程不会与运行大部分工作绑定。然后,您的
longRunningProcess()
方法可以获取数据并使用它:

private void longRunningProcess(object o)
{
    TaskStartupInfo tsi = o as TaskStartupInfo;

    int taskLength = calculateTaskLength()

    // open any files required

    this.Dispatcher.BeginInvoke(() => { progressBar1.Value = 0; progressBar1.Maximum = taskLength; });

    for (int i = 0; i < taskLength; i++ )
    {
        doSomethingSlow();
        this.Dispatcher.BeginInvoke(() => progressBar1.Value += 1);
    }

    // close / dispose files
}
为了节省时间,您似乎可以使用

mySaveFileDialog.SafeFileName
相反

因此,以下代码可以在提升的浏览器外应用程序中工作:

private void start_Click(object sender, RoutedEventArgs e)
{
    SaveFileDialog sfd = new SaveFileDialog();

    if (sfd.ShowDialog() != true)
    {
        return;
    }

    TaskStartupInfo tsi = new TaskStartupInfo()
    {
        SourceFolder = @"C:\Users\MyUser\Documents\Information",
        TargetFile = sfd.SafeFileName
    };

    ThreadPool.QueueUserWorkItem(o => longRunningProcess(tsi));
}

private void longRunningProcess(object o)
{
    TaskStartupInfo tsi = o as TaskStartupInfo;

    var files = Directory.EnumerateFiles(tsi.SourceFolder);

    int taskLength = files.Count();

    this.Dispatcher.BeginInvoke(() => { progressBar1.Value = 0; progressBar1.Maximum = taskLength; });

    using (StreamWriter fs = new StreamWriter(tsi.TargetFile))
    {
        foreach(string file in files)
        {
            fs.WriteLine(file);
            doSomethingSlow();
            this.Dispatcher.BeginInvoke(() => progressBar1.Value += 1);
        }
    }
}

这为您提供了文件访问和正确更新的进度条,用于处理后台文件。

我想我必须考虑使用提升的权限运行SL应用程序。问题是SL
SaveFileDialog
没有提供我可以访问的路径,只有用户指定的流——否则我甚至不会使用ZIP(它实际上只是所有图像的容器),我只会将文件直接写入路径。我相信,如果我被提升,我可以访问用户库文件夹而不受惩罚。为此,我欢呼——我本来不想运行提升,但在这种情况下,这似乎是愚蠢的。不过,运行提升版后,我可以完全删除整个zip文件,只需将图像直接写入“用户我的图片”文件夹中。我想我必须考虑使用提升版权限运行SL应用程序。问题是SL
SaveFileDialog
没有提供我可以访问的路径,只有用户指定的流——否则我甚至不会使用ZIP(它实际上只是所有图像的容器),我只会将文件直接写入路径。我相信,如果我被提升,我可以访问用户库文件夹而不受惩罚。为此,我欢呼——我本来不想运行提升,但在这种情况下,这似乎是愚蠢的。不过,运行提升版后,我可以完全删除整个zip文件,只需将图像直接写入一个文件夹,即用户我的图片文件夹。