Using D3DImage on Remote Desktop (RDP)

As of .NET 4.5, WPF’s D3DImage is now finally capable of rendering in RDP. This post will be demonstrating the changes that are needed to render.

D3DImage.SetBackBuffer Method (D3DResourceType, IntPtr, Boolean) was overloaded to include a fallback to software rendering.

public void SetBackBuffer(
	D3DResourceType backBufferType,
	IntPtr backBuffer,
	bool enableSoftwareFallback
)

enableSoftwareFallback
Type: System.Boolean
true to fall back on software rendering; otherwise, false.

Microsoft added some information in their Remarks section that concerns us to successfully implement D3DImage to work in RDP. However it doesn’t explain everything to get D3DImage to work successfully on RDP.

When you call SetBackBuffer(D3DResourceType, IntPtr, Boolean) with the enableSoftwareFallback parameter set to true, the rendering system retains its reference to the back buffer when the front buffer becomes unavailable, so there is no need to call SetBackBuffer when the front buffer is available again. There may be situations where the user’s device becomes unavailable. When that occurs, call SetBackBuffer to release WPF’s reference to the back buffer. If you need to reset your device, call SetBackBuffer with backBuffer set to null, and then call SetBackBuffer again with backBuffer set to a valid Direct3D surface.

When using Remote Desktop, the front buffer becomes unavailable after rendering a single frame. It’s recommended not to release the reference to the back buffer as the front buffer will not become available again even if you release the reference to the back buffer using SetBackBuffer. Note that even if the IsFrontBufferAvailable flag is stating that it is unavailable, you’ll still be able to render properly. The trick is to simply ignore the flag.

Using the Introduction to D3DImage as a example, here are some couple changes that need to occur.

//We don't need to listen to the event anymore
//_di.IsFrontBufferAvailableChanged += OnIsFrontBufferAvailableChanged;

You’ll also need to remove all instances of the IsFrontBufferAvailable flag when rendering.

private void UpdateScene()
{
    //if (_di.IsFrontBufferAvailable && _scene != IntPtr.Zero)
    if (_scene != IntPtr.Zero)
    {
        // lock the D3DImage
        _di.Lock();
 
        // update the scene 
        // (this is a call into our custom unmanaged library)
        SIZE size = new SIZE();
        RenderScene(size);
 
        // invalidate the updated rect of the D3DImage (in this case, the 
        // whole image)
        _di.AddDirtyRect(new Int32Rect(0, 0, size.Width, size.Height));
 
        // unlock the D3DImage
        _di.Unlock();
    }
}

Sending event messages from C# D3DImage to C++ Direct3D window

If you’re integrating a C++ Direct3D HWND into WPF via D3DImage, and you were handling a lot of messages in your C++ WndProc, then fear not as you will not have to implement each WPF event individually and translate them to a Win32 event. Instead, you can add a hook to a C# WndProc that will live alongside other WPF events.

using System;
using System.Windows;
using System.Windows.Interop;

namespace WpfApplication1
{
    public partial class Window1 : Window
    {
        private D3DImage d3dImage;

        public Window1()
        {
            // Initialize d3dImage scene, frontbufferavailable, etc

            InitializeComponent();
        }
        
        // ...

        protected override void OnSourceInitialized(EventArgs e)
        {
            base.OnSourceInitialized(e);
            HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
            source.AddHook(WndProc);
        }

        private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            // Send message to C++ via P/Invoke
            PInvokeSendMessage((UIntPtr)msg, wParam, lParam);

            return IntPtr.Zero;
        }
    }
}

The beauty is that you will still be able to override every other WPF event (such as PreviewKeyDown, KeyDown, etc) as well. The order that they will be called is as follows: PreviewKeyDown -> KeyDown -> WndProc (WM_KEYDOWN). Therefore it’s possible to make decisions in the regular WPF events to decide whether the event should be sent to your Direct3D engine in C++. As you might not want to send mouse events when the focus isn’t directly on your D3DImage.

Remove the hook when the Window is Closing.

Animated Switch ToggleButton Style in WPF

This post will show you how to implement an animated switch in WPF. This was done with inspiration from uilang.com‘s animated switch and from iOS.

switch

The animation’s keysplines were determined using this tool: http://www.carto.net/papers/svg/samples/keysplines.svg

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" 
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <Page.Resources>
      <ResourceDictionary>
         <Style x:Key="AnimatedSwitch" TargetType="{x:Type ToggleButton}">
            <Setter Property="Foreground" Value="Black" />
            <Setter Property="Background" Value="#FAFAFB" />
            <Setter Property="BorderBrush" Value="#EAEAEB" />
            <Setter Property="Template">
               <Setter.Value>
                  <ControlTemplate TargetType="ToggleButton">
                     <Viewbox Stretch="Uniform">
                        <Canvas Name="Layer_1" Width="20" Height="20" Canvas.Left="10" Canvas.Top="0">
                           <Ellipse  Canvas.Left="0" Width="20" Height="20" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="0.5"/>
                           <Ellipse  Canvas.Left="15" Width="20" Height="20" Fill="{TemplateBinding Background}" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="0.5"/>
                           <Border Canvas.Left="10" Width="15" Height="20" Name="rect416927" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0,0.5,0,0.5"/>
                           <Ellipse x:Name="ellipse"  Canvas.Left="0" Width="20" Height="20" Fill="White" Stroke="{TemplateBinding BorderBrush}" StrokeThickness="0.3">
                              <Ellipse.RenderTransform>
                                 <TranslateTransform X="0" Y="0" />
                              </Ellipse.RenderTransform>
                              <Ellipse.BitmapEffect>
                                 <DropShadowBitmapEffect Softness="0.1" ShadowDepth="0.7" Direction="270" Color="#BBBBBB"/>
                              </Ellipse.BitmapEffect>
                           </Ellipse>
                        </Canvas>
                     </Viewbox>
                     <ControlTemplate.Triggers>
                        <Trigger Property="IsChecked" Value="True" >
                           <Trigger.EnterActions>
                              <BeginStoryboard>
                                 <Storyboard>
                                    <ColorAnimation Storyboard.TargetProperty="Background.Color" To="#52D468" Duration="0:0:0.2" />
                                    <ColorAnimation Storyboard.TargetProperty="BorderBrush.Color" To="#41C955" Duration="0:0:0.2" />
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" Storyboard.TargetName="ellipse">
                                       <SplineDoubleKeyFrame KeyTime="0" Value="0"/>
                                       <SplineDoubleKeyFrame KeyTime="0:0:0.4" Value="15" KeySpline="0, 1, 0.6, 1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                 </Storyboard>
                              </BeginStoryboard>
                           </Trigger.EnterActions>
                           <Trigger.ExitActions>
                              <BeginStoryboard>
                                 <Storyboard>
                                    <ColorAnimation Storyboard.TargetProperty="Background.Color" To="#FAFAFB" Duration="0:0:0.2" />
                                    <ColorAnimation Storyboard.TargetProperty="BorderBrush.Color" To="#EAEAEB" Duration="0:0:0.2" />
                                    <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" Storyboard.TargetName="ellipse">
                                       <SplineDoubleKeyFrame KeyTime="0" Value="15"/>
                                       <SplineDoubleKeyFrame KeyTime="0:0:0.3" Value="0" KeySpline="0, 0.5, 0.5, 1"/>
                                    </DoubleAnimationUsingKeyFrames>
                                 </Storyboard>
                              </BeginStoryboard>
                           </Trigger.ExitActions>
                        </Trigger>
                     </ControlTemplate.Triggers>
                  </ControlTemplate>
               </Setter.Value>
            </Setter>
         </Style>
      </ResourceDictionary>
   </Page.Resources>
   <Grid>
      <ToggleButton Style="{StaticResource AnimatedSwitch}" Height="80"/>
   </Grid>
</Page>

Material Design Shadows in WPF

This post will demonstrate how to implement Google’s new Material Design principles in WPF. First, we’ll tackle the Cards/Modal shadows, seen below.


Their shadows consist of two layers: a top shadow for depth and a bottom shadow for definition.

Thankfully, it’s possible to achieve a similar effect in WPF by using only one DropShadowEffect on a Border.

Cards-DropShadowEffect-WPF

<Page
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
   <Page.Resources>
      <DropShadowEffect x:Key="z-depth1" BlurRadius="5" ShadowDepth="1" Direction="270" Color="#CCCCCC"/>
      <DropShadowEffect x:Key="z-depth2" BlurRadius="8" ShadowDepth="2.5" Direction="270" Color="#BBBBBB"/>
      <DropShadowEffect x:Key="z-depth3" BlurRadius="14" ShadowDepth="4.5" Direction="270" Color="#BBBBBB"/>
      <DropShadowEffect x:Key="z-depth4" BlurRadius="25" ShadowDepth="8" Direction="270" Color="#BBBBBB"/>
      <DropShadowEffect x:Key="z-depth5" BlurRadius="35" ShadowDepth="13" Direction="270" Color="#BBBBBB"/>
   </Page.Resources>
   <Grid Background="#EEEEEE">
      <StackPanel Orientation="Horizontal">
         <Border Effect="{StaticResource z-depth1}" Width="100" Height="100" Background="White" Margin="25" />
         <Border Effect="{StaticResource z-depth2}" Width="100" Height="100" Background="White" Margin="25" />
         <Border Effect="{StaticResource z-depth3}" Width="100" Height="100" Background="White" Margin="25" />
         <Border Effect="{StaticResource z-depth4}" Width="100" Height="100" Background="White" Margin="25" />
         <Border Effect="{StaticResource z-depth5}" Width="100" Height="100" Background="White" Margin="25" />
      </StackPanel>
   </Grid>
</Page>

To use with a Left/Right Nav or Bottom Toolbar, simply change the Direction of the DropShadowEffect to the appropriate angle (right 0, left 180, top 90, bottom 270).

If you’re interested in reproducing their shadows using two DropShadowEffects, use two controls stacked on an empty Grid or Canvas and apply the effect on both..