In this step, you create and initialize the CoreApplicationView that configures the basic settings for a display window, and obtain the CoreWindow object that controls the input events on that view.
You can think of a view as the collection of user interface settings for an app, including the display area and the input behaviors, plus the thread it uses for processing. You create a view by instantiating a factory that returns a view for your app initialized and configured with the settings you need (including the DirectX swap chain).
Here's the process:
- Define the view provider factory and the view provider object.
- Implement the 4 public methods to create a new window for a your app: Initialize, SetWindow, Load, and Run.
- Implement the public method that disposes of the window: Uninitialize.
- Set the application factory to invoke your view provider factory and generate a view provider for your app.
- Tell the app singleton to run the factory with CoreApplication.Run.
Instructions
First, you create a factory for the view, which creates instances of the IFrameworkView object that define the view.
The view provider factory is Direct3DApplicationSource, and it implements IFrameworkViewSource. The IFrameworkViewSource interface has a single method, CreateView, defined on it. This method is called by the app singleton when we pass a reference to Direct3DApplicationSource to CoreApplication.Run.
The implementation of this factory is simple: we return a reference to a new instance of your app object that implements IFrameworkView.
ref class Direct3DApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource { public: virtual Windows::ApplicationModel::Core::IFrameworkView^ CreateView(); };
Note One syntactical feature that might have caught your eye is the "^" operator, also called the hat operator. This new operator enables the automatic deletion of runtime objects by means of reference counting, without having to implement the process yourself.
Of course, you also need to define your app's main object and the implementation of IFrameworkView. This code for the definition of your app object is created for your app as part of the Direct3D App template that ships with Microsoft Visual Studio Express 2012 for Windows 8, Microsoft Visual Studio Professional 2012, and Microsoft Visual Studio Ultimate 2012.
ref class MyDirectXApp sealed : public Windows::ApplicationModel::Core::IFrameworkView { public: MyDirectXApp(); // IFrameworkView Methods. virtual void Initialize(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView); virtual void SetWindow(Windows::UI::Core::CoreWindow^ window); virtual void Load(Platform::String^ entryPoint); virtual void Run(); virtual void Uninitialize(); protected: // Event Handlers. void OnWindowSizeChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ args); void OnActivated(Windows::ApplicationModel::Core::CoreApplicationView^ applicationView, Windows::ApplicationModel::Activation::IActivatedEventArgs^ args); void OnSuspending(Platform::Object^ sender, Windows::ApplicationModel::SuspendingEventArgs^ args); void OnResuming(Platform::Object^ sender, Platform::Object^ args); void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args); void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args); void OnPointerPressed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); void OnPointerMoved(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::PointerEventArgs^ args); private: MyRenderer^ m_renderer; // implementation left to reader. bool m_windowClosed; bool m_windowVisible; };
You can also create your own view provider implementation. If you choose to create your own, keep the following things in mind:
- Your goal is to provide a "view" that the CoreWindow instance for your app can use to draw its state to the display in a way that represents your app. By itself, CoreWindow has no way to draw to the screen. Rather, it's a threaded collection of events and system behaviors that represent user interaction. It's up to you to draw the results of those events and behaviors, and the view provider is the method by which you do just that.
- You have direct access to DirectX, just like you would in a desktop app. To accomplish your goal of providing a view for the CoreWindow, you need to find a way to connect the CoreWindow events and behaviors you care about to DirectX, and ultimately draw those results into the swap chain for presentation.
- The template for connecting CoreWindow to DirectX is your implementation of the IFrameworkView interface, which you pass to the app object when your app starts. Specifically, when you initialize the renderer (when the app object calls your implementation of IFrameworkView::SetWindow), you pass a reference to the app's CoreWindow to the renderer, and then pass that reference to IDXGIFactory2::CreateSwapChainForCoreWindow after you've configured your device context.
Short form: CoreWindow represents the basic UI events you want, like input and screen changes. DirectX is the way you draw to the screen. Your implementation of IFrameworkView is the way you bring them together, by creating a view of these UI events drawn by DirectX. It's up to you to draw all the pixels in the way you want, in response to the events raised on the CoreWindow.
Note As an aside: ComPtr, a type in the Windows Runtime, enables you to manage the lifetime of COM objects. Use this type whenever you are creating variables for projected legacy COM objects, like many DirectX objects.
This implementation of a view provider uses three custom methods defined on the Direct3DBase class and provided as part of the Direct3D App Visual Studio template:
- CreateDeviceResources
- CreateWindowSizeDependentResources
- Render
These methods are specific to the implementation discussed here, and show some important steps.
To keep things simple, your renderer should inherit the Direct3DBase class and call the base implementations of these methods. We'll review the implementation of the Direct3DBase methods in Step 3: Connect the DirectX swap chain.
Now, let's look at these 5 public methods, as declared on your view provider class:
- Initialize
- SetWindow
- Load
- Run
- Uninitialize
The app object expects to find these 5 methods on any view provider instance it receives. In order, it calls these methods to create the resources for the view, set the CoreWindow up to use the view, load any additional resources, run the app, and then dispose of it.
Now, you implement the 5 public methods from the IFrameworkView interface.
First, in Initialize, you initialize the renderer for your view, which is the object that performs the DirectX drawing. You also assign the handler for the view activation event, which obtains the CoreWindow for the app when CoreApplicationView::Activated is raised. You also hook up the handlers for the app suspend and resume events.
Here's how you might implement Initialize.
void MyDirectXApp::Initialize(CoreApplicationView^ applicationView) { applicationView->Activated += ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &MyDirectXApp::OnActivated); CoreApplication::Suspending += ref new EventHandler<SuspendingEventArgs^>(this, &MyDirectXApp::OnSuspending); CoreApplication::Resuming += ref new EventHandler<Platform::Object^>(this, &MyDirectXApp::OnResuming); m_renderer = ref new MyAppRenderer(); // MyAppRenderer is assumed to be derived from Direct3DBase. }
Here, you've connected your handlers to the critical Process Lifetime Management (PLM) events for a Windows Store app: app activation (when it cold starts), app suspension (when the user dismisses it), and app resume (when the user returns it to the foreground). You've also created the renderer that you'll use to draw to the screen.
In the SetWindow method, you actually configure the window and display behaviors. This method is called any time the CoreWindow is changed in some way.
void MyDirectXApp::SetWindow(CoreWindow^ window) { window->SizeChanged += ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &MyDirectXApp::OnWindowSizeChanged); window->VisibilityChanged += ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &MyDirectXApp::OnVisibilityChanged); window->Closed += ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &MyDirectXApp::OnWindowClosed); window->PointerPressed += ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MyDirectXApp::OnPointerPressed); window->PointerMoved += ref new TypedEventHandler<CoreWindow^, PointerEventArgs^>(this, &MyDirectXApp::OnPointerMoved); window->PointerCursor = ref new CoreCursor(CoreCursorType::Arrow, 0); m_renderer->Initialize(CoreWindow::GetForCurrentThread()); // Initialize is implemented on Direct3DBase. }
The CoreWindow for your app is assigned to the renderer by calling the Initialize method of your renderer object and passing it the results of CoreWindow::GetForCurrentThread. This is important, as it is the point where you explicitly connect the CoreWindow to your DirectX swap chain. (We'll get to the code for actually making the connection when we discuss the Direct3DBase::Initialize implementation.)
The DirectX initialization code in the renderer looks like this, if you're curious. It assigns a reference for the CoreWindow returned by CoreWindow::GetForCurrentThread to a global variable for the renderer object, which all the methods in the renderer object can use to get info about UI events, including the Direct3DBase::Present method discussed later. Your own renderer must take a reference to the app's CoreWindow, and should also have a similar step for creating the graphics device resources, including the swap chain, and for reconfiguring and rebuilding those resources if the window size or orientation changes. This tutorial provides the implementation specific to the Direct3D App template.
// Initialize the Direct3D resources required to run. void Direct3DBase::Initialize(CoreWindow^ window) { m_window = window; CreateDeviceResources(); CreateWindowSizeDependentResources(); }
You'll see how to implement CreateDeviceResources and CreateDeviceResources in Step 3: Connect the DirectX swap chain.
Now, you initialized the renderer object with the CoreWindow for your app, created all the graphics resources you need to draw to the screen, and added event handlers for the basic set of input events for that window. Let's go to the next step.
In the Load method, you can optionally load any external or secondary resources for your app, and perform any app state or cache initialization. In this case, your app has no assets to load, or any real state, so we can leave it blank.
void MyDirectXApp::Load(Platform::String^ entryPoint)
{
}
Run creates and starts a new timer to use when processing and rendering your graphics updates. It also creates the main processing loop for your app, and invokes the event dispatcher from the current app singleton with a call to CoreDispatcher::ProcessEvents on every iteration. When you call CoreDispatcher::ProcessEvents, you process all event messages that have arrived since the last call. Be aware that you should always use CoreProcessEventsOption::ProcessAllIfPresent as the message queue behavior for your Windows Store apps built for Windows using DirectX with C++, as the message dispatch behavior it indicates won't impact your overall graphics performance.
(For more info about event dispatcher behaviors, read The app object and DirectX.)
Here, you also render the initial Direct3D output and present it to the window's view.
void MyDirectXApp::Run() { BasicTimer^ timer = ref new BasicTimer(); while (!m_windowClosed) { if (m_windowVisible) { timer->Update(); CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); m_renderer->Update(timer->Total, timer->Delta); m_renderer->Render(); m_renderer->Present(); // This call is synchronized to the display frame rate. } else { CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending); } } }
When you start your app, the application factory calls these 4 methods in order and starts your app's processing loop, which runs on the main app thread. There's still one more method you must implement, though; the one called when the view provider object is disposed.
Uninitialize unloads and cleans up any display, cache, and asset resources we created or loaded in the Load method. You can leave it empty for this for the purposes of the example, because your Load method is also blank:
void MyDirectXApp::Uninitialize()
{
}
We cover the further implementation of the Direct3DBase renderer methods in Connect the DirectX swap chain.
Now, you configured your view provider object and specified the implementation of the 5 methods that define the provider. That finished, it's time for you to create the view itself from the main method of the app.
To do that, you create an instance of the view provider factory when you start the application from your main method, as seen here:
// Start the app. [Platform::MTAThread] int main(Platform::Array<Platform::String^>^) { auto direct3DApplicationSource = ref new Direct3DApplicationSource(); CoreApplication::Run(direct3DApplicationSource); return 0; }
When your app starts, it creates the view provider factory, which the CoreApplication.Run method uses to create a new view provider object and calls the implementations of Initialize, SetWindow,Load, and Run.
Next step
Set up the event handlersRelated topics
- How to set up your DirectX Windows Store app to display a view
- Complete code for a DirectX Windows Store app
Build date: 3/11/2013