質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

Q&A

解決済

3回答

4939閲覧

JsonデータをMVVMで表示

randr

総合スコア202

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

JSON

JSON(JavaScript Object Notation)は軽量なデータ記述言語の1つである。構文はJavaScriptをベースとしていますが、JavaScriptに限定されたものではなく、様々なソフトウェアやプログラミング言語間におけるデータの受け渡しが行えるように設計されています。

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

Xamarin

Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

1グッド

0クリップ

投稿2016/10/30 02:07

編集2016/10/30 02:52

やりたいこと

サーバよりJsonでデータを取得してMVVMで画面に表示したい。

Jsonデータは以下の通りです。

  • Code(データ取得状態コード) ※アプリ内共通
  • Message(エラーメッセージ) ※アプリ内共通
  • Data(詳細データ)※アプリ内各所個別

そのため、以下のようにクラスを作成

C#

1 public class JsonData<T> 2 { 3 public int Code 4 { 5 get; set; 6 } 7 public string Message 8 { 9 get; set; 10 } 11 public T Data 12 { 13 get; set; 14 } 15 } 16 public class OrgData: BindableBase 17 { 18 int _ID; 19 public int ID 20 { 21 get 22 { 23 return _ID; 24 } 25 set 26 { 27 SetProperty(ref _ID, value); 28 } 29 } 30 string _Text; 31 public string Text 32 { 33 get 34 { 35 return _Text; 36 } 37 set 38 { 39 SetProperty(ref _Text, value); 40 } 41 } 42 } 43 public abstract class BindableBase :INotifyPropertyChanged 44 { 45 public event PropertyChangedEventHandler PropertyChanged; 46 protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null) 47 { 48 if(object.Equals(storage, value)) 49 return false; 50 51 storage = value; 52 this.OnPropertyChanged(propertyName); 53 return true; 54 } 55 protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 56 { 57 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 58 } 59 }

作った環境

画面側は以下の通りです。
今回はテストのためEntryに入力したJson文字列をMVVMで画面するものを作成しています。

XAML

1<?xml version="1.0" encoding="utf-8" ?> 2<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 3 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 4 x:Class="App1.Page1"> 5 <StackLayout x:Name="stackMain"> 6 <Entry x:Name="txtData" /> 7 <Label Text="{Binding ID}" /> 8 <Label Text="{Binding Text}" /> 9 <Button x:Name="btn1" 10 Text="GetData" /> 11 </StackLayout> 12</ContentPage>

C#

1using System; 2using Xamarin.Forms; 3using Newtonsoft.Json; 4 5namespace App1 6{ 7 public partial class Page1 :ContentPage 8 { 9 public Page1() 10 { 11 InitializeComponent(); 12 var _vm = new JsonData<OrgData> { Data = new OrgData() }; 13 stackMain.BindingContext = _vm.Data; 14 this.btn1.Clicked += (sender, e) => 15 { 16 try 17 { 18 var ret = txtData.Text; 19 _vm = JsonConvert.DeserializeObject<JsonData<OrgData>>(ret); 20 } 21 catch(Exception ex) 22 { 23 DisplayAlert("error", ex.Message, "OK"); 24 } 25 }; 26 } 27 } 28}

困っていること

_vm = JsonConvert.DeserializeObject<JsonData<OrgData>>(ret); の場所で、新しくオブジェクトが生成されるためか、PropertyChanged がnullとなり画面に反映されませんでした。
ちなみに、JsonConvertをせず、一つ一つプロパティをセットすると問題なく反映されました。

考えてみたこと

  1. Type.GetPropertiesで回してコード上簡潔に1つずつプロパティをセットしようと考えましたが、Xamarin上?ではGetPropertiesはアクセスできず、できませんでした。

  2. もう一つ上にViewModelを作成して反映させる

C#

1 public class BindingData:BindableBase 2 { 3 OrgData _data; 4 public OrgData Data 5 { 6 get 7 { 8 return _data; 9 } 10 set 11 { 12 SetProperty(ref _data, value); 13 } 14 } 15 }

こちらもPropertyChangedがnullになりだめでした。

ご回答いただきたいこと

そのままですが、サーバからデータを取得してMVVMで表示したいと思ったときに、皆様はどのような方法をとられていますか?
項目数が多い場合も考慮して、一つ一つプロパティをセットすることは避けたいです。

amay077👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

P3PPP

2016/10/30 10:42

PCLの場合は `GetProperties` の代わりに `GetRuntimeProperties()` が支えます。
guest

回答3

0

ちょっとプログラムを簡略化しましたが、以下で動作しました。

xml

1<?xml version="1.0" encoding="utf-8" ?> 2<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 3 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 4 x:Class="XFApp1.Page1"> 5 <StackLayout> 6 <Label Text="{Binding Data.ID}" /> 7 <Label Text="{Binding Data.Text}" /> 8 <Button x:Name="btn1" Text="GetData" /> 9 </StackLayout> 10</ContentPage> 11

csharp

1using System; 2using System.ComponentModel; 3using System.Runtime.CompilerServices; 4using Xamarin.Forms; 5 6namespace XFApp1 7{ 8 public partial class Page1 : ContentPage 9 { 10 private int _count; 11 12 public Page1() 13 { 14 InitializeComponent(); 15 var _vm = new TestViewModel {Data = new DataModel()}; 16 BindingContext = _vm; 17 btn1.Clicked += (sender, e) => 18 { 19 try 20 { 21 var data = new DataModel 22 { 23 ID = ++_count, 24 Text = $"{_count} 回目のクリック" 25 }; 26 (BindingContext as TestViewModel).Data = data; 27 } 28 catch (Exception ex) 29 { 30 DisplayAlert("error", ex.Message, "OK"); 31 } 32 }; 33 } 34 } 35 36 public class TestViewModel : BindableBase 37 { 38 private DataModel _data; 39 40 public DataModel Data 41 { 42 get { return _data; } 43 set { SetProperty(ref _data, value); } 44 } 45 } 46 47 public class DataModel 48 { 49 public int ID { get; set; } 50 public string Text { get; set; } 51 } 52 53 public abstract class BindableBase : INotifyPropertyChanged 54 { 55 public event PropertyChangedEventHandler PropertyChanged; 56 57 protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) 58 { 59 if (Equals(storage, value)) 60 return false; 61 62 storage = value; 63 OnPropertyChanged(propertyName); 64 return true; 65 } 66 67 protected void OnPropertyChanged([CallerMemberName] string propertyName = null) 68 { 69 PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 70 } 71 } 72}

考えてみたこと2のもう一つ上にViewModelを作るパターンでプログラムを行っています。
そしてそのViewModel構造に対応するためにXamlのBindingのPathをID -> Data.IDというような形に変更しています。
このサンプルではDataModelはBindableBaseを継承していませんが、BidableBaseを継承してプロパティ変更を通知するようにすれば、ID、Textの個別の変更にも対応できると思います。

投稿2016/10/30 04:37

yu_ka_san

総合スコア29

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

INotifyPropertyChangedは、インスタンスに対して変更を検知するので、新たなインスタンスを生成してしまうと、検知の対象にはならないです(のはず...)。

そのため、上述T-T-T-T-T-T-T-Tさんの記載のように、インスタンスのプロパティ(今回だと_vm.Data)にデータを渡さなければなりません。
(その際は、DataプロパティにもSetPropertyをつけます。)
もし、現状定義しているViewModelをそのまま(CodeやMessageプロパティも含め)データをバインドしたいのであれば、もう一こクラスをラップしてあげると実現できます(できなそうでしたら言っていただければサンプル書きます..)。

サンプルとして、Dataプロパティのみを渡すパターンで(プラスMVVMということでCommandも使ってみるとテスタビリティも上がってよいかと)を書いておきます。ボタンクリックでTextを渡して何かを処理する処理をCommandで実装しています。

XAML

1<?xml version="1.0" encoding="utf-8" ?> 2<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 3 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 4 x:Class="HelloWorld.Views.Page1"> 5 6 <StackLayout x:Name="stackMain"> 7 <Entry x:Name="txtData" /> 8 <Label Text="{Binding Data.ID}" /> 9 <Label Text="{Binding Data.Text}" /> 10 <Button x:Name="btn1" Text="GetData" Command="{Binding SomethingCommand}" CommandParameter="{Binding Data.Text}" /> 11 </StackLayout> 12 13</ContentPage>

cs

1public partial class Page1 : ContentPage 2{ 3 public Page1() 4 { 5 InitializeComponent(); 6 //サンプルに適当なデータを初期化... 7 var vm = new JsonData<OrgData> { Code = 1, Message = "init!", Data = new OrgData() { ID = 99, Text = "text!!!" } }; 8 9 stackMain.BindingContext = vm; 10 } 11}

cs

1 public class JsonData<T> : BindableBase 2 { 3 private T _data; 4 private int _count; 5 6 public int Code { get; set; } 7 8 public string Message { get; set; } 9 10 11 public T Data 12 { 13 get { return _data; } 14 set { SetProperty(ref _data, value); } 15 } 16 17 public JsonData() 18 { 19 SomethingCommand = new Command<string>( 20 execute: (inputText) => 21 { 22 _count++; //サンプルとしてカウントをアップしています。 23 Data = GetData(_count, inputText); 24 }); 25 } 26 27 public ICommand SomethingCommand { get; } 28 29 private static T GetData(int count, string text) 30 { 31 //この処理も動的に変更させたいのであれば、Page1クラスのViewModelをインスタンス化するタイミングでデリゲートを渡してあげればよいかと。 32 33 //なんらかのデータ処理でjsonを取得したと仮定したとします(とりあえずjsonを取得したと仮定) 34 var jsonStringFromPersistence = GetJsonString(count, text); 35 36 //Deserialize 37 return JsonConvert.DeserializeObject<T>(jsonStringFromPersistence); 38 } 39 40 private static string GetJsonString(int count, string text) 41 { 42 return JsonConvert.SerializeObject(new OrgData() 43 { 44 ID = count, 45 Text = $"{text}**{count}" 46 }); 47 }

投稿2016/11/01 07:14

BEACHSIDE

総合スコア294

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

c#

1try 2{ 3 var ret = txtData.Text; 4 _vm = JsonConvert.DeserializeObject<JsonData<OrgData>>(ret); 5} 6catch(Exception ex) 7{ 8 DisplayAlert("error", ex.Message, "OK"); 9}

これを↓のように書き換えてみては?

c#

1try 2{ 3 var ret = txtData.Text; 4 var temp = JsonConvert.DeserializeObject<JsonData<OrgData>>(ret); 5 _vm.Data = temp.Data; 6} 7catch(Exception ex) 8{ 9 DisplayAlert("error", ex.Message, "OK"); 10}

ご自身でも言われているように _vm のインスタンスが再生成されてしまう事が原因です。
そのため、_vmインスタンスを生成しなおさず、dataだけを取ってしまえばいいかと。

投稿2016/10/31 02:47

LaLaLand

総合スコア107

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問