tags:

views:

11

answers:

1

My checkbox is rendered without a check mark. If I use only one checkbox (instance object) to render then I can get the check mark to show, but I cannot use this solution. I need be able to render it using the local checkbox. This checkbox has the Aero theme applied to it via "/PresentationFramework.Aero;component/themes/Aero.NormalColor.xaml" and this is also a must have for me. Other themes built-in themes like Royal, and Luna render with a check mark.

XAML

<Canvas Name="canvas">
  <Button Click="Button_Click" Canvas.Left="440" Canvas.Top="277">
      Do Paint
  </Button>
  <Image Name="image"/>
</Canvas> 

C#

private void Button_Click(object sender, RoutedEventArgs e) {
  var checkBox = new CheckBox { Width = 100, Height = 30, IsChecked = true, };
  var rectangle = new Rect(0, 0, checkBox.Width, checkBox.Height);

  //need this
  var visualBrush = new VisualBrush(checkBox);
  checkBox.Arrange(rectangle);

  var renderTargetBitmap = new RenderTargetBitmap((int)rectangle.Width, (int)rectangle.Height, 96, 96, PixelFormats.Default);
  renderTargetBitmap.Render(checkBox);
  renderTargetBitmap.Freeze();

  image.Source = renderTargetBitmap;
}
A: 

I used Reflector to look at the content of the PresentationFramework.Aero assembly, and I found out what's going on here. I have a solution, but you're probably not going to like it...

In the Aero theme, checkboxes are rendered with a BulletChrome control. This control uses animations when it switches from one state to another, so when you render the checkbox, the animation from unchecked to checked is not complete, and the tick mark is not yet visible. So you need to wait for the animation to complete...

Just putting a Thread.Sleep before rendering won't work, because while the dispatcher thread is sleeping, the animation will be stopped. So you need to start a new thread where you wait before rendering the checkbox:

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        var checkBox = new CheckBox { Width = 100, Height = 30, IsChecked = true, };
        var rectangle = new Rect(0, 0, checkBox.Width, checkBox.Height);

        //need this
        var visualBrush = new VisualBrush(checkBox);
        checkBox.Arrange(rectangle);

        new Thread(() =>
            {
                Thread.Sleep(300); // the animation lasts 300ms
                Dispatcher.Invoke(() =>
                                {
                                    var renderTargetBitmap =
                                        new RenderTargetBitmap((int)rectangle.Width,
                                                            (int)rectangle.Height, 96, 96,
                                                            PixelFormats.Default);
                                    renderTargetBitmap.Render(checkBox);
                                    renderTargetBitmap.Freeze();

                                    image.Source = renderTargetBitmap;
                                });
            }).Start();
    }

(this code uses the DispatcherExtensions.Invoke extension method, so you need to reference the System.Windows.Presentation assembly and import the System.Windows.Threading namespace)

Note that the animation doesn't occur if SystemParameters.ClientAreaAnimation is false. But since this property is readonly, it doesn't really help you...

Thomas Levesque