2017-03-03 2 views
1

그래서 휴대용 클래스 라이브러리 (Xamarin)에서 만드는 UWP 응용 프로그램에서 작업하고 있습니다. 사용자가 XML 파일에 입력 한 정보 (예 : 텍스트 상자)를 저장해야합니다. 각 페이지에서너무 오래 걸리는 직렬화

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

는 "계속"버튼이 : 그 텍스트 상자에서 정보를 얻을 곳

그러므로 나는 PCL의 클래스를 만들었습니다. 파일을 클릭 마지막 단추에서

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

작성해야합니다 및 저장 : 클릭하면 데이터가 저장됩니다 dependencyService의 UWP 클래스 내 코드 (I 클래스를 만들어 여기에

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

라고 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

왜 "WriteStringToFileAsync (문자열들)"와 같은 인터페이스에 메서드를 추가 한 다음 모든 플랫폼에서 구현하지 않는다 즐길 수 있습니다. 따라서 두 클래스 사이에서 스트림을 전달할 필요가 없습니다. 이렇게하면 디버그가 훨씬 쉬워집니다. –

+0

@Malte 저는 프로그래밍에 익숙하지 않아, 당신의 제안이 제 이해를 초월 할 것이라고 생각합니다. 나는 당신이 의미하는 바를 이해하지 못하거나 그것을 구현할 방법을 이해하지 못합니다. 하지만 어쨌든 당신의 제안에 감사드립니다, 나는 그것을 감사드립니다! – RoloffM

+0

MakeFileStream 대신 IFileHelper 인터페이스에 "WriteStringToFileAsync (string s)"메서드를 추가하고 모든 플랫폼에서이 메서드를 구현 한 다음 (예 : Windows.Storage.FileIO.WriteTextAsync의 UWP) 데이터를 XML 문자열로 serialize합니다. 구현을 얻습니다. DependencyService에서 IFileHelper를 제거하고 문자열을 WriteStringToFile 메서드에 전달합니다. 이것이 내가 설명하려고했던 것을 이해하는 데 도움이되기를 바랍니다. –

답변

1

명령어는 MakeFileStreamAsync() 메서드 실행이 끝날 때까지 기본 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(); 
} 

희망 하시겠습니까?

편집 : 당신의 빈 XML 파일 문제에 관한 , 난 당신이 다른 페이지의 데이터를 저장할 수 있기 때문에 생각하지만 아무것도하지 않습니다. 따라서 Page2을로드 할 때 잃어 버릴 수 있습니다. 따라서 CreateandSave_Clicked 메서드에서는 사용할 수 없으며 현재는 빈 XMLData 개체를 저장합니다.

더 직관적 인 방법은 Page2 생성자에 데이터를 전달하고이 데이터 형식을 Page2의 공용 속성으로 추가하는 것입니다. 그것은 생성하고 XML 파일 자체를 저장하는 책임이있다 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; 
    } 
} 

다음 :

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 클래스/생성자가된다 : 그래서 당신의 Continue_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); 
    } 
} 

참고가 있다면 Page3 또는 XML 파일을 저장의 책임 Page, 그냥 페이지에서 페이지로 저장된 사용자 데이터를 전달 유지하고 CreateandSave_Clicked 메서드를 호출 할 때 한 번에 저장 뭐든간에.

다른 점은 클래스를 중첩하는 것이 유용하다는 확신이 없다는 것입니다. XMLData. XMLData 중첩 클래스를 제거하고 MyRootElement 클래스를 "기본"클래스로 유지하면됩니다.

정적 필드도 사용하여 생성자를 사용하지 않아도됩니다. 그러나 내 관점에서 볼 때, 직관적이지 못하며 또한 깨끗합니다.

+0

@ TaiT 's! 어떻게''기다리고 있니? '라고 제게 설명해 주시겠습니까? 전에 무엇을 추가해야합니까? "형식 또는 네임 스페이스 이름 ''... '오류가 발생하기 때문에 오류가 발생합니다. – RoloffM

+0

알았어. 이제 얼어 붙지 않아! 그것은 작동합니다! 정말 고맙습니다! 유일한 문제는 xml 파일이 비어 있다는 것입니다. (서식 파일은 있지만 내 요소는 없지만 어떤 생각입니까? – RoloffM

+1

도움이 되니 기쁩니다!자세한 내용을 보려면이 주제를 학습하는 동안 [이 Microsoft 페이지] (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

관련 문제