// // NavigationHelper.cpp // Implementierung der NavigationHelper-Klasse und verknüpfter Klassen // #include "pch.h" #include "NavigationHelper.h" #include "RelayCommand.h" #include "SuspensionManager.h" using namespace $safeprojectname$::Common; using namespace Platform; using namespace Platform::Collections; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; using namespace Windows::System; using namespace Windows::UI::Core; using namespace Windows::UI::ViewManagement; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Interop; using namespace Windows::UI::Xaml::Navigation; #if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP using namespace Windows::Phone::UI::Input; #endif /// /// Initialisiert eine neue Instanz der -Klasse. /// /// /// Der an übergebene Parameterwert /// übergeben wurde, als diese Seite ursprünglich angefordert wurde. /// /// /// Ein Wörterbuch des Zustands, der von dieser Seite während einer früheren /// beibehalten wurde. Beim ersten Aufrufen einer Seite ist dieser Wert NULL. /// LoadStateEventArgs::LoadStateEventArgs(Object^ navigationParameter, IMap^ pageState) { _navigationParameter = navigationParameter; _pageState = pageState; } /// /// Ruft die -Eigenschaft der -Klasse ab. /// Object^ LoadStateEventArgs::NavigationParameter::get() { return _navigationParameter; } /// /// Ruft die -Eigenschaft der -Klasse ab. /// IMap^ LoadStateEventArgs::PageState::get() { return _pageState; } /// /// Initialisiert eine neue Instanz der -Klasse. /// /// Ein leeres Wörterbuch, das mit dem serialisierbaren Zustand aufgefüllt wird. SaveStateEventArgs::SaveStateEventArgs(IMap^ pageState) { _pageState = pageState; } /// /// Ruft die -Eigenschaft der -Klasse ab. /// IMap^ SaveStateEventArgs::PageState::get() { return _pageState; } /// /// Initialisiert eine neue Instanz der -Klasse. /// /// Ein Verweis auf die aktuelle für die Navigation verwendete Seite. /// Dieser Verweis ermöglicht eine Änderung des Rahmens und stellt sicher, dass die Tastatur /// Navigationsanforderungen treten nur auf, wenn die Seite das gesamte Fenster einnimmt. NavigationHelper::NavigationHelper(Page^ page, RelayCommand^ goBack, RelayCommand^ goForward) : _page(page), _goBackCommand(goBack), _goForwardCommand(goForward) { // Zwei Änderungen vornehmen, wenn diese Seite Teil der visuellen Struktur ist: // 1) Den Ansichtszustand der Anwendung dem visuellen Zustand für die Seite zuordnen // 2) Behandeln von Hardwarenavigationsanforderungen _loadedEventToken = page->Loaded += ref new RoutedEventHandler(this, &NavigationHelper::OnLoaded); //// Dieselben Änderungen rückgängig machen, wenn die Seite nicht mehr sichtbar ist _unloadedEventToken = page->Unloaded += ref new RoutedEventHandler(this, &NavigationHelper::OnUnloaded); } NavigationHelper::~NavigationHelper() { delete _goBackCommand; delete _goForwardCommand; _page = nullptr; } /// /// Wird aufgerufen, wenn die Seite Teil der visuellen Struktur ist /// /// Instanz, von der das Ereignis ausgelöst wurde. /// Ereignisdaten, die die Bedingungen beschreiben, die zu dem Ereignis geführt haben. void NavigationHelper::OnLoaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { #if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP _backPressedEventToken = HardwareButtons::BackPressed += ref new EventHandler(this, &NavigationHelper::HardwareButton_BackPressed); #else Page ^page = _page.Resolve(); // Tastatur- und Mausnavigation trifft nur zu, wenn das gesamte Fenster ausgefüllt wird. if (page != nullptr && page->ActualHeight == Window::Current->Bounds.Height && page->ActualWidth == Window::Current->Bounds.Width) { // Direkt am Fenster lauschen, sodass kein Fokus erforderlich ist _acceleratorKeyEventToken = Window::Current->CoreWindow->Dispatcher->AcceleratorKeyActivated += ref new TypedEventHandler(this, &NavigationHelper::CoreDispatcher_AcceleratorKeyActivated); _pointerPressedEventToken = Window::Current->CoreWindow->PointerPressed += ref new TypedEventHandler(this, &NavigationHelper::CoreWindow_PointerPressed); _navigationShortcutsRegistered = true; } #endif } /// /// Wird aufgerufen, wenn die Seite aus der visuellen Struktur entfernt wird /// /// Instanz, von der das Ereignis ausgelöst wurde. /// Ereignisdaten, die die Bedingungen beschreiben, die zu dem Ereignis geführt haben. void NavigationHelper::OnUnloaded(Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { #if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP HardwareButtons::BackPressed -= _backPressedEventToken; #else if (_navigationShortcutsRegistered) { Window::Current->CoreWindow->Dispatcher->AcceleratorKeyActivated -= _acceleratorKeyEventToken; Window::Current->CoreWindow->PointerPressed -= _pointerPressedEventToken; _navigationShortcutsRegistered = false; } #endif // Den Handler entfernen und den Seitenverweis freigeben Page ^page = _page.Resolve(); if (page != nullptr) { page->Loaded -= _loadedEventToken; page->Unloaded -= _unloadedEventToken; delete _goBackCommand; delete _goForwardCommand; _goForwardCommand = nullptr; _goBackCommand = nullptr; } } #pragma region Navigation support /// /// Von der -Eigenschaft verwendete Methode /// um zu bestimmen, ob zurückgesetzt werden kann. /// /// /// "True", wenn mindestens einen Eintrag aufweist /// im Rückwärtsnavigationsverlauf. /// bool NavigationHelper::CanGoBack() { Page ^page = _page.Resolve(); if (page != nullptr) { auto frame = page->Frame; return (frame != nullptr && frame->CanGoBack); } return false; } /// /// Von der -Eigenschaft verwendete Methode /// um die -Methode aufzurufen. /// void NavigationHelper::GoBack() { Page ^page = _page.Resolve(); if (page != nullptr) { auto frame = page->Frame; if (frame != nullptr && frame->CanGoBack) { frame->GoBack(); } } } /// /// Von der -Eigenschaft verwendete Methode /// um zu bestimmen, ob fortgesetzt werden kann. /// /// /// "True", wenn mindestens einen Eintrag aufweist /// im Vorwärtsnavigationsverlauf. /// bool NavigationHelper::CanGoForward() { Page ^page = _page.Resolve(); if (page != nullptr) { auto frame = page->Frame; return (frame != nullptr && frame->CanGoForward); } return false; } /// /// Von der -Eigenschaft verwendete Methode /// um die -Methode aufzurufen. /// void NavigationHelper::GoForward() { Page ^page = _page.Resolve(); if (page != nullptr) { auto frame = page->Frame; if (frame != nullptr && frame->CanGoForward) { frame->GoForward(); } } } /// /// Ruft die -Eigenschaft der -Klasse ab. /// RelayCommand^ NavigationHelper::GoBackCommand::get() { if (_goBackCommand == nullptr) { _goBackCommand = ref new RelayCommand( [this](Object^) -> bool { return CanGoBack(); }, [this](Object^) -> void { GoBack(); } ); } return _goBackCommand; } /// /// Ruft die -Eigenschaft der -Klasse ab. /// RelayCommand^ NavigationHelper::GoForwardCommand::get() { if (_goForwardCommand == nullptr) { _goForwardCommand = ref new RelayCommand( [this](Object^) -> bool { return CanGoForward(); }, [this](Object^) -> void { GoForward(); } ); } return _goForwardCommand; } #if WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP /// /// Behandelt das Verhalten der Schaltfläche "Zurück" und navigiert durch den Verlauf des Stammframes. /// void NavigationHelper::HardwareButton_BackPressed(Object^ sender, BackPressedEventArgs^ e) { if (this->GoBackCommand->CanExecute(nullptr)) { e->Handled = true; this->GoBackCommand->Execute(nullptr); } } #else /// /// Wird bei jeder Tastatureingabe aufgerufen, einschließlich Systemtasten wie ALT-Tastenkombinationen, wenn /// diese Seite aktiv ist und das gesamte Fenster ausfüllt. Wird verwendet zum Erkennen von Tastaturnavigation /// zwischen Seiten, auch wenn sich die Seite selbst nicht im Fokus befindet. /// /// Instanz, von der das Ereignis ausgelöst wurde. /// Ereignisdaten, die die Bedingungen beschreiben, die zu dem Ereignis geführt haben. void NavigationHelper::CoreDispatcher_AcceleratorKeyActivated(CoreDispatcher^ sender, AcceleratorKeyEventArgs^ e) { sender; // Nicht verwendeter Parameter auto virtualKey = e->VirtualKey; // Weitere Untersuchungen nur durchführen, wenn die Taste "Nach links", "Nach rechts" oder die dezidierten Tasten "Zurück" oder "Weiter" // gedrückt werden if ((e->EventType == CoreAcceleratorKeyEventType::SystemKeyDown || e->EventType == CoreAcceleratorKeyEventType::KeyDown) && (virtualKey == VirtualKey::Left || virtualKey == VirtualKey::Right || virtualKey == VirtualKey::GoBack || virtualKey == VirtualKey::GoForward)) { auto coreWindow = Window::Current->CoreWindow; auto downState = Windows::UI::Core::CoreVirtualKeyStates::Down; bool menuKey = (coreWindow->GetKeyState(VirtualKey::Menu) & downState) == downState; bool controlKey = (coreWindow->GetKeyState(VirtualKey::Control) & downState) == downState; bool shiftKey = (coreWindow->GetKeyState(VirtualKey::Shift) & downState) == downState; bool noModifiers = !menuKey && !controlKey && !shiftKey; bool onlyAlt = menuKey && !controlKey && !shiftKey; if ((virtualKey == VirtualKey::GoBack && noModifiers) || (virtualKey == VirtualKey::Left && onlyAlt)) { // Wenn die Taste "Zurück" oder ALT+NACH-LINKS-TASTE gedrückt wird, zurück navigieren e->Handled = true; GoBackCommand->Execute(this); } else if ((virtualKey == VirtualKey::GoForward && noModifiers) || (virtualKey == VirtualKey::Right && onlyAlt)) { // Wenn die Taste "Weiter" oder ALT+NACH-RECHTS-TASTE gedrückt wird, vorwärts navigieren e->Handled = true; GoForwardCommand->Execute(this); } } } /// /// Wird bei jedem Mausklick, jeder Touchscreenberührung oder einer äquivalenten Interaktion aufgerufen, wenn diese /// Seite aktiv ist und das gesamte Fenster ausfüllt. Wird zum Erkennen von "Weiter"- und "Zurück"-Maustastenklicks /// im Browserstil verwendet, um zwischen Seiten zu navigieren. /// /// Instanz, von der das Ereignis ausgelöst wurde. /// Ereignisdaten, die die Bedingungen beschreiben, die zu dem Ereignis geführt haben. void NavigationHelper::CoreWindow_PointerPressed(CoreWindow^ sender, PointerEventArgs^ e) { auto properties = e->CurrentPoint->Properties; // Tastenkombinationen mit der linken, rechten und mittleren Taste ignorieren if (properties->IsLeftButtonPressed || properties->IsRightButtonPressed || properties->IsMiddleButtonPressed) { return; } // Wenn "Zurück" oder "Vorwärts" gedrückt wird (jedoch nicht gleichzeitig), entsprechend navigieren bool backPressed = properties->IsXButton1Pressed; bool forwardPressed = properties->IsXButton2Pressed; if (backPressed ^ forwardPressed) { e->Handled = true; if (backPressed) { if (GoBackCommand->CanExecute(this)) { GoBackCommand->Execute(this); } } else { if (GoForwardCommand->CanExecute(this)) { GoForwardCommand->Execute(this); } } } } #endif #pragma endregion #pragma region Process lifetime management /// /// Wird aufgerufen, wenn diese Seite in einem Rahmen angezeigt werden soll. /// /// Ereignisdaten, die beschreiben, wie diese Seite erreicht wurde. Die /// Parametereigenschaft stellt die anzuzeigende Gruppe bereit. void NavigationHelper::OnNavigatedTo(NavigationEventArgs^ e) { Page ^page = _page.Resolve(); if (page != nullptr) { auto frameState = SuspensionManager::SessionStateForFrame(page->Frame); _pageKey = "Page-" + page->Frame->BackStackDepth; if (e->NavigationMode == NavigationMode::New) { // Bestehenden Zustand für die Vorwärtsnavigation löschen, wenn dem Navigationsstapel eine neue // Seite hinzugefügt wird auto nextPageKey = _pageKey; int nextPageIndex = page->Frame->BackStackDepth; while (frameState->HasKey(nextPageKey)) { frameState->Remove(nextPageKey); nextPageIndex++; nextPageKey = "Page-" + nextPageIndex; } // Den Navigationsparameter an die neue Seite übergeben LoadState(this, ref new LoadStateEventArgs(e->Parameter, nullptr)); } else { // Den Navigationsparameter und den beibehaltenen Seitenzustand an die Seite übergeben, // dabei die gleiche Strategie verwenden wie zum Laden des angehaltenen Zustands und zum erneuten Erstellen von im Cache verworfenen // Seiten LoadState(this, ref new LoadStateEventArgs(e->Parameter, safe_cast^>(frameState->Lookup(_pageKey)))); } } } /// /// Wird aufgerufen, wenn diese Seite nicht mehr in einem Rahmen angezeigt wird. /// /// Ereignisdaten, die beschreiben, wie diese Seite erreicht wurde. Die /// Parametereigenschaft stellt die anzuzeigende Gruppe bereit. void NavigationHelper::OnNavigatedFrom(NavigationEventArgs^ e) { Page ^page = _page.Resolve(); if (page != nullptr) { auto frameState = SuspensionManager::SessionStateForFrame(page->Frame); auto pageState = ref new Map(); SaveState(this, ref new SaveStateEventArgs(pageState)); frameState->Insert(_pageKey, pageState); } } #pragma endregion