Bagikan melalui


Step 1: Create and initialize a view

[ This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation ]

In this step, you create and initialize the CoreApplicationView that configures the basic settings for a display window, and you 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. This includes the display area and the input behaviors, plus the thread that the app uses for processing. You create a view by instantiating a factory that returns a view for your app, with the view 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 four 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.

Note  The code used in this guidance is taken from the DirectX App template in Microsoft Visual Studio 2013, and can be found in the files App.h, App.cpp, DeviceResources.h, and DeviceResources.cpp.

 

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. The factory is declared in your header file like this:

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. The ref operator indicates a reference class. Reference classes should only be used for types that will be interoperating with DirectX.

 

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 DirectX App template that ships with Visual Studio 2013.

ref class App sealed : public Windows::ApplicationModel::Core::IFrameworkView
    {
    public:
        App();

        // 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:
        // App lifecycle event handlers
        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);

        // Window event handlers
        void OnWindowSizeChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::WindowSizeChangedEventArgs^ args);
        void OnVisibilityChanged(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::VisibilityChangedEventArgs^ args);
        void OnWindowClosed(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::CoreWindowEventArgs^ args);

        // Display properties event handlers
        void OnDpiChanged(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args);
        void OnOrientationChanged(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args);
        void OnDisplayContentsInvalidated(Windows::Graphics::Display::DisplayInformation^ sender, Platform::Object^ args);

    private:
        std::shared_ptr<DeviceResources> m_deviceResources;
        std::unique_ptr<MyDirectXAppMain> m_main;
        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.

// The first method called when the IFrameworkView is being created.
void App::Initialize(CoreApplicationView^ applicationView)
{
    // Register event handlers for app lifecycle. This example includes Activated, so that we
    // can make the CoreWindow active and start rendering on the window.
    applicationView->Activated +=
        ref new TypedEventHandler<CoreApplicationView^, IActivatedEventArgs^>(this, &App::OnActivated);

    CoreApplication::Suspending +=
        ref new EventHandler<SuspendingEventArgs^>(this, &App::OnSuspending);

    CoreApplication::Resuming +=
        ref new EventHandler<Platform::Object^>(this, &App::OnResuming);

    // At this point we have access to the device. 
    // We can create the device-dependent resources.
    m_deviceResources = std::make_shared<DeviceResources>();
}

Here, you've connected your handlers to the critical Process Lifetime Management (PLM) events for a Windows Store app:

  • App activation, when the app cold starts
  • App suspension, when the user dismisses the app
  • App resume, when the user returns the app to the foreground

You've also created the renderer that you'll use to draw to the screen.

In the SetWindow method, you configure the window and display behaviors. This method is called any time the CoreWindow is changed in some way.

// Called when the CoreWindow object is created (or re-created).
void App::SetWindow(CoreWindow^ window)
{
    window->SizeChanged += 
        ref new TypedEventHandler<CoreWindow^, WindowSizeChangedEventArgs^>(this, &App::OnWindowSizeChanged);

    window->VisibilityChanged +=
        ref new TypedEventHandler<CoreWindow^, VisibilityChangedEventArgs^>(this, &App::OnVisibilityChanged);

    window->Closed += 
        ref new TypedEventHandler<CoreWindow^, CoreWindowEventArgs^>(this, &App::OnWindowClosed);

    DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();

    currentDisplayInformation->DpiChanged +=
        ref new TypedEventHandler<DisplayInformation^, Object^>(this, &App::OnDpiChanged);

    currentDisplayInformation->OrientationChanged +=
        ref new TypedEventHandler<DisplayInformation^, Object^>(this, &App::OnOrientationChanged);

    DisplayInformation::DisplayContentsInvalidated +=
        ref new TypedEventHandler<DisplayInformation^, Object^>(this, &App::OnDisplayContentsInvalidated);

    // Disable all pointer visual feedback for better performance when touching.
    auto pointerVisualizationSettings = PointerVisualizationSettings::GetForCurrentView();
    pointerVisualizationSettings->IsContactFeedbackEnabled = false; 
    pointerVisualizationSettings->IsBarrelButtonFeedbackEnabled = false;

    m_deviceResources->SetWindow(window);
}

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 Direct3D swap chain. You can use the Windows::Graphics::Display::DisplayInformation type to store and access information about the current state of the display, and set your resources accordingly.

The DirectX initialization code in the renderer looks like this, if you're curious. Likewise, your own renderer must take a reference to the app's CoreWindow. It should also have similar steps for:

  • Creating the graphics device resources, including the swap chain
  • Reconfiguring and rebuilding those resources if the window size or orientation changes, or if the graphics device is lost (or changes)
void DeviceResources::SetWindow(CoreWindow^ window)
{
    m_window = window;
    
    // SetDpi() will call CreateWindowSizeDependentResources()
    // if those resources have not been created yet.
    SetDpi(DisplayInformation::GetForCurrentView()->LogicalDpi);

    UpdateForWindowSizeChange();
}
void DeviceResources::UpdateForWindowSizeChange()
{
    DisplayInformation^ currentDisplayInformation = DisplayInformation::GetForCurrentView();

    if (m_swapChainPanel == nullptr && (
            m_window->Bounds.Width  != m_outputSize.Width ||
            m_window->Bounds.Height != m_outputSize.Height
        ) || m_swapChainPanel != nullptr && (
            m_swapChainPanel->ActualWidth != m_outputSize.Width ||
            m_swapChainPanel->ActualHeight != m_outputSize.Height
        ) || m_orientation != currentDisplayInformation->CurrentOrientation)

    {
        CreateWindowSizeDependentResources();
    }
}

You'll see how to implement CreateWindowSizeDeviceResources 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. Here, we create a pointer to the main app object and assign it to a global variable for later access.

// Initializes scene resources, or loads a previously saved app state.
void App::Load(Platform::String^ entryPoint)
{
        m_main = std::unique_ptr<MyDirectXAppMain>(new MyDirectXAppMain(m_deviceResources));
}

Run creates and starts 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 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.

// This method is called after the window becomes active.
void App::Run()
{
    while (!m_windowClosed)
    {
        if (m_windowVisible)
        {
            CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);

            m_main->Update();

            if (m_main->Render())
            {
                m_deviceResources->Present();
            }
        }
        else
        {
            CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending);
        }
    }
}

When you start your app, the application factory calls these four 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 while the app is in the foreground.

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 this example.

// Required for IFrameworkView.
// Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView
// class is torn down while the app is in the foreground.
void App::Uninitialize()
{
}

We cover the further implementation of the specific DeviceResources methods in Connect the DirectX swap chain.

Now, you configured your view provider object and specified the implementation of the five 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 handlers

How to set up your DirectX Windows Store app to display a view

Complete code for a DirectX Windows Store app