440 likes | 648 Views
Tips for building a Windows Store app using XAML and C++: The Hilo project. Scott Densmore – Development Lead 3-001. Agenda. Hilo C++ Background Tips for building C++ Windows Store apps. The Hilo Project.
E N D
Tips for building a Windows Store app using XAML and C++: The Hilo project Scott Densmore – Development Lead 3-001
Agenda • Hilo C++ Background • Tips for building C++ Windows Store apps
The Hilo Project • End-to-end photo sample that provides guidance to C++ developers on how to use modern C++, asynchronous programming, XAML, and the Windows Runtime to build a world-ready app for the global market.
Demo • Walkthrough
1. Use smart pointers, stack semantics and RAII pattern • // old way • void func() { • widget* p = new widget(); • p->draw(); • delete p; • }
Smart Pointers • void func() { • auto p = make_shared<widget>(); // no leak, and exception safe • p->draw(); • } • // no delete required, out-of-scope triggers smart pointer destructor
Stack semantics and the RAII pattern • void f() { • unique_ptr<widget> p( new widget(…) ); • my_classx( new widget() ); … • } • // automatic destruction and deallocation for both widget objects • // automatic exception safety
2. Use public ref classes only for interop • public ref class Photo sealed : public IResizable, public IPhoto, public Windows::UI::Xaml::Data::INotifyPropertyChanged • { • internal: • Photo(Windows::Storage::BulkAccess::FileInformation^ file, IPhotoGroup^ photoGroup, std::shared_ptr<ExceptionPolicy> exceptionPolicy); • public: • virtual ~Photo(); • virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged; • ... • internal: • concurrency::task<void> QueryPhotoImageAsync(); • ... • private: • Windows::Storage::BulkAccess::FileInformation^ m_fileInfo; • ... • };
3. Don't create circular references between ref classes and lambda expressions. • … • { • auto wr = Platform::WeakReference(this); • function<void()> callback = [wr] { • auto vm = wr.Resolve<HubPhotoGroup>(); • if (nullptr != vm) • { • vm->OnDataChanged(); • } • }; • m_repository->AddObserver(callback, PageType::Hub); • }
4. Use explicit capture for lambda expressions • intmain() • { • using namespace std; • inti = 3; • int j = 5; • ... // more variables • // bad – specifies that the body of the lambda expression accesses all captured variables by reference unless you explicitly specify otherwise • function<int(void)> f = [&] { return i + j; }; • // bad – that the body of the lambda expression accesses all captured variables by value • function<int (void)> f = [=] { return i + j; }; • // The following lambda expression captures i by value and j by reference. • function<int (void)> f = [i, &j] { return i + j; }; • }
5. Use task cancellation consistently. • void CartoonizeImageViewModel::CartoonizeImage(Object^ parameter) { • m_cts = cancellation_token_source(); • auto token = m_cts.get_token(); • m_initializationTask = m_initializationTask.then([this, token]() -> task<void> • { • if (m_useHardwareAcceleration){ • return CartoonizeImageAmpAsync(token); • } • ... • } • void CartoonizeImageViewModel::CancelCartoonize(Object^ parameter) • { • m_cts.cancel(); • ViewModelBase::GoBack(); • }
6. Be aware of context rules for continuations of tasks • task<void> ImageViewModel::QueryPhotosAsync() { • assert(IsMainThread()); • ... • auto t = m_repository->GetPhotosForDateRangeQueryAsync(m_query); • return t.then([this](IVectorView<IPhoto^>^ photos) { • assert(IsBackgroundThread()); • auto temp = ref new Vector<IPhoto^>(); • ... • }, task_continuation_context::use_arbitrary()).then([this](Vector<IPhoto^>^ temp) { • assert(IsMainThread()); • auto size = temp->Size; • ... • }).then([this](task<void> priorTask) { • assert(IsMainThread()); • try { • priorTask.get(); • ... • });
7. Use the LayoutAwarePage class to provide navigation, state management, and view management • Navigation support • virtual void GoHome(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); • virtual void GoBack(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); • virtual void GoForward(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); • Visual state switching • void StartLayoutUpdates(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); • void StopLayoutUpdates(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); • Process lifetime management • virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; • virtual void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; • virtual void LoadState(Platform::Object^ navigationParameter, • Windows::Foundation::Collections::IMap<Platform::String^, Platform::Object^>^ pageState); • virtual void SaveState(Windows::Foundation::Collections::IMap<Platform::String^, • Platform::Object^>^ pageState);
Navigating with XAML • <local:HiloPage … /> • <Button x:Name="FilmStripBackButton" AutomationProperties.AutomationId="FilmStripBackButton" Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Margin="26,53,36,36" Style="{StaticResourceBackButtonStyle}“VerticalAlignment="Top"/>
Navigating with Code • void RotateImageViewModel::Initialize(String^ photoPath) { assert(IsMainThread()); m_photo= nullptr; m_photoPath= photoPath; GetImagePhotoAsync().then([this](IPhoto^ photo) { assert(IsMainThread()); // Return to the hub page if the photo is no longer present if (photo == nullptr) { GoHome(); } }); • }
8. 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>
9. Use asynchronous programming techniques to keep the UI responsive. • task<void> ImageViewModel::QueryPhotosAsync() { • assert(IsMainThread()); • ... • auto t = m_repository->GetPhotosForDateRangeQueryAsync(m_query); • return t.then([this](IVectorView<IPhoto^>^ photos) { • assert(IsBackgroundThread()); • auto temp = ref new Vector<IPhoto^>(); • ... • }, task_continuation_context::use_arbitrary()).then([this](Vector<IPhoto^>^ temp) { • assert(IsMainThread()); • auto size = temp->Size; • ... • }).then([this](task<void> priorTask) { • assert(IsMainThread()); • try { • priorTask.get(); • ... • });
10. Use BindableBase class to provide support for MVVM • public ref class BindableBase : Windows::UI::Xaml::DependencyObject, • Windows::UI::Xaml::Data::INotifyPropertyChanged, • Windows::UI::Xaml::Data::ICustomPropertyProvider • { • public: • virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged; • ... • protected: • virtual void OnPropertyChanged(Platform::String^ propertyName); • ... • }
11. Use MVVM to build an interop layer between XAML and your models
13. Use the Calendar class to support world calendars • Calendar^ CalendarExtensions::GetCalendar() { • return ref new Calendar(); • } • intCalendarExtensions::GetNumberOfMonthsInYear(DateTimeyearDate) • { • auto cal = GetCalendar(); • cal->SetDateTime(yearDate); • return cal->NumberOfMonthsInThisYear; • }
15. When the app resumes after termination, use the saved app data to restore the app state.
16. Release exclusive resources when the app is being suspended.
17. Ensure your controls for tap and hold appears above the touch point • void Hilo::ImageView::OnImagePointerPressed(Object^ sender, PointerRoutedEventArgs^ e) • { • m_pointerPressed = true; • PointerPoint^ point = e->GetCurrentPoint(PhotoGrid); • m_pointer = point->Position; • if (point->Properties->IsLeftButtonPressed) • { • ImageViewFileInformationPopup->HorizontalOffset = point->Position.X - 200; • ImageViewFileInformationPopup->VerticalOffset = point->Position.Y - 200; • ImageViewFileInformationPopup->IsOpen = true; • } • }
18. Use the standard touch gestures and controls that Windows 8 provides.
19. Use Windows Runtime to create image thumbnail for tiles • auto createThumbnail = create_task(sourceFile->GetThumbnailAsync(ThumbnailMode::PicturesView, ThumbnailSize)); • return createThumbnail.then([](StorageItemThumbnail^ thumbnail) { • IRandomAccessStream^ imageFileStream = static_cast<IRandomAccessStream^>(thumbnail); • return BitmapDecoder::CreateAsync(imageFileStream); • }).then([decoder](BitmapDecoder^ createdDecoder) { • (*decoder) = createdDecoder; • return createdDecoder->GetPixelDataAsync(BitmapPixelFormat::Rgba8, ...); • }).then([pixelProvider, resizedImageStream](PixelDataProvider^ provider) { • (*pixelProvider) = provider; • return BitmapEncoder::CreateAsync(BitmapEncoder::JpegEncoderId, resizedImageStream); • }).then([pixelProvider, decoder](BitmapEncoder^ createdEncoder) { • createdEncoder->SetPixelData(BitmapPixelFormat::Rgba8, ...); • return createdEncoder->FlushAsync(); • }).then([resizedImageStream] { • resizedImageStream->Seek(0); • return resizedImageStream; • });
20. Ensure queries prefetchproperties to improve performance • auto queryOptions = ref new QueryOptions(CommonFileQuery::OrderByDate, fileTypeFilter); • queryOptions->IndexerOption = IndexerOption::UseIndexerWhenAvailable; • queryOptions->SetPropertyPrefetch(PropertyPrefetchOptions::ImageProperties, dateTakenProperties); • queryOptions->Language = CalendarExtensions::ResolvedLanguage(); • auto fileQuery = folderQuery->CreateFileQueryWithOptions(queryOptions);
21. Keep the launch times of your app fast • ... • m_tileUpdateScheduler= std::make_shared<TileUpdateScheduler>(); • m_tileUpdateScheduler->ScheduleUpdateAsync(m_repository, m_exceptionPolicy); • ...
Related Sessions • 11/2/2012 12:45- B33 McKinley- The Future of C++
Links & thank you • Code http://aka.ms/Hilocpp-code • Documentation http://aka.ms/hilocpp-doc • Codeplex http://hilo.codeplex.com • Thank you