ViewModel First

advertisement
Tips for building a Windows Store app
using XAML and C#: The Kona project
Francis Cheung
fcheung@microsoft.com
patterns & practices Symposium 2013
Agenda


Prism for WPF, Silverlight and Windows Phone
•
•
•
•
•
•
The Kona Project
Rethink Prism Scenarios for Windows Store
•
•
•
•
•
•
Modularity
UI Composition
Region Navigation
Decoupled
Communication
Commands
MVVM Support
Demo AdventureWorks Shopper

Walkthrough
1. Leverage background in Windows Phone
development
Windows Phone apps
Windows Store apps
Deactivate/Tombstoned/Reactivate
Suspend/Terminate/Resume
Microsoft Push Notification Service
(MPN)
Windows Push Notification Service
(WNS)
Windows Phone Marketplace certification Windows Store app certification &
Application Excellence Review (AER)
App manifest declares capabilities
App manifest declares capabilities
2. Focus on AttachedBehaviors
•
•
•
No Blend Behavior<T>
No BindingExpressions
Break out your
AttachedBehavior
experience and
ROCK ON!
3. Push Notification requires Windows Store registration
•
•
Make sure to register your app
with the Windows Store to get
proper credentials (SID &
secret key)
Purely sideloaded apps won’t
be able to receive notifications
from Windows Notification
Service (WNS)
4. async & await are your friends
1: async Task<int> AccessTheWebAsync()
2: {
3:
HttpClient client = new HttpClient();
4:
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
5:
DoIndependentWork();
6:
7:
string urlContents = await getStringTask;
8:
return urlContents.Length;
9: }
Pages and Navigation
5. Use the LayoutAwarePage class to provide navigation,
state management, and visual state management
Navigation support
protected virtual void GoHome(object sender, RoutedEventArgs e)
protected virtual void GoBack(object sender, RoutedEventArgs e)
protected virtual void GoForward(object sender, RoutedEventArgs e)
Visual state switching
public void StartLayoutUpdates(object sender, RoutedEventArgs e)
public void StopLayoutUpdates(object sender, RoutedEventArgs e)
Process lifetime management
protected override void OnNavigatedTo(NavigationEventArgs e)
protected override void OnNavigatedFrom(NavigationEventArgs e)
protected virtual void LoadState(Object navigationParameter,
Dictionary<String, Object> pageState)
protected virtual void SaveState(Dictionary<String, Object> pageState)
Navigation & Visual State Support
XAML:
<Button Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}"
Style="{StaticResource BackButtonStyle}"/>
<UserControl Loaded="StartLayoutUpdates" Unloaded="StopLayoutUpdates">
LoadState & SaveState: SuspensionManager
protected override void SaveState(System.Collections.Generic.Dictionary<string, object> pageState)
{
var virtualizingStackPanel =
VisualTreeUtilities.GetVisualChild<VirtualizingStackPanel>(itemGridView);
if (virtualizingStackPanel != null && pageState != null)
{
pageState["virtualizingStackPanelHorizontalOffset"] = virtualizingStackPanel.HorizontalOffset;
}
}
protected override void LoadState(object navigationParameter,
System.Collections.Generic.Dictionary<string, object> pageState)
{
if (pageState != null && pageState.ContainsKey("virtualizingStackPanelHorizontalOffset"))
{
double.TryParse(pageState["virtualizingStackPanelHorizontalOffset"].ToString(),
out virtualizingStackPanelHorizontalOffset);
}
}
6. Support visual state for landscape, portrait, fill, and snap
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ApplicationViewStates">
<VisualState x:Name="FullScreenLandscape"/>
<VisualState x:Name="Filled"/>
<VisualState x:Name="FullScreenPortrait">
<Storyboard>
...
</Storyboard>
</VisualState>
<VisualState x:Name="Snapped">
<Storyboard>
...
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
World Ready
7. Separate resources for each locale
<ToolTip
x:Uid=“PreviewCartoonizeAppBarButtonToolTip”
Content=“Preview Cartoonization”
… />
Model-View-ViewModel Pattern
8. Navigation: View or ViewModel First
View First:
this.Frame.Navigate(typeof(ItemDetailPage), itemId);
ViewModel First:
Var itemDetailPageViewModel = new ItemDetailPageViewModel(…)
{ ItemId = itemId };
navigationService.Navigate(itemDetailPageViewModel);
Nicki says:
9. Use BindableBase class to provide INPC
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
10. Use the Kona ViewModelLocator
•
Convention based lookup
•
•
•
MyNamespace.MyPage -> MyNamespace.MyPageViewModel
Ability to override convention with exceptions to rule
Can leverage container to instantiate ViewModels.
Typical Validation in WPF/Silverlight
•
•
Implement INotifyDataErrorInfo
UI controls bind to errors dictionary if
NotifyOnValidationError=True
<TextBox Text="{Binding Id, Mode=TwoWay, ValidatesOnExceptions=True,
NotifyOnValidationError=True}"/>
11. Use Kona BindableValidator
View:
<TextBox
Text="{Binding Address.FirstName, Mode=TwoWay}"
behaviors:HighlightFormFieldOnErrors.PropertyErrors="{Binding
Errors[FirstName]}" />
ViewModel:
_bindableValidator = new BindableValidator(_address);
public BindableValidator Errors
{
get { return _bindableValidator; }
}
Decoupled Eventing
•
Hollywood Parent style UI
Composition (user control)
Child control needs to listen to
events raised by long lived services
but no way to unhook…
•
Ported Prism EventAggregator
•
12. Use EventAggregator when necessary
public SubscriberViewModel(IEventAggregator eventAggregator)
{
eventAggregator.GetEvent<ShoppingCartUpdatedEvent>()
.Subscribe(s => UpdateItemCountAsync());
}
public PublisherViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
_eventAggregator.GetEvent<ShoppingCartUpdatedEvent>()
.Publish(string.Empty);
Commanding vs ViewModel Method Invocation
ICommand:
void Execute(object)
bool CanExecute(object)
event EventHandler CanExecuteChanged
Command Invoker:
ButtonBase
----------------------------------------------------Event -> Action
13. Use DelegateCommand for controls that support ICommand
View:
<Button Content=“Go to shopping cart”
Command="{Binding ShoppingCartNavigationCommand}" />
ViewModel:
ShoppingCartNavigationCommand = new
DelegateCommand(NavigateToShoppingCartPage,
CanNavigateToShoppingCartPage);
ShoppingCartNavigationCommand.RaiseCanExecuteChanged();
14. Use AttachedBehaviors and Actions for the rest
View:
<GridView x:Name="itemGridView“
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplate="{StaticResource KonaRI250x250ItemTemplate}"
SelectionMode="None“ IsItemClickEnabled="True"
behaviors:ListViewItemClickedToAction.Action=
"{Binding CategoryNavigationAction}">
ViewModel:
CategoryNavigationAction = NavigateToCategory;
Suspend, Resume, and Terminate
15. Use Kona RestorableStateAttribute and MVVM framework
public class MyViewModel : ViewModel, INavigationAware
{
private string _name;
[RestorableState]
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
}
Symposium 2013
16. Unit Testing nicely integrated into VS2012
WP7: Jeff Wilcox's Silverlight Unit Test Framework
• Tests run in emulator or device
Unit Test Library (Windows Store apps)
• Run and debug from IDE
• Can run tests from command line and export as trx format.
<ItemGroup>
<TestAppPackages Include="$(MSBuildProjectDirectory)\..\Source\**\*.appx" />
</ItemGroup>
<Target Name="Test">
<Exec ContinueOnError="true" Command="vstest.console.exe /InIsolation /logger:trx
%(TestAppPackages.Identity)" />
</Target>
17. File System
 Local Data (SQLite)
 Roaming Data
 Hi Priority Roaming Data
 Password Vault
Symposium 2013
Thanks!
 http://konaguidance.codeplex.com
 fcheung@microsoft.com
Symposium 2013
Download