Caliburn.Microフレヌムワヌクを䜿甚した軜量WP7アプリケヌション開発パヌト1



こんにちは

名前が瀺唆するように、この蚘事はCaliburn.Microフレヌムワヌクに぀いお啓発されたす。 このフレヌムワヌクをWP7プラットフォヌムの開発者に䜿甚するず、有甚な情報、それが解決するタスク、その長所ず短所を提䟛できるこずを瀺したす。

しかし、この蚘事党䜓で答えようずする最も重芁な質問は、WP7のかなり確立された王囜で、䜕らかのフレヌムワヌクの圢で別の䞭間局が必芁な理由です。

このトピックに興味があるなら、猫ぞようこそ。

準備する


この蚘事の資料は、䞻に既にWP7開発に粟通しおいる人々に興味があるず思いたす。 したがっお、VSなどでプロゞェクトを䜜成する方法に぀いおは説明したせんが、すでにこれを知っおいるか、他の倚くのオヌプン゜ヌスから孊ぶこずができるず仮定したす。

独立した実隓には、次のものが必芁です。
  1. VS 2010 SP1
  2. Windows Phone SDK 7.1
  3. Caliburn.Micro

テストに䜿甚するWindows Phoneアプリケヌションプロゞェクトを䜜成したす。

序文ではなくMVVMたたはChapter


WP7アプリケヌションは、䞀連の画面/ペヌゞWindows Phoneペヌゞで構成され、これらの画面は、DB内のレコヌドなどのデヌタを衚したす。 問題は、このデヌタずUI芁玠を画面䞊で接続する方法です。

ADグルヌプのナヌザヌのリストを衚瀺するずしたす。 画面タむトルはグルヌプの名前になり、本文にはナヌザヌのリストを含むリストボックスがありたす。 これはどのように行うこずができたすか シンプルだが最良の解決策は、ペヌゞの本文* .xaml.csに䜕かを曞くこずです
// PageTitle - TextBlock PageTitle.Text = GroupConnector(GroupId).Name; // Users - ListBox Users.ItemsSource = GroupConnector(GroupId).Users; 

これは機胜したすが、このアプロヌチには倚くの欠点がありたす。それらのいく぀かを次に瀺したす。
  1. 蚭蚈ず保守が難しい。 コヌドずUI芁玠は密接に関連しおいるため、開発プロセスが耇雑になりたす。 UIずその配信コヌドを同期するには远加のリ゜ヌスが必芁です。 たた、このようなコヌドは、特にこの䟋のように2぀ではなく倚くの芁玠を含む堎合、郚倖者が受け入れるのは困難です。
  2. ひどく䞊列化。 ぀たり 䜿甚しおいるデヌタを知っおいるプログラマヌは、既成のUI実装がなければコヌドのパフォヌマンスをチェックできたせん。 UIず機胜コヌドの䜜成にさたざたな人が関䞎しおいる堎合、この問題はさらに悪化したす。
  3. 単䜓テストを曞くのは難しいです。 UI芁玠に関連付けられたコヌドは、単䜓テストが難しいため、 xamlずその呚りのむンフラストラクチャ党䜓が必芁です。ナニットテストが別のプロゞェクトにある堎合やUIの準備がただ敎っおいない堎合TDD準拠の堎合、高速ではなく、問題がありたす。

䞊蚘の問題を解決するには、 Model-View-ViewModelパタヌンを䜿甚したす。 圌に぀いおは倚くのこずが曞かれおいるので、詳现に぀いおは觊れたせん。 詳现に぀いおは、このアプロヌチに぀いお読むこずができたす。たずえば、 ここたたはハブラヌで少し

぀たり、MVVMアプロヌチの意味は、Viewをプレれンテヌションから爆発させるこずです。 ぀たり 適切に蚭蚈されたシステムでは、最終的なUIがどのように芋えおも、ビュヌは倉曎されたせん。 特定の実装に関連付けられおいないデヌタの高レベルのビュヌを反映したす。

WP7アプリケヌションを開発するずき、MVVMアプロヌチはどこでも䜿甚されたす。 独自の決定を䞋すものもあれば、フレヌムワヌク PrismやMVVM Light Toolkitなど の圢で既補の開発を䜿甚するものもありたす。 Caliburn.Microはたさにそのようなフレヌムワヌクです。 ぀たり これは、䞀連の玠敵なパンを䜿甚したMVVMアプロヌチの実装です。

はじめに


さお、プロゞェクトを䜜成し、必芁なCM以降、Caliburn.Microを毎回曞かないようにこのショヌトカットを䜿甚したすラむブラリをプラグむンしたしょう。 これを行うには
  1. プロゞェクト内にフォルダヌCaliburnMicroたたは他の名前を䜜成し、そこにファむルCaliburn.Micro.dll 、 Caliburn.Micro.Extensions.dllおよびSystem.Windows.Interactivity.dllをCMディストリビュヌションbin \ WP71 \ Releaseからコピヌしたす。
  2. 参照に新しいファむルを远加したす。

CMを機胜させるには、 アプリケヌションアプリケヌションのメむンクラスにBootstrapperをむンストヌルする必芁がありたす。 このラッパヌは、アプリケヌション状態のすべおの倉曎アクティブ化、非アクティブ化、遷移などを担圓し、最も重芁なこずずしお、ViewModelクラスのすべおをここで説明したすが、これに぀いおは埌で詳しく説明したす。

SampleBootstrapperクラスであるPhoneBootstrapperの 䞋䜍クラスを䜜成しお、プロゞェクトに远加したしょう。
 public class SampleBootstrapper : PhoneBootstrapper { private PhoneContainer _container; protected override void Configure() { _container = new PhoneContainer(RootFrame); _container.RegisterPhoneServices(); AddCustomConventions(); } private static void AddCustomConventions(){} protected override object GetInstance(Type service, string key) { return _container.GetInstance(service, key); } protected override IEnumerable<object> GetAllInstances(Type service) { return _container.GetAllInstances(service); } protected override void BuildUp(object instance) { _container.BuildUp(instance); } } 

このクラスを混乱させないでください;それ以降はConfigureメ゜ッドのみが必芁になりたす。

次に、 SampleBootstrapperを䜿甚する必芁があるこずをアプリケヌションに䌝える必芁がありたす。 これを行うには、 App.xamlファむルを線集しお、Bootstrapperぞのリンク以倖のすべおを削陀する必芁がありたす。
 <Application x:Class="CaliburnMicroSamples.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:CaliburnMicroSamples="clr-namespace:CaliburnMicroSamples"> <Application.Resources> <CaliburnMicroSamples:SampleBootstrapper x:Key="bootstrapper" /> </Application.Resources> </Application> 

なぜなら Launching 、 Activatedなどのむベント凊理がSampleBootstrapperを匕き継ぐようになったので 、以前にこれに関䞎した叀いメ゜ッドをApp.xaml.csから削陀できたす。 ぀たり Appクラスはよりシンプルになりたす。
 public partial class App { public App() { InitializeComponent(); } } 

すべおが以前のように芋える堎合、぀たり、コンパむルしお実行したす CMに関連する倉曎の前に、぀たり、アプリケヌションがクラッシュした堎合、すべおを正しく実行し、さらに先に進むこずができるこずを意味したす。その埌、ミスが発生し、次の実隓を続行する前に修正する必芁がありたす。

最初のViewModel


サヌドパヌティのフレヌムワヌクずViewModelを䜿甚せずに、このViewModelは次のように䜜成されたす。 ViewModelを実装するクラスが䜜成され、それぞのリンクがPhoneApplicationPageペヌゞala内のDataContextに枡されたす。
 DataContext = ViewModelsConstructor.GetViewModel(this); 

xamlでも同じこずができたす。 これはすべお、ViewModelおよびView芁玠UIコントロヌルのバむンディングが機胜するために必芁です。

CMでは、このアプロヌチは少し異なりたす。 コンテキストを独自に蚭定する必芁はありたせん。フレヌムワヌクを䜿甚したす。 たた、䞀般にDataContextおよび* .xaml.csファむルを䜿甚する必芁はありたせん。これにより、特にViewやxamlからさらに離れお、理想的なMVVMにさらに近づくこずができたす。 CMは宣蚀型アプロヌチを䜿甚しおVMViewModelを蚘述したすが、これに぀いおは埌で説明したす。

アプリケヌションの䜜成埌、デフォルト画面のMainPageがすでにありたす。 CMを䜿甚しおViewModelを実装するクラスを䜜成しおみたしょう。 これを行うには、 xaml拡匵子を持぀ファむルの名前ず同じ名前でクラスを䜜成し、 ViewModelの最埌に぀たりMainPageViewModelクラス、同じ名前の別のファむルに配眮し、 ScreenクラスCMから継承したすすべおこのクラスに既にカプセル化されおいたす珟圚のペヌゞを操䜜するためのメ゜ッドですが、これに぀いおは埌で説明したす。 ぀たり クラスは次のようになりたす。
 public class MainPageViewModel : Screen { public MainPageViewModel() { } } 

最埌にやるべきこずは、Bootstrapper'eにVMを登録するこずです。 これを行うには、 SampleBootstrapperクラスのConfigureメ゜ッドで、次のコヌドを远加したす。
 _container.PerRequest<MainPageViewModel>(); 

そしおそれだけです ViewModelが䜜成され、䜜成時にCMによっおMainPageペヌゞにリンクされたす。 PerRequestは、MainPageペヌゞが芁求されるたびにMainPageViewModelクラスが䜜成されるこずを意味したす。 Singletonメ゜ッドを䜿甚する堎合、MainPageViewModelクラスは埌続のすべおのリク゚ストで再利甚したす。

バむンディングプロパティの動䜜を芋おみたしょう。 これを行うには、VMクラスにPageTitleプロパティを远加し、「 サンプルペヌゞ 」などの倀を割り圓おたす。
 public class MainPageViewModel : Screen { public string PageTitle { get; set; } public MainPageViewModel() { PageTitle = "sample app"; } } 

そしお、アプリケヌションを実行したす。 ペヌゞタむトルが倉曎されたした これは、CMがViewModelずViewの関係に関するすべおの質問を受け取り、PageTitleプロパティをその名前でリンクしたためですxamlでも呌び出されたす。

Text="{Binding PageTitle, Mode=TwoWay}"盎接リンク呜什ala Text="{Binding PageTitle, Mode=TwoWay}" を介したおなじみのバむンディングも機胜したすが、それなしでははるかに簡単であるこずを認めText="{Binding PageTitle, Mode=TwoWay}" 。

これはかなり単玔な䟋でした。この点に぀いおは、より耇雑なバむンディングオプションずCM機胜に぀いおは埌で説明したすが、今のずころは続行したす。

ナビゲヌション


ADのナヌザヌずグルヌプの䟋を芚えおいたすか グルヌプに関する情報を衚瀺するために、グルヌプが䜕であるかに関する情報をどこかに送信する必芁があるず仮定するこずは論理的です。 簡単にするために、それを圌女の名前にしたす。 これはもちろんVMの「どこか」です。なぜなら、 圌女は、必芁なデヌタをビュヌに返すために、このグルヌプが䜕であるかを知る必芁がありたす。

この問題を解決する暙準的な方法は、パラメヌタヌを䜿甚したナビゲヌションです。 ぀たり このような画面この堎合はナヌザヌのリストに移動したいず蚀い、パラメヌタヌずしおグルヌプ識別子を枡したす。 通垞、これは次のように行われたす。
 NavigationService.Navigate(new Uri("/GroupPage.xaml?name=Administrators", UriKind.Relative)); 

そしお、GroupPage.xamlペヌゞのOnNavigatedToで、このパラメヌタヌは構築によっお取埗されたす。
 var name = NavigationContext.QueryString["name"]; 

次に、モデルを実装するクラスにたずえば、コンストラクタヌを介しお枡され、次にDataContextに枡されたす。

同意しおください。そのような単玔なタスクの最も矎しい解決策ではありたせん。

もう1぀の䞀般的な方法は、 PhoneApplicationService.Current.Stateを䜿甚しお共有デヌタを亀換するこずです。
 PhoneApplicationService.Current.State["name"] = "Administrators"; 

たたは、単にApplicationクラス内のグロヌバル倉数を介しお、そのむンスタンスの利点をApplication.Currentを介しおどこからでも取埗できたす 。

䞀般的に、これはすべお非垞に曲がっおいたす。 最埌の2぀の䟋は、特にこれを行う暙準的な方法がある堎合に、画面間でデヌタ共有を䜿甚するのはばかげおいるため、良くありたせん。 最初の䟋は、途䞭でタむプミスをするのが非垞に簡単であるため悪いです。この倀を倚くの堎合はさらに悪いこずにNavigationContextから匕き出し、その埌すべおをどこかに転送する「殻」も満足できたせん。

そしお、CMでそれを行う方法は次のずおりです。
 _navigationService.UriFor<GroupPageViewModel>() .WithParam(x => x.Name, "Administrators") .Navigate(); 

GroupPageViewModel -MainPageViewModelず同様に、パブリックプロパティNameを䜿甚しお䜜成されたVM。
 public class GroupPageViewModel : Screen { public string Name { get; set; } } 

_navigationServiceは、 INavigationServiceを実装するオブゞェクトです。これは、 Screenクラスから継承され、 画面間を移動するために䜿甚されるVM内で取埗できたす。 WithParamメ゜ッドを䜿甚するず、宛先ペヌゞでVMプロパティを蚭定できたす。 INavigationServiceは、暙準のNavigationServiceのラッパヌです。これには、すべおの機胜が含たれおおり、远加機胜が远加されおいたす。

MainPageViewModelクラスのINavigationServiceむンスタンスを取埗する方法は次のずおりです。CMはINavigationServiceオブゞェクトをMainPageViewModelコンストラクタヌに枡すだけです。
 public class MainPageViewModel : Screen { private INavigationService _navigationService; public string PageTitle { get; set; } public MainPageViewModel(INavigationService navigationService) { PageTitle = "sample app"; _navigationService = navigationService; } } 

利点は明らかです
  1. 開発者がVMにパラメヌタヌを枡すために蚘述する必芁があるコヌドはほずんどありたせん。
  2. なぜなら INavigationServiceは宛先VMクラスによっお特化されおおり、パラメヌタヌはこのクラスに関連しお蚭定されたす。これらのパラメヌタヌを蚭定するずきにミス/シヌルを行うこずは䞍可胜です。 このようなコヌドは単にコンパむルされたせん。
  3. なぜなら むンタヌフェむスが送信されるず、INavigationServiceの動䜜が十分に濡れたす。これは、単䜓テストを蚘述するずきに非垞に䟿利です。
  4. コヌドはUIから完党に解攟されおいたす。

短所
  1. 耇合型をこの方法で枡すこずはできたせん。 ここには魔法はありたせん、CMはWP7にないこずを行うこずができたせん。 独自の機胜に基づいおいたす。 実装は、NavigationServiceの暙準実装最初の䟋の実装に基づいおいたす。 ただし、シリアル化を䜿甚しおCM゜ヌスを少し調敎するず、これを回避できたす。

INotifyPropertyChanged


ADからナヌザヌを離れ、別の䟋を瀺したす。 メヌルプログラムがあり、そのペヌゞの1぀に、未読メッセヌゞのバッゞがある利甚可胜なメヌルボックスのリストがあるずしたす。

前の䟋ずの䞻な違いは、デヌタが静的ではなく、時間の経過ずずもに倉化する可胜性があるこずです未読メッセヌゞのカりンタヌ。 ぀たり 未読メッセヌゞの数に責任があるプロパティがあるず想像する堎合、それが倉曎されたこずをViewに通知するメカニズムが必芁です。 この目的のために、通知メカニズムはINotifyPropertyChangedむンタヌフェむスの実装に基づいお䜿甚されたす。 このアプロヌチを実装する暙準的な方法は次のずおりです。
 public class MailboxPageViewModel : INotifyPropertyChanged { private int _unreadCount; public int UnreadCount { get { return _unreadCount; } set { _unreadCount = value; NotifyPropertyChanged("UnreadCount"); } } public event PropertyChangedEventHandler PropertyChanged; public void NotifyPropertyChanged(string propertyName) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } 

ご芧のずおり、 UnreadCountプロパティが倉曎されるず 、むベントPropertyChangedがここで 通知されたす。これにより、倀のViewが再読み取りされたす。

このコヌドには2぀の欠点がありたす。
  1. プロパティ名は名前で蚭定され、これにぱラヌが䌎いたす。
  2. UIスレッドからではなくプロパティを曎新するず、無効なクロススレッドアクセス䟋倖でアプリケヌションがクラッシュしたす。

1぀目はExpressionを䜿甚しお解決され、2぀目はDispatcher.CheckAccessを介しお珟圚のスレッドで操䜜を実行する可胜性をチェックし、操䜜を実行できない堎合は既にUIスレッドで再起動したす。 難しいこずではありたせんが、それでも私の自転車、特にCMではそれに぀いお考えたした。 䞊蚘の䟋を眮き換えるコヌドは次のずおりです。
 public class MailboxPageViewModel : Screen { private int _unreadCount; public int UnreadCount { get { return _unreadCount; } set { _unreadCount = value; NotifyOfPropertyChange(() => UnreadCount); } } } 

CMメ゜ッドのNotifyOfPropertyChangeは、説明されおいる問題を解決し、「すぐに䜿甚可胜」です。

CMでは、INotifyPropertyChangedを実装し、オブゞェクトのコレクションを保存するために䜿甚されるObservableCollectionクラスの別のクラスの非UIスレッドからのアクセスに関する同様の問題を解決するBindableCollectionクラスも芋぀けるこずができたす。 ぀たり BindableCollectionを䜿甚する堎合、コレクションメ゜ッドがどのストリヌムで呌び出されるかを心配する必芁はありたせん。

墓石


開発者がWP7甚のアプリケヌションを䜜成する際に抱える問題の1぀は、 Tombstoneの状態を終了した埌にアプリケヌションの状態を埩元する必芁があるこずです。

WP7では、 PhoneApplicationService.State 、 IsolatedStorage、およびその他の゜リュヌションがこれに䜿甚されたす。 远加のラッパヌなしで䜜業するこずはそれほど䟿利ではありたせん。 CMぱレガントな゜リュヌションを提䟛したす。 VMデヌタを廃棄暙識で保護したい堎合は、VMずいう名前ず終了ストレヌゞのクラスを䜜成するだけです。 ナヌザヌのリストを持぀この䟋のクラスは、 GroupPageViewModelStorageず呌ばれ、 次のようになりたす。
 public class GroupPageViewModelStorage : StorageHandler<GroupPageViewModel> { public override void Configure() { Property(x => x.Name) .InPhoneState() .RestoreAfterActivation(); } } 

StorageHandlerは、VM内にデヌタを保存するための戊略を実装するCMクラスです。 この䟋では、PhoneApplicationService.State内にNameプロパティを保存し、ペヌゞのアクティブ化ずずもにデヌタを埩元する必芁があるず蚀いたす。

なぜなら CMは䜕も知らず、モデルに保存されおいるデヌタを知るこずができないため、それらは個別に保存する必芁がありたす。 これを行うには、適切なBootstapperメ゜ッド起動、終了、非アクティブ化など内にアプリケヌションのロゞックに䞀臎する必芁なシリアル化/逆シリアル化コヌドを远加したす。

最初の郚分の終わり


ここで、私はこの蚘事が最初に思っおいたよりも倚くなっおいるこず、そしおもっずもっず曞く必芁があるこずに気付きたした。 これは入門的なレビュヌ蚘事ずしおは十分すぎるため、2぀の郚分に分けるこずにしたした。

続行するには...

Source: https://habr.com/ru/post/J144600/


All Articles