Skip to content

Instantly share code, notes, and snippets.

@robmikh
Last active June 18, 2020 02:10
Show Gist options
  • Save robmikh/8cfa063cdec2b477e43ce99d733767ee to your computer and use it in GitHub Desktop.
Save robmikh/8cfa063cdec2b477e43ce99d733767ee to your computer and use it in GitHub Desktop.
On Framework-less Apps

So you can write an app using XAML, WinJS, and DirectX. But is there another option? There is! And it's actually what all of the other more traditional options do under the covers. At the end of the day, some Composition content needs to be hooked up to a CoreWindow. The first step is to create a main function that will start our CoreApplication:

static int Main(string[] args)
{
    CoreApplication.Run(/* Some IFrameworkViewSource here */);

    return 0;
}

The next step is to implement the IFrameworkViewSource interface. The IFrameworkViewSource interface isn't that exciting, and it's mainly just a vehicle to create an IFrameworkView which will actually contain your application. This is done so that you can choose which IFrameworkView you want to show, or if you have some special way you want to initialize your object. Here is a simple implementation of an IFrameworkViewSource: (You can also find another example here)

public sealed class MainViewFactory : IFrameworkViewSource
{
    IFrameworkView IFrameworkViewSource.CreateView()
    {
        return new MainView();
    }

    static int Main(string[] args)
    {
        CoreApplication.Run(new MainViewFactory());

        return 0;
    }
}

Here we've moved our main to within the MainViewFactory and passed in an instance of our factory. The CoreApplication will call our CreateView method, which will create our MainView which implements IFrameworkView. The job of our view will be to present our content as well as hold onto our CoreApplicationView which will be given to us by the caller of our Initialize method.

class MainView : IFrameworkView
{
    void IFrameworkView.Initialize(CoreApplicationView view)
    {
        _view = view;
    }
    
    void IFrameworkView.SetWindow(CoreWindow window)
    {
        _window = window;
        _compositor = new Compositor();
        _root = _compositor.CreateContainerVisual();
        _compositionTarget = _compositor.CreateTargetForCurrentView();
        _compositionTarget.Root = _root;
        
        _spriteVisual = _compositor.CreateSpriteVisual();
        _spriteVisual.Size = new Vector2((float)_window.Bounds.Width, (float)_window.Bounds.Height);
        _spriteVisual.Brush = _compositor.CreateColorBrush(Colors.Red);
        
        _root.Children.InsertAtTop(_spriteVisual);
        
        _window.SizeChanged += (s, a) => 
        {
            _spriteVisual.Size = new Vector2((float)_window.Bounds.Width, (float)_window.Bounds.Height);
        };
    }
    
    void IFrameworkView.Load(string arg)
    {
    }
    
    void IFrameworkView.Run()
    {
        _window.Activate();
        _window.Dispatcher.ProcessEvents(CoreProcessEventOption.ProcessUntilQuit);
    }
    
    private CoreWindow _window;
    private CoreApplicationView _view;
    private Compositor _compositor;
    private ContainerVisual _root;
    private SpriteVisual _spriteVisual;
    private CompositionTarget _compositionTarget;
}

In order to attach our composition content to our CoreWindow and to the greater visual tree, we need to create a CompositionTarget from our current CoreApplicationView. Whatever we set as the root of our CompositionTarget will now become the root of our visual tree for this CoreWindow. All that's left is to activate our CoreWindow and then to tell the CoreDispatcher to start processing messages (in terms of a Win32k program, this is essentially our main window message loop). And that's the basis for all UWP applications under the hood. Instead of creating Composition content you could instead use DirectX to create a swapchain for our current view.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment