1 //********************************************************* 2 // 3 // Copyright (c) Microsoft. All rights reserved. 4 // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 5 // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY 6 // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR 7 // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. 8 // 9 //********************************************************* 10 11 // 12 // MainPage.xaml.cpp 13 // Implementation of the MainPage.xaml class. 14 // 15 16 #include "pch.h" 17 #include "MainPage.xaml.h" 18 #include "App.xaml.h" 19 20 #include <collection.h> 21 22 using namespace Windows::UI::Xaml; 23 using namespace Windows::UI::Xaml::Controls; 24 using namespace Windows::Foundation; 25 using namespace Windows::Foundation::Collections; 26 using namespace Platform; 27 using namespace SDKSample; 28 using namespace Windows::UI::Xaml::Navigation; 29 using namespace Windows::UI::Xaml::Interop; 30 using namespace Windows::Graphics::Display; 31 using namespace Windows::UI::ViewManagement; 32 33 MainPage^ MainPage::Current = nullptr; 34 35 MainPage::MainPage() 36 { 37 InitializeComponent(); 38 39 // This frame is hidden, meaning it is never shown. It is simply used to load 40 // each scenario page and then pluck out the input and output sections and 41 // place them into the UserControls on the main page. 42 HiddenFrame = ref new Windows::UI::Xaml::Controls::Frame(); 43 HiddenFrame->Visibility = Windows::UI::Xaml::Visibility::Collapsed; 44 ContentRoot->Children->Append(HiddenFrame); 45 46 FeatureName->Text = FEATURE_NAME; 47 48 this->SizeChanged += ref new SizeChangedEventHandler(this, &MainPage::MainPage_SizeChanged); 49 Scenarios->SelectionChanged += ref new SelectionChangedEventHandler(this, &MainPage::Scenarios_SelectionChanged); 50 51 MainPage::Current = this; 52 autoSizeInputSectionWhenSnapped = true; 53 } 54 55 /// <summary> 56 /// We need to handle SizeChanged so that we can make the sample layout property 57 /// in the various layouts. 58 /// </summary> 59 /// <param name="sender"></param> 60 /// <param name="e"></param> 61 void MainPage::MainPage_SizeChanged(Object^ sender, SizeChangedEventArgs^ e) 62 { 63 InvalidateSize(); 64 MainPageSizeChangedEventArgs^ args = ref new MainPageSizeChangedEventArgs(); 65 args->ViewState = ApplicationView::Value; 66 MainPageResized(this, args); 67 68 } 69 70 void MainPage::InvalidateSize() 71 { 72 // Get the window width 73 double windowWidth = this->ActualWidth; 74 75 if (windowWidth != 0.0) 76 { 77 // Get the width of the ListBox. 78 double listBoxWidth = Scenarios->ActualWidth; 79 80 // Is the ListBox using any margins that we need to consider? 81 double listBoxMarginLeft = Scenarios->Margin.Left; 82 double listBoxMarginRight = Scenarios->Margin.Right; 83 84 // Figure out how much room is left after considering the list box width 85 double availableWidth = windowWidth - listBoxWidth; 86 87 // Is the top most child using margins? 88 double layoutRootMarginLeft = ContentRoot->Margin.Left; 89 double layoutRootMarginRight = ContentRoot->Margin.Right; 90 91 // We have different widths to use depending on the view state 92 if (ApplicationView::Value != ApplicationViewState::Snapped) 93 { 94 // Make us as big as the the left over space, factoring in the ListBox width, the ListBox margins. 95 // and the LayoutRoot's margins 96 InputSection->Width = ((availableWidth) - 97 (layoutRootMarginLeft + layoutRootMarginRight + listBoxMarginLeft + listBoxMarginRight)); 98 } 99 else 100 { 101 // Make us as big as the left over space, factoring in just the LayoutRoot's margins. 102 if (autoSizeInputSectionWhenSnapped) 103 { 104 InputSection->Width = (windowWidth - (layoutRootMarginLeft + layoutRootMarginRight)); 105 } 106 } 107 } 108 InvalidateViewState(); 109 } 110 111 void MainPage::InvalidateViewState() 112 { 113 // Are we going to snapped mode? 114 if (ApplicationView::Value == ApplicationViewState::Snapped) 115 { 116 Grid::SetRow(DescriptionText, 3); 117 Grid::SetColumn(DescriptionText, 0); 118 119 Grid::SetRow(InputSection, 4); 120 Grid::SetColumn(InputSection, 0); 121 122 Grid::SetRow(FooterPanel, 2); 123 Grid::SetColumn(FooterPanel, 0); 124 } 125 else 126 { 127 Grid::SetRow(DescriptionText, 1); 128 Grid::SetColumn(DescriptionText, 1); 129 130 Grid::SetRow(InputSection, 2); 131 Grid::SetColumn(InputSection, 1); 132 133 Grid::SetRow(FooterPanel, 1); 134 Grid::SetColumn(FooterPanel, 1); 135 } 136 137 // Since we don't load the scenario page in the traditional manner (we just pluck out the 138 // input and output sections from the page) we need to ensure that any VSM code used 139 // by the scenario's input and output sections is fired. 140 VisualStateManager::GoToState(InputSection, "Input" + LayoutAwarePage::DetermineVisualState(ApplicationView::Value), false); 141 VisualStateManager::GoToState(OutputSection, "Output" + LayoutAwarePage::DetermineVisualState(ApplicationView::Value), false); 142 } 143 144 void MainPage::PopulateScenarios() 145 { 146 ScenarioList = ref new Platform::Collections::Vector<Object^>(); 147 148 // Populate the ListBox with the list of scenarios as defined in Constants.cpp. 149 for (unsigned int i = 0; i < scenarios->Length; ++i) 150 { 151 Scenario s = scenarios[i]; 152 ListBoxItem^ item = ref new ListBoxItem(); 153 item->Name = s.ClassName; 154 item->Content = (i + 1).ToString() + ") " + s.Title; 155 ScenarioList->Append(item); 156 } 157 158 // Bind the ListBox to the scenario list. 159 Scenarios->ItemsSource = ScenarioList; 160 Scenarios->ScrollIntoView(Scenarios->SelectedItem); 161 } 162 163 /// <summary> 164 /// This method is responsible for loading the individual input and output sections for each scenario. This 165 /// is based on navigating a hidden Frame to the ScenarioX.xaml page and then extracting out the input 166 /// and output sections into the respective UserControl on the main page. 167 /// </summary> 168 /// <param name="scenarioName"></param> 169 void MainPage::LoadScenario(String^ scenarioName) 170 { 171 autoSizeInputSectionWhenSnapped = true; 172 173 // Load the ScenarioX.xaml file into the Frame. 174 TypeName scenarioType = {scenarioName, TypeKind::Custom}; 175 HiddenFrame->Navigate(scenarioType, this); 176 177 // Get the top element, the Page, so we can look up the elements 178 // that represent the input and output sections of the ScenarioX file. 179 Page^ hiddenPage = safe_cast<Page^>(HiddenFrame->Content); 180 181 // Get each element. 182 UIElement^ input = safe_cast<UIElement^>(hiddenPage->FindName("Input")); 183 UIElement^ output = safe_cast<UIElement^>(hiddenPage->FindName("Output")); 184 185 if (input == nullptr) 186 { 187 // Malformed input section. 188 NotifyUser("Cannot load scenario input section for " + scenarioName + 189 " Make sure root of input section markup has x:Name of 'Input'", NotifyType::ErrorMessage); 190 return; 191 } 192 193 if (output == nullptr) 194 { 195 // Malformed output section. 196 NotifyUser("Cannot load scenario output section for " + scenarioName + 197 " Make sure root of output section markup has x:Name of 'Output'", NotifyType::ErrorMessage); 198 return; 199 } 200 201 // Find the LayoutRoot which parents the input and output sections in the main page. 202 Panel^ panel = safe_cast<Panel^>(hiddenPage->FindName("LayoutRoot")); 203 204 if (panel != nullptr) 205 { 206 unsigned int index = 0; 207 UIElementCollection^ collection = panel->Children; 208 209 // Get rid of the content that is currently in the intput and output sections. 210 collection->IndexOf(input, &index); 211 collection->RemoveAt(index); 212 213 collection->IndexOf(output, &index); 214 collection->RemoveAt(index); 215 216 // Populate the input and output sections with the newly loaded content. 217 InputSection->Content = input; 218 OutputSection->Content = output; 219 220 ScenarioLoaded(this, nullptr); 221 } 222 else 223 { 224 // Malformed Scenario file. 225 NotifyUser("Cannot load scenario: " + scenarioName + ". Make sure root tag in the '" + 226 scenarioName + "' file has an x:Name of 'LayoutRoot'", NotifyType::ErrorMessage); 227 } 228 } 229 230 void MainPage::Scenarios_SelectionChanged(Object^ sender, SelectionChangedEventArgs^ e) 231 { 232 if (Scenarios->SelectedItem != nullptr) 233 { 234 NotifyUser("", NotifyType::StatusMessage); 235 236 LoadScenario((safe_cast<ListBoxItem^>(Scenarios->SelectedItem))->Name); 237 InvalidateSize(); 238 } 239 } 240 241 void MainPage::NotifyUser(String^ strMessage, NotifyType type) 242 { 243 switch (type) 244 { 245 case NotifyType::StatusMessage: 246 // Use the status message style. 247 StatusBlock->Style = safe_cast<Windows::UI::Xaml::Style^>(this->Resources->Lookup("StatusStyle")); 248 break; 249 case NotifyType::ErrorMessage: 250 // Use the error message style. 251 StatusBlock->Style = safe_cast<Windows::UI::Xaml::Style^>(this->Resources->Lookup("ErrorStyle")); 252 break; 253 default: 254 break; 255 } 256 StatusBlock->Text = strMessage; 257 258 // Collapsed the StatusBlock if it has no text to conserve real estate. 259 if (StatusBlock->Text != "") 260 { 261 StatusBlock->Visibility = Windows::UI::Xaml::Visibility::Visible; 262 } 263 else 264 { 265 StatusBlock->Visibility = Windows::UI::Xaml::Visibility::Collapsed; 266 } 267 } 268 269 void MainPage::Footer_Click(Object^ sender, RoutedEventArgs^ e) 270 { 271 auto uri = ref new Uri((String^)((HyperlinkButton^)sender)->Tag); 272 Windows::System::Launcher::LaunchUriAsync(uri); 273 } 274 275 276 /// <summary> 277 /// Populates the page with content passed during navigation. Any saved state is also 278 /// provided when recreating a page from a prior session. 279 /// </summary> 280 /// <param name="navigationParameter">The parameter value passed to 281 /// <see cref="Frame::Navigate(Type, Object)"/> when this page was initially requested. 282 /// </param> 283 /// <param name="pageState">A map of state preserved by this page during an earlier 284 /// session. This will be null the first time a page is visited.</param> 285 void MainPage::LoadState(Object^ navigationParameter, IMap<String^, Object^>^ pageState) 286 { 287 (void) navigationParameter; // Unused parameter 288 289 PopulateScenarios(); 290 291 // Starting scenario is the first or based upon a previous state. 292 ListBoxItem^ startingScenario = nullptr; 293 int startingScenarioIndex = -1; 294 295 if (pageState != nullptr && pageState->HasKey("SelectedScenarioIndex")) 296 { 297 startingScenarioIndex = safe_cast<int>(pageState->Lookup("SelectedScenarioIndex")); 298 } 299 300 Scenarios->SelectedIndex = startingScenarioIndex != -1 ? startingScenarioIndex : 0; 301 302 InvalidateViewState(); 303 } 304 305 /// <summary> 306 /// Preserves state associated with this page in case the application is suspended or the 307 /// page is discarded from the navigation cache. Values must conform to the serialization 308 /// requirements of <see cref="SuspensionManager::SessionState"/>. 309 /// </summary> 310 /// <param name="pageState">An empty map to be populated with serializable state.</param> 311 void MainPage::SaveState(IMap<String^, Object^>^ pageState) 312 { 313 int selectedListBoxItemIndex = Scenarios->SelectedIndex; 314 pageState->Insert("SelectedScenarioIndex", selectedListBoxItemIndex); 315 } 316