2017-03-03 19 views
1

私はポータブルクラスライブラリ(Xamarin)で作成しているUWPアプリケーションを開発中です。ユーザーがXMLファイルに入力した情報(TextBoxなど)を保存する必要があります。シリアライズが長すぎます

ので、私はそれらのTextBoxから情報を取得PCLでクラスを作成しました:「継続」ボタンがあり、各ページの

namespace myProject 
{ 
    public class XMLData 
    { 
     [XmlRoot("MyRootElement")] 
     public class MyRootElement 
     { 
      [XmlAttribute("MyAttribute1")] //name of the xml element 
      public string MyAttribute1  //name of a textboxt e.g. 
      { 
       get; 
       set; 
      } 
      [XmlAttribute("MyAttribute2")] 
      public string MyAttribute2 
      { 
       get; 
       set; 
      } 
      [XmlElement("MyElement1")] 
      public string MyElement1 
      { 
       get; 
       set; 
      } 
    } 
} 

を。クリックすると、データが保存されます:最後のボタンで

async void Continue_Clicked(object sender, EventArgs e) 
     { 
      await Navigation.PushAsync(new Page2()); 
      XMLData.MyRootElement mre = new XMLData.MyRootElement 
      { 
       MyAttribute1 = editor1.ToString(), 
       MyAttribute2 = editor2.ToString(), 
       MyElement1 = editor3.ToString() 
      }; 
     } 

をファイルをクリックして作成して保存する必要があります。

private void CreateandSave_Clicked(object sender, EventArgs e) 
     { 
      var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL) 
      XMLData xmldat = new XMLData(); 
      using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8)) 
      { 
       XmlSerializer serializer = new XmlSerializer(typeof(XMLData)); 
       serializer.Serialize(sw, xmldat); 
      } 
     } 

ここUWPクラスの私のコード(dependencyServiceのために私はと呼ばれるクラスを作成しましたストリームを取得し、保存場所+ファイルを作成するためのFileHelper)

namespace myProject.UWP 
{ 
    public class FileHelper: IFileHelper //IFileHelper is a simple interface I made with the Stream MakeFileStream(); method in it 
    { 
     public async Task<IRandomAccessStream> MakeFileStreamAsync() 
     { 
      StorageFolder sf = KnownFolders.DocumentsLibrary; 

      var file = await sf.CreateFileAsync("data.xml", CreationCollisionOption.OpenIfExists); 
      using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite)) 
      { 
       return stream; 
      } 
     } 
     Stream IFileHelper.MakeFileStream() 
     { 
      var task = MakeFileStreamAsync(); 
      task.Wait(); 
      return task.Result.AsStreamForWrite(); 
     } 
    } 
} 

問題は、私はCreateandSaveボタンに到達し、それをクリックすると、アプリがちょうどフリーズということです。エラーはありません。何も表示されません。私はデバッグを分解した後、私は望んでいるフォルダにXMLファイルが作成されているのを見ることができますが、空(0バイト)です。コードの何が間違っていますか?誰かアイデア?コードのこの部分で

+1

をお楽しみください。したがって、2つのクラスの間でストリームを渡す必要はありません。これにより、デバッグが非常に簡単になります。 –

+0

@Malte私はプログラミングには新しく、あなたの提案が私の理解を超えていると思います。私はあなたが何を意味しているのか、それをどのように実装するのかをよく理解していません。とにかくあなたの提案に感謝します、私はそれを感謝します! – RoloffM

+0

MakeFileStreamの代わりにIFileHelperインターフェイスにメソッド "WriteStringToFileAsync(string s)"を追加し、このメソッドをすべてのプラットフォーム(Windows.Storage.FileIO.WriteTextAsyncのUWPなど)で実装し、データをXML文字列にシリアル化して実装を取得しますDependencyServiceからIFileHelperを取得し、その文字列をWriteStringToFileメソッドに渡します。 これは私が説明しようとしていたことを理解するのに役立ちます。 –

答えて

1

MakeFileStreamAsync()メソッドの実行が完了するまで、あなたの凍結問題はTask.Wait()命令ブロックすることをメインUIスレッドです。

あなたはこの方法asyncを作り、Task<Stream>型を返す、とのawaitキーワードを使用してMakeFileStreamAsyncメソッドを呼び出す必要があります。

private void CreateandSave_Clicked(object sender, EventArgs e) 
{ 
    var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL) 
    XMLData xmldat = new XMLData(); 

    // Here you should await your `s` Task: 
    await s; 

    using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8)) 
    { 
     XmlSerializer serializer = new XmlSerializer(typeof(XMLData)); 
     serializer.Serialize(sw, xmldat); 
    } 
} 

async Task<Stream> IFileHelper.MakeFileStream() 
{ 
    var stream = await MakeFileStreamAsync(); 
    return stream.AsStreamForWrite(); 
} 

したがって、作成クリックのためのあなたのコードのようなものでなければなりません

希望すると助かります!

EDIT: あなたの空のXMLファイルの問題については、私はあなたが別のページにデータを保存するので、それはだと思うが、それを何もしません。つまり、Page2をロードするときにそれらを失うことを意味します。したがって、CreateandSave_Clickedメソッドでは使用できず、現在は空のXMLDataオブジェクトを保存しています。

より直観的な方法は、データをPage2コンストラクタに渡し、このデータ型をPage2のパブリックプロパティとして追加することです。だからあなたのContinue_Clicked方法は次のようになります。

async void Continue_Clicked(object sender, EventArgs e) 
{ 
    // Note you must REVERSE instructions here 
    // Create first your object (save the user data in it) 
    XMLData.MyRootElement mre = new XMLData.MyRootElement 
    { 
     MyAttribute1 = editor1.ToString(), 
     MyAttribute2 = editor2.ToString(), 
     MyElement1 = editor3.ToString() 
    }; 
    // Pass it to Page2 through the constructor 
    await Navigation.PushAsync(new Page2(mre)); 
} 

そしてそうPage2クラス/コンストラクタは次のようになります。

その後
public class Page2 : SomeParentClass 
{ 
    ... 
    // add your XMLData property 
    public XMLData.MyRootElement mre { get; set; } 
    ... 
    // the constructor 
    public Page2(XMLData.MyRootElement data){ 
     // Save the user data in xmldat property. So this data could be reused later. 
     this.mre = data; 
    } 
} 

、それはあなたができる、XMLファイル自体を作成し、保存する責任があることPage2だ場合コンストラクタを通過したオブジェクトを再利用します。それは、Page3だか、XMLファイルを保存する責任があることどんなPage場合だけでページからページへ保存されたユーザデータを渡しておくと、あなたがCreateandSave_Clickedメソッドを呼び出すときに、一度にそれらを保存することを

private void CreateandSave_Clicked(object sender, EventArgs e) 
{ 
    var s = DependencyService.Get<IFileHelper>().MakeFileStream();//using dependencyService to get a stream (there is no system.io.stream in PCL) 
    // You want to remove that here as you created a public property of type XMLData.MyRootElement (called mre) holding user data instead 
    //XMLData xmldat = new XMLData(); 

    // Here you should await your `s` Task: 
    await s; 

    using (StreamWriter sw = new StreamWriter(s, Encoding.UTF8)) 
    { 
     // Change of serialized type here 
     XmlSerializer serializer = new XmlSerializer(typeof(XMLData.MyRootElement)); 
     // Here, just seralize the property saved through constructor 
     serializer.Serialize(sw, mre); 
    } 
} 

注意。

もう1つのポイントは、クラスをネストすることの有用性が、XMLDataに分かれていないことです。 XMLDataネストクラスを削除して、MyRootElementクラスを「メイン」クラスとして保つことができます。

同様に、静的フィールドを使用してコンストラクタを使用しないこともできます。しかし私の視点から見ると、それはあまり直感的ではなく、しかもそれほど清潔ではありません。

は、あなたが「WriteStringToFileAsync(文字列s)」のようなインターフェースにメソッドを追加して、すべてのプラットフォーム上でそれを実装していないのはなぜ

+0

ありがとう@太極拳!あなたは私に「待っている」ことを説明することができますか?前に追加する必要があるのは何ですか? "型または名前空間の名前 'が' ...を待つので、エラーが発生します。 – RoloffM

+0

大丈夫私は今それを得て凍っていない!できます!どうもありがとうございます!唯一の問題は、xmlファイルが空であることです:(テンプレートはそこにありますが、私の要素、考え方はありませんか? – RoloffM

+1

助けてくれてうれしいです!詳細については、私は[このマイクロソフトのページ](https://msdn.microsoft.com/en-us/library/mt674882.aspx)がこのトピックを学ぶ際に非常に役立つことを発見しました。非同期/待機は本当に分かりやすいが、習得するのは難しい)。 **非同期メソッドで何が起こるか**のセクションのスキーマを特に見てください。 あなたの空のファイルに関して、私は時間がなくなったので、後で見るかもしれません:) –

2

using (var stream = await file.OpenAsync(FileAccessMode.ReadWrite)) 
{ 
    return stream; 
} 

あなたはusingブロックで作成されたインスタンスを返すされています。返却前に処分され、その結果、処分されたオブジェクトが返されます。

return streamに変更してください。 StreamWriter使用はusingブロック自体にあるので、その処分it will dispose the underlying stream中に:StreamWriter.Disposeが呼び出されたときに

StreamWriterオブジェクトが提供StreamオブジェクトにDispose()を呼び出します。

+0

これは、 '使用する権利 'の外に書かなければならないのですか?@BartoszKP – RoloffM

+0

@RoloffMはい – BartoszKP

+0

ありがとうございました!コードを変更しましたが、まだ問題は残っています。エラーはありませんが、アプリケーションはただフリーズします... – RoloffM

関連する問題