1

WebCamからLocalStorageに一連の画像をキャプチャする小さなSilverLightOut-of-Browserアプリがあります。次に、それらをLocalStorageからZipファイルを介してユーザーが指定した場所にエクスポートしたいと思います。

これまでのところ、すべてがメインUIスレッドで発生し、追加のメソッドがない場合は、非常に簡単です。

ただし、一連のファイルが十分に大きい場合、zipファイルの作成にはかなりの時間がかかるため、バックグラウンドワーカースレッドなどでこれを実行して、進行状況をユーザーに報告したいと思います。

私の問題はこれです:

メインUIスレッドですべてを実行しようとすると、保存が完了するまでProgressBarは更新されません。

バックグラウンドワーカーでSaveFileDialogを開こうとすると、バックグラウンドスレッドであるため機能せず、「ユーザーが開始していない」と見なされます。

SaveFileDialogで開かれたStreamを、バックグラウンドワーカーのデリゲートの一部としてメソッドにどのように渡しても、常にに変更されCanWrite == false、使用できなくなります。

大きなファイルを保存してSilverLightで進捗状況を報告する簡単な例はありますか?

4

1 に答える 1

1

Silverlightでのファイル処理に関する特定の知識を主張することはできませんが、WPFアプリケーションのワーカースレッドでの長いタスクに使用するパターンは次のとおりです。クイックテストのSilverlightプロジェクトでは問題なく動作するようです。

スレッド間でストリームを渡そうとしないようにします。代わりに、バックグラウンドタスクに必要なパラメーターのセットを作成し、それらをスレッドに渡すためのオブジェクトを作成します。バックグラウンドスレッドにファイルを開かせます。したがって、圧縮するファイルを検索するためのフォルダーと、zipを配置するための出力場所が必要な場合は、次のように宣言できます。

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
}

progressBar1Dispatcherオブジェクトを使用してデリゲートを実行することにより、UIオブジェクト(この場合)にアクセスする試みがどのように行われるかに注意してください。このディスパッチャは、UIオブジェクトがUIスレッドによってのみ更新されるようにする問題を扱います。これにより、タスクの各フラグメントが完了した後、プログレスバーが確実に更新されます。


編集: OPのコメントに基づいて、さらに掘り下げてみると、Silverlightのセキュリティサンドボックスは、デスクトップWPFアプリケーションでは課されていないファイルアクセスに制限を課していることがわかります。

分離ストレージの外部でファイルシステムに書き込むには、Silverlightアプリを昇格して実行する必要があります。これは、プロジェクトプロパティの一部として構成できます。プロパティのSilverlightタブに[ブラウザの不足を有効にする]チェックボックスがあり、有効にすると、下の[ブラウザの設定が不足しています]ボタンを開くことができます。 [ブラウザの外部で実行する場合は昇格された信頼が必要]チェックボックスが付いたその他のオプションダイアログ。私はそれをテストしていませんが、そのオプションは確かにブラウザ内で高い信頼を得られないように聞こえます-したがって、コードのセキュリティエラーをチェックし、発生した場合は低い信頼の状況を処理することはおそらく理にかなっています。

この設定を有効にすると、通常のストリームを使用してユーザーのライブラリ内のファイルにアクセスできるようになりますが、ファイルシステムの他の場所ではアクセスできなくなります。デフォルトではOpenFileDialogSaveFileDialogクラスは質問に示されているようにストリームを返しますが、どちらも必要に応じてストリームではなくファイル名にアクセスできます。ファイルを開くと、ファイル名は下に隠されます

myOpenFileDialog.File.FullName

そして節約のためにあなたは使うことができるように見えます

mySaveFileDialog.SafeFileName

代わりは。

したがって、次のコードは、昇格されたOut-of-Browserアプリで機能します。

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);
        }
    }
}

そして、それはあなたにファイルアクセスとバックグラウンドでファイルを処理するための正しく更新されたプログレスバーの両方を与えます。

于 2011-07-16T18:35:49.913 に答える