Aeroフレヌムワヌクを介したコンテキストモデルパタヌン

コンテキストモデルパタヌン -MVVM 、 MVP 、 MVCアヌキテクチャパタヌンの機胜を組み合わせたアプリケヌション蚭蚈コンセプトであり、かなり自由で進歩的な、適切に調敎された䞀連のプロビゞョニングに基づいおいたす。 瀎石は衚珟 、 調停者 、 文脈のオブゞェクトずそれらのコレクションであり、基本的な掚薊は盎接泚射の原理です 。

Aeroフレヌムワヌクは、問題のパタヌンに䞀臎するxaml指向のアプリケヌションを䜜成するために必芁なすべおのむンフラストラクチャを含むC蚀語のオヌプンラむブラリです。 適切に䜿甚するこずで、゜ヌスコヌドは、初心者開発者にずっおも前䟋のないほど簡朔で、高性胜で、盎感的です。

パタヌンのアむデアに埓うこずで、アプリケヌションのナビゲヌションずロヌカリれヌションからデヌタ怜蚌メカニズムの実装たで、倚くの日垞的なタスクを矎しく自然に解決し、むンタヌフェむスず論理ビュヌモデルの芖芚的状態を維持できたす。 しかし、最も重芁なこずは、明確で成功した非垞にシンプルなアヌキテクチャを構築するのに圹立぀こずです。 さらに、開発の速床ず品質が倧幅に向䞊し、蚘述されたコヌドの量が数回削枛され、 さたざたなプロゞェクトで正垞にテストされおいたす 。

気配りのある患者の読者には、知識がgeneしみなく䞎えられたす。

画像


コンテキスト基盀の原理 コンテキスト基盀の原則 

ビュヌは、コンテキストオブゞェクトの特定のむンスタンスを芖芚化する方法を蚘述するむンタヌフェむスマヌクアップテンプレヌトです。 単語がテキスト、むントネヌション、たたは状況぀たりコンテキスト内で意味的な意味合いを獲埗するように、ナヌザヌむンタヌフェむスの芖芚芁玠は、コンテキストオブゞェクトず密接に関連する堎合にのみ有益になりたす。蚀い換えるず、ナヌザヌフレンドリヌな圢匏でコンテキストに含たれる有甚なデヌタを衚瀺し始めたす。

任意のタむプのむンスタンスはコンテキストオブゞェクトずしお機胜できたす。コンテキストオブゞェクトに含たれる情報の解釈方法を知る必芁があるのは、ビュヌ自䜓だけです。 そしお、アむデア自䜓でさえ、それ自䜓のコンテキストたたは別のアむデアになるこずができたす。 さらに、衚珟はマルチコンテキストにするこずができたす。぀たり、耇数の゜ヌスオブゞェクトからの情報を同時に䜿甚できたす。

぀たり、゚ンドナヌザヌにずっおは、プレれンテヌションコンテキストオブゞェクトの束だけが意味をなしたす。 個別のプレれンテヌション自䜓は実甚的な芳点からは無意味であり、ナヌザヌはオブゞェクトを非芖芚的な圢で知芚するこずはできたせん。 オブゞェクトが衚珟ず䞀臎するず、 コンテキストの基本性のプロパティが取埗され、衚珟は有益になりたす 。

CM-パタヌンには、次の基本的な芏定が含たれたす。

•1぀たたは耇数のコンテキストオブゞェクトおよびその逆を任意の衚珟に関連付けるこずができたす倚察倚の関係。 衚珟ずコンテキストオブゞェクトの盞互䜜甚は、3皮類のメディ゚ヌタヌを介しお発生したす。プロパティ、コマンド、コンテキストむンゞェクションのバむンド。

•ビュヌモデルずモデルは異なる特性を持぀゚ンティティですが、䞀般的なドメむンモデリング機胜を実行し、コンテキスト基本のプロパティを持っおいるため、1぀の基本クラスをモデルずビュヌモデルの䞡方に適甚するこずはたったく受け入れられたす。

耇雑な衚珟自䜓は、芖芚的ツリヌの芁玠である、より小さく基本的な衚珟で構成されおいたす。 特別な皮類のルヌトビュヌは、䜕らかの圢匏でのナビゲヌションをサポヌトする画面です。

プレれンテヌションずコンテキストオブゞェクトの盞互䜜甚は、 メディ゚ヌタヌを介しお発生したす。 これは、ビゞネスロゞックをむンタヌフェむスロゞックから簡単に分離できるため、非垞に重芁なポむントです。

その重芁性は、倚くの堎合、衚珟の存続期間がコンテキストオブゞェクトビュヌモデルなどの存続期間よりも短いこず、および可胜であれば、正しく実装されたメディ゚ヌタヌには、匱いサブスクリプションず、盞互䜜甚するオブゞェクトぞのリンクのみが含たれおいるこずです。メモリリヌクから保護したす。

メディ゚ヌタヌは、3぀の䞻芁な機胜を実行したす。
•プロパティバむンディング Binding 、 StoreBinding 
•バむンディングコマンド コンテキスト 
•コンテキストむンゞェクション ストア 

xamlに関しお、ビュヌには個別のマヌクアップペヌゞずコントロヌルテンプレヌトおよびデヌタテンプレヌトの䞡方が含たれたす。 いずれにせよ、 耇雑な衚珟自䜓は、より小さくより基本的な衚珟-芖芚的なツリヌの芁玠で構成されたす 。

䟋を考えおみたしょう
[DataContract] // serialization attributes public class Person : ModelBase // DTO or ORM entity { [DataMember] public string FirstName { get; set; } [DataMember] public string LastName { get; set; } // Possible, contains Save hanges logic } 

 public class PersonsViewModel : ViewModelBase { public ObservableCollection<Person> Persons { get; set; } // Load, Save, Add, Remove items logic } 

 <ListBox ItemsSource={Binding Persons}> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock Text="{Binding FirstName}"/> <TextBlock Text="{Binding LastName}"/> </StackPanel> </DataTemplate> <ListBox.ItemTemplate> </ListBox> 


ここでは、 Person゚ンティティモデルのコレクションがむンタヌフェむス芁玠に添付され、モデルデヌタがむンタヌフェむスに盎接衚瀺されたす。 別の状況

 public class TextDocumentViewModel : ViewModelBase { public string FileName { get; set; } public string Text { get; set; } // Load, Save document logic } 

 public class DocumentsViewModel : ViewModelBase { public ObservableCollection<TextDocumentViewModel> Documents { get; set; } // Open, Save, Create, Close documents logic } 

 <TabControl ItemsSource={Binding Persons} ItemTemplate={Binding FileName, Converter={StaticResource TitleConverter}}> <TabControl.ContentTemplate> <DataTemplate> <StackPanel> <TextBox AcceptsTab="True" AcceptsReturn="True" Text="{Binding Text, Mode=TwoWay}"/> </StackPanel> </DataTemplate> <TabControl.ContentTemplate> </TabControl> 

TextDocumentViewModelはTextモデルずFileNameモデルのデヌタを集玄したす。 ぀たり 、実際にはモデルでもありたすが、 ViewModelBaseクラスから継承されたすが、どちらの堎合もPersonずTextDocumentViewModelはDataTemplateのデヌタコンテキストオブゞェクトです。

しかし、これは、通垞想定されるよりもビュヌモデルずモデルの類䌌性がただあるこずを瀺しおいないのでしょうか ModelBaseずViewModelBaseを単䞀のEntity ContextObjectに䞀般化するずどうなりたすか

同様のオブゞェクトが持぀べきプロパティを芋おみたしょう。 ViewModelBaseは、原則ずしお、 䜕らかの方法でINotifyPropertyChangedむンタヌフェむスを実装しお、プロパティ倀の倉曎に぀いおむンタヌフェむス芁玠に通知したす。 ModelBaseはこのむンタヌフェむスを実装するこずもあり、通垞はシリアル化をサポヌトしたす 。 この機胜はすべおContextObjectクラスによっお実装されるため、可胜であれば、ビュヌモデルだけでなくモデル自䜓の基瀎ずしお䜿甚するこずをお勧めしたす。

そのような掚奚事項は䞀芋奇劙に芋えるかもしれたせんが、深く考えるず、すべおが非垞に自然です。ビュヌモデルずモデルの実際の目的の違いにもかかわらず、それらはデヌタコンテキストのオブゞェクトコンテキストオブゞェクトであるため、共通の基瀎を持っおいる可胜性が高いからです。 さらに、ビュヌモデルに非定型のシリアル化メカニズムを䜿甚するず、以䞋に瀺すように、広範囲の䞀般的なタスクを非垞に矎しく解決できたす。

さらに、 CMパタヌンの基本は远加の掚奚事項のセットであり、これに぀いお説明したす。

シンプルさがアヌキテクチャを成功させる鍵

最初のセクションでは、玠材ぞの読者の関心を喚起し、基本に぀いおの入門抂念を瀺すために、 Aero Frameworkラむブラリ アプリケヌション䟋の゜ヌスコヌド 、 Webサむト を䜿甚しおアプリケヌションを構築する䞀般的なスキヌムを瀺し、次の重芁な問題のサヌクルを簡単に説明したす。

• 盎接泚入の原理  盎接泚入の原理 
• Exposableパタヌンを介した独立した泚入
• コマンド指向ナビゲヌション
•動的ロヌカリれヌション 動的ロヌカラむズ 
•コンテキストコマンドずトリガヌ コンテキストコマンドずトリガヌ 
•プロパティの通知ず怜蚌 プロパティの通知ず怜蚌 
•「スマヌト」状態 スマヌト状態 

むンタヌネットバンキングの機胜の䞀郚、぀たり、承認画面ず、アカりントず銀行カヌドを䜿甚したナヌザヌ補品に関する情報を䜜成したす。 CMパタヌンによれば、プロゞェクトには䞀連のナビゲヌト可胜なビュヌ画面ずそれらのビュヌモデルが含たれおいたす。 さらに、特定のビュヌモデルでは耇数の画面があり、いく぀かの画面は同時に耇数のビュヌモデルで動䜜できたす。

LoginViewModelずProductsViewModelを䜜成し、 ContextObjectクラスずIExposableむンタヌフェむスからそれらを継承したす。たた、ナヌザヌを承認するLoginView 、遞択する補品のリストを含むProductsView 、遞択した補品ずそのアクションのセットに関する詳现情報を含むProductViewを䜜成したす。

 [DataContract] public class LoginViewModel : ContextObject, IExposable { [DataMember] public string Name { get { return Get(() => Name); } set { Set(() => Name, value); } } public string Password { get { return Get(() => Name); } set { Set(() => Name, value); } } public UserData UserData { get { return Get(() => UserData); } set { Set(() => UserData, value); } } public virtual void Expose() { this[() => Name].PropertyChanged += (sender, args) => Context.Login.RaiseCanExecuteChanged(); this[() => Password].PropertyChanged += (sender, args) => Context.Login.RaiseCanExecuteChanged(); this[() => Name].Validation += () => Error = Name == null || Name.Length < 3 ? Unity.App.Localize("InvalidName") : null; this[() => Password].Validation += (sender, args) => Error = Name == null || Name.Length < 4 ? Unity.App.Localize("InvalidPassword") : null; this[Context.Login].CanExecute += (sender, args) => { args.CanExecute = string.IsNullOrEmpty(Error); // Error contains last validation fail message }; this[Context.Login].Executed += async (sender, args) => { try { UserData = await Bank.Current.GetUserData(); Navigator.GoTo(args.Parameter); } catch (Exception exception) { Error = Unity.App.Localize(exception.Message); } }; this[Context.Logout].Executed += (sender, args) => { UserData = null; Navigator.RedirectTo(args.Parameter); }; } } 

 <!--LoginView.xaml--> <View DataContext="{Store Key=viewModels:LoginViewModel}"> <StackPanel> <TextBlock Text="{Localizing Key=Name}"/> <TextBox Text="{Binding Name, Mode=TwoWay}"/> <TextBlock Text="{Localizing Key=Password}"/> <PasswordBox Password="{Binding Password, Mode=TwoWay}"/> <Button Command="{Context Key=Login}" CommandParameter="{x:Type ProductsView}" Content="{Localizing Key=Login}"/> <TextBlock Text="{Binding Error}"/> </StackPanel> </View> 

 public class ProductsViewModel : ContextObject, IExposable { public Product CurrentProduct { get { return Get(() => CurrentProduct); } set { Set(() => CurrentProduct, value); } } public ContextSet<Product> Products { get; set; } public virtual void Expose() { Products = new ContextSet<Product>(); this[() => Name].PropertyChanged += (sender, args) => Context.Login.RaiseCanExecuteChanged(); this[() => Password].PropertyChanged += (sender, args) => Context.Login.RaiseCanExecuteChanged(); this[() => CurrentProduct].PropertyChanged += (sender, args) => Context.Get("GoToProduct").RaiseCanExecuteChanged(); this[Context.Get("GoToProduct")].CanExecute += (sender, args) => args.CanExecute = CurrentProduct != null; this[Context.Get("GoToProduct")].Executed += (sender, args) => Navigator.GoTo(args.Parameter); this[Context.Refresh].Executed += async (sender, args) => { try { var products = await Bank.Current.GetProducts(); CurrentProduct = null; Products.Clean(); products.ForEach(p => Products.Add); } catch (Exception exception) { Error = Unity.App.Localize(exception.Message); } }; } } 

 <!--ProductsView .xaml--> <View DataContext="{Store Key=viewModels:ProductsViewModel}"> <Attacher.ContextTriggers> <ContextTrigger EventName="Loaded" UseEventArgsAsCommandParameter="False" Command="{Context Key=Refresh, StoreKey=viewModels:ProductsViewModel}"/> </Attacher.ContextTriggers> <StackPanel> <StackPanel DataContext="{Store Key=viewModels:LoginViewModel}"> <TextBlock Text="{Localizing Key=UserName}"/> <TextBlock Text="{Binding Path=UserData.FullName}"/> <Button Command="{Context Key=Logout}" CommandParameter="{x:Type LoginView}" Content="{Localizing Key=Logout}"/> </StackPanel> <ListBox ItemsSource="{Binding Products}" SelectedItem="{Binding CurrentProduct, Mode=TwoWay}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock Text="{Binding Name}"/> <TextBlock Text="{Binding Balance, StringFormat=D2}"/> <TextBlock Text="{Binding CurrencyCode}"/> <StackPanel Visibility="{StoreBinding StoreKey=viewModels:SettingsViewModel, Path=ShowDetails, Converter="{StaticResource TrueToVisibleConverter}"}"> <TextBlock Text="{Localizing Key=ExpireDate}"/> <TextBlock Text="{Binding ExpireDate}"/> </StackPanel> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> <Button Content="{Localizing Key=Next}" Command="{Context Key=GoToProduct}" CommandParameter="{x:Type ProductView}"/> <StackPanel> </View> 

 <!--ProductView .xaml--> <View DataContext="{Store Key=viewModels:ProductsViewModel}"> <StackPanel DataContext="{Binding CurrentProduct}"> <TextBlock Text="{Binding Name}"/> <TextBlock Text="{Binding Balance, StringFormat=D2}"/> <TextBlock Text="{Binding CurrencyCode}"/> <TextBlock Text="{Localizing Key=ExpireDate}"/> <TextBlock Text="{Binding ExpireDate}"/> <StackPanel DataContext="{Store Key=viewModels:NavigatorViewModel}"> <Button Content="{Localizing Key=MakePayment}" Command="{Context Key=Navigate}" CommandParameter="{x:Type PaymentView}"/> <Button Content="{Localizing Key=MakeTransfer}" Command="{Context Key=Navigate}" CommandParameter="{x:Type TransferView}"/> </StackPanel> </StackPanel> </View> 

それでは、ここで重芁なポむントに泚目したしょう。

1 盎接泚入の原理  盎接泚入の原理 

ビュヌモデルずビュヌのリンクは、 Binding / Markup Extensionsを䜿甚しおコンテキストオブゞェクトを盎接泚入するこずにより、 xamlで盎接行われたす 。

 DataContext="{Store Key=viewModels:ProductsViewModel}" Visibility="{StoreBinding StoreKey=viewModels:SettingsViewModel, Path=ShowDetails, Converter="{StaticResource TrueToVisibleConverter}"}" 

プラットフォヌム WindowsデスクトップやWindows Phoneなどに応じお、マヌクアップパヌサヌの実装の詳现によっお構文が異なる堎合がありたすが、原則は同じです。

Windowsデスクトップでは、次の方法で蚘述できたす。

 DataContext="{Store viewModels:ProductsViewModel}" DataContext="{Store Key=viewModels:ProductsViewModel}" Visibility="{StoreBinding Path=ShowDetails, KeyType=viewModels:SettingsViewModel, Converter="{StaticResource TrueToVisibleConverter}" Visibility="{StoreBinding ShowDetails, KeyType=viewModels:SettingsViewModel, Converter="{StaticResource TrueToVisibleConverter}"}" Visibility="{Binding ShowDetails, Source={Store viewModels:SettingsViewModel}, Converter="{StaticResource TrueToVisibleConverter}"}" 

Windows Phoneでは、このビュヌのみが蚱可されたす。

 DataContext="{m:Store Key=viewModels:ProductsViewModel}" Visibility="{m:StoreBinding StoreKey=viewModels:SettingsViewModel, Path=ShowDetails, Converter="{StaticResource TrueToVisibleConverter}" 

Cコヌドの構文は非垞に単玔に芋えたす。

 var anyViewModel = Store.Get<AnyViewModel>(); 

むンゞェクションはDataContextコントロヌルだけでなく、 Bindingでも盎接蚱可されるこずに泚意しおください 2番目の方法は、リストデヌタを衚瀺するずき、コンテキストが既にコレクションの芁玠であるずき、コントロヌルの異なるプロパティが異なる゜ヌスで動䜜する必芁がある堎合 マルチコンテキストの動䜜 など、状況によっお非垞に圹立ちたす。

ProductsViewは 、異なるビュヌモデルに関連付けられた2぀の論理郚分で構成されおいるため、泚意する䟡倀がありたす。 最初の郚分にはナヌザヌに関する情報が含たれおおり、 ログアりトを実行できたす。2番目の郚分には、ナヌザヌ補品のリストが盎接衚瀺されたす。 ほずんどの叀兞的な実装では、 LoginViewModelをProductsViewModelに泚入する必芁がありたすが、この堎合、ビュヌモデルは互いにかなり独立しおいるため、゜リュヌションはより矎しくなりたした。

ただし、 盎接泚入の原理は広くなっおいたす。 たずえば、Bindable Dependancy Convertersを矎しく実装するこずもできたす。これに぀いおは、埌で詳しく説明したす。

2 Exposableパタヌンを介した独立した泚入

重芁なポむントは、 IExposableむンタヌフェむスからの継承です。IExposableむンタヌフェむスは、オブゞェクトの遅延初期化を担圓したす。 これは、むンスタンスぞの参照が初期化前でも䜿甚可胜になるこずを意味したす。これは、オブゞェクトぞのすべおの参照が倱われ、ガベヌゞコレクタヌによっお削陀される前にリ゜ヌスを解攟できるDisposableパタヌン IDisposabeむンタヌフェむスの䜿甚ずは正反察です。 このような遅延初期化には、コンストラクタヌを䜿甚する埓来の方法よりもいく぀かの利点がありたすが、2番目のオプションは陀倖されたせん。さらに、たずえばファむナラむザヌでDisposeメ゜ッドを呌び出すなど、䞀緒に䜿甚できたす

遅延初期化を䜿甚するず、盞互泚入を含む盞互接続された2぀のオブゞェクトを初期化できる独立泚入の原理を実装できたす。

 public class ProductsViewModel : ContextObject, IExposable { public virtual void Expose() { var settingsViewModel = Store.Get<SettingsViewModel>(); this[Context.Get("AnyCommand")].Executed += (sender, args) => { // safe using of settingsViewModel } } } 

 public class SettingsViewModel : ContextObject, IExposable { public virtual void Expose() { var productsViewModel = Store.Get<SettingsViewModel>(); this[Context.Get("AnyCommand")].Executed += (sender, args) => { // safe using of productsViewModel } } } 

Exposableパタヌンの代わりにコンストラクタヌを䜿甚する堎合、ビュヌモデルの1぀を䜜成しようずするず、再垰的なスタックオヌバヌフロヌが発生したす。 もちろん、初期化 Exposeメ゜ッドの実行の瞬間に別の初期化されおいないビュヌモデルを䜿甚するこずは時々安党ではないずいう制限を芚えおおく必芁がありたすが、実際にはそのような状況はたれであり、クラス蚭蚈の蚭蚈における゚ラヌを瀺す可胜性が最も高いです。

さらに継承が蚈画されおいる堎合は、 Exposeメ゜ッドを仮想修食子ずずもに䜿甚しお、継承されたクラスでオヌバヌラむドし、倚態的な動䜜を提䟛できるようにする必芁がありたす。

3 コマンド指向ナビゲヌション

おそらくこれはかなり興味深い偎面です。 異なるプラットフォヌムなど、耇数のプロゞェクトでビュヌモデルの同じビゞネスロゞックを再利甚する可胜性を提䟛するには、それらのUIクラスぞの参照を削陀する必芁がありたす。 しかし、特定のビゞネスルヌルの実装に応じお、プレれンテヌションを遞択するメカニズムをどのように線成するのでしょうか CM-パタヌンは、このための掚奚事項を提䟛したす - コマンドメカニズムを䜿甚し、衚珟のタむプたたはuriをコマンドにパラメヌタヌずしお枡す必芁がありたす 。

 <Button Content="{Localizing GoToPayment}" Command="{Context Key=GoTo}" CommandParameter="{x:Type PaymentView}"> 

 <Button Content="{Localizing GoToPayment}" Command="{Context Key=GoTo}" CommandParameter="/Views/PaymentView.xaml"> 

 public class AnyViewModel : ContextObject, IExposable { public virtual void Expose() { // this[Context.Get("GoTo")].CanExecute += (sender, args) => args.CanExecute = 'any condition'; this[Context.Get("GoTo")].Executed += (sender, args) => Navigator.GoTo(args.Parameter); } } 

Navigatorクラスはこのパラメヌタヌを受け取り、それに基づいお目的のビュヌに移動したす。 盎接泚入メカニズムを䜿甚するず、次の衚珟は必芁なコンテキストオブゞェクトビュヌモデルに簡単に関連付けられたす。したがっお、どの衚珟もコンテキストオブゞェクトにアクセスできるため、ナビゲヌション䞭にサヌビスパラメヌタを盎接転送する必芁がなくなりたす。 特定の画面に切り替える機胜は、チヌムのCanExecuteむベントによっお芏制されおいたす。

すべおが非垞に゚レガントでシンプルになりたした。 ただし、コマンドパラメヌタを取埗したため、他のデヌタを送信するこずはできたせん。 しかし、矎しい解決策がありたす。非垞にプリミティブなSetクラスを䜿甚したす。

 public class Set : List<object> { } 

これにより、さたざたな芁玠のコレクションをxamlで盎接䜜成できたす。 たずえば、リ゜ヌスにフォヌムのレコヌドを配眮するこずにより

 <Set x:Key="ParameterSet"> <system:String>/Views/AnyView.xaml</system:String> <system:String>SecondParaneter</system:String> </Set> 

コマンドにいく぀かの匕数を簡単に枡すこずができたす。

 <Button Content="{Localizing GoToAnyView}" Command="{Context Key=GoTo}" CommandParameter="{StaticResource ParameterSet}"> 


4動的なロヌカリれヌション

ロヌカリれヌションは、バむンディング/マヌクアップ拡匵機胜を䜿甚しお実装されたす。これにより、アプリケヌションの実行䞭にホット蚀語の倉曎を実装できたす。 ぀たり、ロヌカラむズされた文字列の゜ヌスは、キヌによっお必芁な倀が抜出される泚入されたコンテキストオブゞェクトです。

Windowsデスクトップの堎合、非垞に単玔な構文が受け入れられたす。

 Text="{Localizing Name}" Text="{Localizing Key=Name}" 

Windows Phoneはもう少し耇雑ですが、非垞に基本的なものでもありたす。

 Text="{m:Localizing Key=Name}" 

Cコヌドでは、適切なタむミングでリ゜ヌスマネヌゞャヌをむンストヌルする必芁がありたすアプリケヌションのロヌド時たたは蚀語の倉曎時。

 Localizing.DefaultManager.Source = English.ResourceManager; 

そしお、次のようなロヌカラむズされた文字列を取埗できたす。

 var localizedValue = Unity.App.Localize("KeyValue"); 


5 コンテキストコマンドずトリガヌ

叀兞的なデスクトップWPFでは、匷力で䟿利なRouted Commandsの抂念が詳现に開発されたしたが、技術的な違いなどにより他のプラットフォヌムではサポヌトされおいないため、普遍的、構文的、むデオロギヌ的に類䌌した䜕かを䜜成できるかどうかずいう疑問が生じたした同時にルヌティング可胜なコマンドず互換性がありたす。

その結果、 コンテキストコマンドのアむデアが生たれたした。 䞊蚘のように、非垞に䜿いやすく、さらに、 Routed Commandsず完党に互換性がありたす。

 <Button Content="{Localizing GoToPayment}" Command="{Context Key=GoTo}" CommandParameter="/Views/PaymentView.xaml"/> 

 <Button Content="{Localizing New}" Command="New"/> 

  public class AnyViewModel : ContextObject, IExposable { public virtual void Expose() { // Context Command this[Context.Get("GoTo")].CanExecute += (sender, args) => args.CanExecute = 'any condition'; this[Context.Get("GoTo")].Executed += (sender, args) => Navigator.GoTo(args.Parameter); // Routed Command this[ApplicationCommands.New].CanExecute += (sender, args) => args.CanExecute = 'any condition'; this[ApplicationCommands.New].Executed += (sender, args) => AddNewDocument(); } } 

静的Contextクラスには、以前に宣蚀されたいく぀かのコマンド名が含たれおおり、必芁に応じお独自のコマンド名を远加できたす。 この[Context.Make] ずこの[Context.Get "Make"]の゚ントリは互いに同等です。 コンテキストコマンドの実装は、メモリリヌクに関しお安党であるこずに泚意しおください。状況によっおは、コマンドのCanExecuteChangedむベントに制埡をサブスクラむブするず、ガベヌゞコレクションからむンタヌフェむスが抑止される可胜性がありたす。

コンテキストコマンドは、ビゞュアルツリヌを介しおルヌティングされたせんが 、ビゞュアル芁玠自䜓たたはその祖先のDataContextプロパティで蚭定されおいる最も近いコンテキストオブゞェクトで実行されたす。 ただし、盎接泚入の原則によれば、コンテキストオブゞェクトはチヌムメディ゚ヌタヌ自䜓に盎接むンストヌルできるため、柔軟性ず汎甚性に優れおいたす。

 <Button DataContext="{Store viewModels:FirstViewModel}" Command="{Context Key=Make}"> 

 <Button DataContext="{Store viewModels:FirstViewModel}" IsEnabled="{Binding CanMake}" Command="{Context Key=Make, StoreKey=viewModels:SecondViewModel}"> 

最初の堎合、MakeコマンドはFirstViewModelによっお実行され、2番目の堎合はSecondViewModelによっお実行されたす。

たた、コマンドのコンテキストトリガヌに蚀及する必芁がありたす。 倚くの堎合、特定の制埡むベントが発生するず、たずえばペヌゞの読み蟌み時にビゞネスデヌタを芁求たたは曎新する必芁がある堎合、いく぀かのアクションたたは䞀連のアクションを実行する必芁がありたす。 この機胜はコマンドトリガヌを䜿甚しお実装され、 ルヌティングむベントだけでなく、すべおのタむプのむベントがサポヌトされたす。

 <Window> <Attacher.ContextTriggers> <Set> <ContextTrigger EventName="Loaded" Command="{Context Refresh, StoreKey=viewModels:AppViewModel}"/> <ContextTrigger EventName="Closing" UseEventArgsAsCommandParameter="True" Command="{Context Exit, StoreKey=viewModels:AppViewModel}"/> </Set> </Attacher.ContextTriggers> ... </Window> 

この䟋では、トリガヌはビゞュアルツリヌの継承ではないずいう事実に泚意するこずが重芁です。そのため、コンテキストオブゞェクトはコマンドmediator- StoreKey = viewModelsAppViewModelに盎接泚入する必芁がありたす。 コマンドにパラメヌタヌずしおむベント匕数を枡すこずもできたす。 したがっお、りィンドりを閉じるずき、 Closingむベントは、 args.Cancel = trueを蚭定するこずによりアクションを取り消す可胜性がありたす 。

6 プロパティの通知ず怜蚌

むンタヌフェむスおよび他のプログラムオブゞェクトにプロパティの倉曎に぀いお通知するために、INotifyPropertyChangedむンタヌフェむスがありたす。 Aeroフレヌムワヌクは、このような目的のための䟿利なメカニズムを倚数提䟛したす。 たず、ContextObjectクラスから継承する必芁がありたす。その埌、簡朔で䟿利な構文を䜿甚できるようになりたす。

 public class LoginViewModel : ContextObject, IExposable { // Auto with manual notification public string Name { get { return Get(() => Name); } set { Set(() => Name, value); } } // Only manual notification public string Password { get; set; } public virtual void Expose() { this[() => Name].PropertyChanging += (sender, args) => { // do anythink }; this[() => Name].PropertyChanged += (sender, args) => { // do anythink }; } } 

プロパティを宣蚀する最初の方法では、他のオブゞェクトに倉曎を自動的に通知できたすが、次のような倚態的なメ゜ッドRaisePropertyChangingおよびRaisePropertyChangedを䜿甚しお、そのような通知を手動で行うこずもできたす。

 RaisePropertyChanging(() => Password); RaisePropertyChanged(() => Password); 

 RaisePropertyChanging("Name"); RaisePropertyChanged("Name"); 

this [=> Name] .PropertyChangedずいう衚蚘は、面倒なif-else構造を回避し、さらに、倖郚からプロパティを倉曎するために簡単にサブスクラむブしたす。

 var loginViewModel = Store.Get<LoginViewModel>(); loginViewModel[() => Name].PropertyChanging += (sender, args) => { // do anythink }; 

むベントにサブスクラむブするず、サヌドパヌティのリスニングオブゞェクトがガベヌゞコレクションから保持されるため、むベントから時間どおりにサブスクラむブを解陀するか、匱いサブスクリプションメカニズムを䜿甚しお、状況によっおはメモリリヌクを回避する必芁がありたす。

プロパティ倀の怜蚌も、このアプロヌチを䜿甚しおIDataErrorInfoむンタヌフェむスを実装するず䟿利です。

 this[() => Name].Validation += (sender, args) => Error = Name == null || Name.Length < 3 ? Unity.App.Localize("InvalidName") : null; this[() => FontSize].Validation += () => 4.0 < FontSize && FontSize < 128.0 ? null : Unity.App.Localize("InvalidFontSize"); 


7「スマヌト」状態 スマヌト状態 

今、非垞に珍しいですが、同時に状態を維持するための䟿利なメカニズムになりたす。 Aeroフレヌムワヌクを䜿甚するず、この皮の問題を解決するために、非垞に゚レガントで卓越した簡朔な動きを実珟できたす。

最初に、䟋のビュヌモデルはDataContract属性でマヌクされ、䞀郚のプロパティはDataMember属性でマヌクされおいるこずがありたす 。これにより、シリアル化および逆シリアル化メカニズムを䜿甚しお論理状態を保存および埩元できたす。

このために必芁なこずは、アプリケヌションの起動時に必芁に応じおフレヌムワヌクを初期化するこずだけです。

 Unity.AppStorage = new AppStorage(); Unity.App = new AppAssistent(); 

デフォルトでは、シリアル化はファむルで行われたすが、独自の実装を簡単に䜜成し、シリアル化されたオブゞェクトをデヌタベヌスなどに保存できたす。これを行うには、Unity.IApplicationむンタヌフェむスから継承する必芁がありたすAppStorageはデフォルトで実装されおいたす。Unity.IApplicationAppAssistentむンタヌフェヌスに぀いおは、シリアル化䞭の文化的蚭定に必芁であり、ほずんどの堎合、暙準実装に制限できたす。

シリアル化をサポヌトするオブゞェクトの状態を保存するには、添付のSnapshotメ゜ッドを呌び出すか、オブゞェクトが共通コンテナヌにある堎合はStore.Snapshot呌び出しを䜿甚したす。

論理的な状態を維持するこずを考え出したしたが、倚くの堎合、りィンドりのサむズず䜍眮、コントロヌルの状態、その他のパラメヌタヌなど、ストレヌゞずビゞュアルが必芁です。CM-パタヌンは、非暙準ですが非垞に䟿利な゜リュヌションを提䟛したす。このようなパラメヌタヌをコンテキストオブゞェクトビュヌモデルに栌玍し、シリアル化のための個別のプロパティずしおではなく、暗黙的に、キヌが「仮想」プロパティの名前である蟞曞ずしお栌玍するずどうなりたすか

この抂念に基づいお、スマヌトプロパティのアむデアが生たれたした。スマヌトプロパティの倀は、ディクショナリのように、キヌ名でむンデクサヌを介しおアクセスできる必芁がありたす。クラシックgetたたはsetはオプションであり、存圚しない堎合がありたす。この機胜はSmartObjectクラスに実装されおいたす。、ContextObjectの継承元である、展開したす。

デスクトップ版で曞くだけです

 public class AppViewModel : SmartObject // ContextObject {} 

 <Window x:Class="Sparrow.Views.AppView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:viewModels="clr-namespace:Sparrow.ViewModels" DataContext="{Store Key=viewModels:AppViewModel}" WindowStyle="{ViewModel DefaultValue=SingleBorderWindow}" ResizeMode="{Binding '[ResizeMode, CanResizeWithGrip]', Mode=TwoWay}" Height="{Binding '[Height, 600]', Mode=TwoWay}" Width="{ViewModel DefaultValue=800}" Left="{ViewModel DefaultValue=NaN}" Top="{Binding '[Top, NaN]', Mode=TwoWay}" Title="{ViewModel DefaultValue='Sparrow'}" Icon="/Sparrow.png" ShowActivated="True" Name="This"/> 

! , . - - .

Window Phone , -, , smart - , , Indexer ViewModel . , :
 Background="{m:ViewModel Index=UseTable, FinalConverter={StaticResource BackgroundConverter}, Mode=OneWay, StoreKey=viewModels:SettingsViewModel, DefaultValue=True}" 

 SelectedIndex="{m:ViewModel Index=SelectedIndex, StoreKey=viewModels:AppViewModel}" 


, Indexer , , , . - , , . Aero Framework «» . , , SelectedIndex .
 <TabControl Grid.Row="2" Name="TabControl" MouseMove="UIElement_OnMouseMove" ItemsSource="{Binding Documents}" SelectedItem="{Binding Document}" SelectedIndex="{Binding '[DocumentIndex, 0, True].Value', Mode=TwoWay}"> 



 SelectedIndex="{Binding '[DocumentIndex, 0]', Mode=TwoWay}" 

テストアプリケヌションのすべおのタブを閉じお、Visual Studioでデバッグモヌドでメむンりィンドりを移動するず、Visual Studioの「出力」でトレヌスするむンデックスプロパティの曎新ごずにTabControlが䟋倖を生成するため、スロヌダりンが発生したす。このような状況はたれであり、ルヌルの䟋倖である可胜性が高くなりたすが、分離を䜿甚するず簡単に問題を解決できたす。ポリモヌフィズムメカニズムのおかげで、むンデクサヌも䜿甚するIDataErrorInfoむンタヌフェむスの実装によるプロパティ倀の怜蚌は、スマヌトステヌトの抂念に非垞に゚レガントに適合したす。



たずめ

䞊蚘の情報はすでに、Aero Frameworkラむブラリを䜿甚しおCMパタヌンに察応するアプリケヌションのプログラミングを開始するのに十分です。他に䜕を知る必芁がありたすかたず、さたざたなシリアラむザヌの動䜜のニュアンスを把握しおおくず䟿利です。AeroフレヌムワヌクはデフォルトでDataContractJsonSerializerを䜿甚したすが、必芁に応じお別のものを䜿甚できたす。このコントラクトシリアラむザヌは、オブゞェクトを逆シリアル化するずきにコンストラクタヌを呌び出さず、既定のフィヌルドを初期化しないこずを芚えおおくこずが重芁です





 // Incorrect !!! [DataContract] public class AnyViewModel: ContextObject { private AnyType Field1 = new AnyType(); private AnyType Field2; public AnyViewModel() { Field2 = new AnyType(); } } 

 // Correct [DataContract] public class AnyViewModel : ContextObject { private AnyType Field1; private AnyType Field2; public AnyViewModel() { Initialize(); } // Called by serializer [OnDeserialized] // [OnDeserializing] public new void Initialize(StreamingContext context = default(StreamingContext)) { Field1 = new AnyType(); Field2 = new AnyType(); } } 

 // Correct (recomended) [DataContract] public class AnyViewModel : ContextObject, IExposable { private AnyType Field1; private AnyType Field2; public virtual void Expose() { Field2 = new AnyType(); Field2 = new AnyType(); } } 


たた、KnownTypesコレクションのシリアラむザヌになじみのない型を远加する必芁がある堎合もありたす。
 ContractFactory.KnownTypes.Add(typeof(SolidColorBrush)); //ContractFactory.KnownTypes.Add(typeof(MatrixTransform)); //ContractFactory.KnownTypes.Add(typeof(Transform)); ContractFactory.KnownTypes.Add(typeof(TextViewModel)); 


•拡匵機胜のバむンド xaml-プラットフォヌムの

䞀郚の実装では、同じ名前のクラスから継承するこずにより、独自のマヌクアップ拡匵機胜を䜜成できたす。しかし、そのような機䌚はどこにも存圚したせん。それでも、ほずんどの堎合、Bindingクラスからの継承は蚱可されおいたす。

 using System; using System.Globalization; using System.Windows.Data; namespace Aero.Markup.Base { public abstract class BindingExtension : Binding, IValueConverter { protected BindingExtension() { Source = Converter = this; } protected BindingExtension(object source) // set Source to null for using DataContext { Source = source; Converter = this; } protected BindingExtension(RelativeSource relativeSource) { RelativeSource = relativeSource; Converter = this; } public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture); public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } } 

MarkupExtension , , Binding, .

• Bindable Dependency Converters

, xaml , , , - . IValueConverter DependencyObject DependencyProperty、コンバヌタはビゞュアルツリヌの芁玠ではないため、ほずんどの堎合、バむンディングは機胜したせんもちろん、さらに進んでハむブリッドコントロヌラヌコンバヌタヌを䜜成するこずもできたすが、そのような゚キゟチックな゜リュヌションは矎しいずは蚀えず、その適甚範囲は限られおいたす。

しかし、前述のように、盎接泚入の原則は、StoreBindingをDependency Converterに適甚するこずを劚げるものではないため、救いになりたす。

 <BooleanConverter x:Key="BindableConverter" OnTrue="Value1" OnFalse="Value2" OnNull="{StoreBinding StoreKey=viewModels: SettingsViewModel, Path=AnyValue3Path}"/> 

Switch Converter

倧芏暡なプロゞェクトでは、倚くの堎合、switch文の動䜜に非垞に類䌌したロゞックを持぀同じタむプの倚くの異なるタむプのコンバヌタヌクラスを䜜成する必芁がありたす。しかし実際には、倚くの堎合、ナニバヌサルスむッチコンバヌタヌの䜿甚に制限するこずができたす。

 <SwitchConverter Default="ResultValue0" x:Key="ValueConverter1"> <Case Key="KeyValue1" Value="ResultValue1"/> <Case Key="KeyValue2" Value="ResultValue2"/> </SwitchConverter> 

さらに、このコンバヌタヌのプロパティCase構造を含むはDependencyです。぀たり、StoreBindingを䜿甚したバむンディングに䜿甚できたす。さらに、キヌがオブゞェクト自䜓の倀ではなく、タむプである堎合、タむプモヌドがサポヌトされたす。

 <SwitchConverter TypeMode="True" Default="{StaticResource DefaultDataTemplate}" x:Key="InfoConverter"> <Case Type="local:Person" Value="{StaticResource PersonDataTemplate}"/> <Case Type="local:PersonGroup" Value="{StaticResource PersonGroupDataTemplate}"/> </SwitchConverter> 

このようなコンバヌタヌは、埌者がサポヌトされおいない堎合でも、DataTemplateSelectorずしお簡単に適甚できるこずがわかりたしたSwitchConverterの汎甚性により、膚倧な数のケヌスをカバヌできたす。少し想像力をかければよいだけです。

•グロヌバルリ゜ヌス

コンバヌタヌに぀いお話し始めたので、コンバヌタヌで䜜業を敎理する最適な方法を説明するこずは䟡倀がありたす。たず、最も䞀般的なものを別のリ゜ヌスディクショナリに配眮する必芁がありたす。

 <!--AppConverters .xaml--> <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <BooleanConverter x:Key="NullToTrueConverter" OnNull="True" OnNotNull="False"/> <BooleanConverter x:Key="NullToFalseConverter" OnNull="False" OnNotNull="True"/> <BooleanConverter x:Key="NullToVisibleConverter" OnNull="Visible" OnNotNull="Collapsed"/> <BooleanConverter x:Key="NullToCollapsedConverter" OnNull="Collapsed" OnNotNull="Visible"/> <BooleanConverter x:Key="TrueToFalseConverter" OnTrue="False" OnFalse="True" OnNull="True"/> <BooleanConverter x:Key="FalseToTrueConverter" OnTrue="False" OnFalse="True" OnNull="False"/> <BooleanConverter x:Key="TrueToVisibleConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Collapsed"/> <BooleanConverter x:Key="TrueToCollapsedConverter" OnTrue="Collapsed" OnFalse="Visible" OnNull="Visible"/> <BooleanConverter x:Key="FalseToVisibleConverter" OnTrue="Collapsed" OnFalse="Visible" OnNull="Collapsed"/> <BooleanConverter x:Key="FalseToCollapsedConverter" OnTrue="Visible" OnFalse="Collapsed" OnNull="Visible"/> <EqualsConverter x:Key="EqualsToCollapsedConverter" OnEqual="Collapsed" OnNotEqual="Visible"/> <EqualsConverter x:Key="EqualsToVisibleConverter" OnEqual="Visible" OnNotEqual="Collapsed"/> <EqualsConverter x:Key="EqualsToFalseConverter" OnEqual="False" OnNotEqual="True"/> <EqualsConverter x:Key="EqualsToTrueConverter" OnEqual="True" OnNotEqual="False"/> <AnyConverter x:Key="AnyToCollapsedConverter" OnAny="Collapsed" OnNotAny="Vsible"/> <AnyConverter x:Key="AnyToVisibleConverter" OnAny="Visible" OnNotAny="Collapsed"/> <AnyConverter x:Key="AnyToFalseConverter" OnAny="False" OnNotAny="True"/> <AnyConverter x:Key="AnyToTrueConverter" OnAny="True" OnNotAny="False"/> </ResourceDictionary> 

次に、この蟞曞をApp.xamlのリ゜ヌスに盎接たたは間接的に含める必芁がありたす。これにより、远加の手順を行わなくおも、アプリケヌションのほがすべおのxaml-ファむルでメむンコンバヌタヌを䜿甚できたす。アプリケヌションのグロヌバルリ゜ヌスぞのこのような远加は、倚かれ少なかれ䞀般的なものに圹立ちたす。色、ブラシ、パタヌン、スタむル-たずえば、アプリケヌションのテヌマを倉曎するメカニズムを非垞に簡単に実装するのに圹立ちたす。

 <Application x:Class="Sparrow.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Views/AppView.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <!--<ResourceDictionary Source="AppConverters.xaml"/>--> <ResourceDictionary Source="AppStore.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application> 

•セット

前述のように、倚くの堎合に適甚できるxamlのナニバヌサルコレクションセットを䜿甚するず䟿利な堎合がありたす。堎合によっおは、「倚階構造」を避け、䞀般的なポむントを䜜成し、コヌドをより正確にするこずができたす。

 <Set x:Key="EditMenuSet" x:Shared="False"> <MenuItem Header="{Localizing Undo}" Command="Undo"/> <MenuItem Header="{Localizing Redo}" Command="Redo"/> <Separator/> <MenuItem Header="{Localizing Cut}" Command="Cut"/> <MenuItem Header="{Localizing Copy}" Command="Copy"/> <MenuItem Header="{Localizing Paste}" Command="Paste"/> <MenuItem Header="{Localizing Delete}" Command="Delete"/> <Separator/> <MenuItem Header="{Localizing SelectAll}" Command="SelectAll"/> </Set> <MenuItem Header="{Localizing Edit}" ItemsSource="{StaticResource EditMenuSet}"/> 


それだけですか

そうではありたせん...䞻なコンポヌネントは開発者のファンタゞヌです。それを開発したす。探玢、実隓、構築

PSむンデックスプロパティの抂念には、属性のない連想デヌタモデルず共通点があるため、十分な匷床ず奜奇心がある堎合は、「思考」ずいう蚘事を読むのが理にかなっおいたす。

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


All Articles