8

「Visual to RenderTargetBitmap」の質問に新たなひねりを加えました!

デザイナー向けに WPF のプレビューをレンダリングしています。つまり、WPF ビジュアルを取得して、そのビジュアルを表示せずにビットマップにレンダリングする必要があります。ここでそれを見るのと同じように、それを行うための素敵な小さな方法を手に入れました

private static BitmapSource CreateBitmapSource(FrameworkElement visual)
{
    Border b = new Border { Width = visual.Width, Height = visual.Height };
    b.BorderBrush = Brushes.Black;
    b.BorderThickness = new Thickness(1);
    b.Background = Brushes.White;
    b.Child = visual;

    b.Measure(new Size(b.Width, b.Height));
    b.Arrange(new Rect(b.DesiredSize));

    RenderTargetBitmap rtb = new RenderTargetBitmap(
                                (int)b.ActualWidth,
                                (int)b.ActualHeight,
                                96,
                                96,
                                PixelFormats.Pbgra32);

    // intermediate step here to ensure any VisualBrushes are rendered properly
    DrawingVisual dv = new DrawingVisual();
    using (var dc = dv.RenderOpen())
    {
        var vb = new VisualBrush(b);
        dc.DrawRectangle(vb, null, new Rect(new Point(), b.DesiredSize));
    }
    rtb.Render(dv);
    return rtb;
}

私のFrameworkElementにVisualBrushがある場合、そのブラシは最終的にレンダリングされたビットマップにはなりません。このようなもの:

<UserControl.Resources>
    <VisualBrush
        x:Key="LOLgo">
        <VisualBrush.Visual>
            <!-- blah blah -->
<Grid 
    Background="{StaticResource LOLgo}">
<!-- yadda yadda -->

それ以外はすべてビットマップにレンダリングされますが、その VisualBrush は表示されません。明らかな Google ソリューションが試みられ、失敗しました。RTB されたビットマップにない VisualBrushes について具体的に言及しているものでさえ。

これはリソースであり、遅延リソースがインライン化されていないことが原因である可能性があるという卑劣な疑いがあります。したがって、可能な修正は、何らかの方法で (???)、レンダリングの前にすべての静的リソース参照の解決を強制することです。しかし、私はそれを行う方法がまったくわかりません。

誰でもこれを修正できますか?

4

2 に答える 2

14

2 つの問題があります。

  1. ビジュアルに PresentationSource を設定しなかったため、Loaded イベントは発生しません。
  2. Dispatcher キューをフラッシュしませんでした。Dispatcher キューをフラッシュしないと、Dispatcher コールバックを使用する機能は動作しません。

問題の直接の原因は、VisualBrush が Dispatcher キューを使用しているため、Dispatcher キューをフラッシュできないことですが、おそらくすぐに PresentationSource の問題に遭遇するので、これらの両方を修正します。

これが私がそれを行う方法です:

// Create the container
var container = new Border
{
  Child = contentVisual,
  Background = Brushes.White,
  BorderBrush = Brushes.Black,
  BorderThickness = new Thickness(1),
};

// Measure and arrange the container
container.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
container.Arrange(new Rect(container.DesiredSize));

// Temporarily add a PresentationSource if none exists
using(var temporaryPresentationSource = new HwndSource(new HwndSourceParameters()) { RootVisual = (VisualTreeHelper.GetParent(container)==null ? container : null) })
{
  // Flush the dispatcher queue
  Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(() => { }));

  // Render to bitmap
  var rtb = new RenderTargetBitmap((int)b.ActualWidth, (int)b.ActualHeight, 96, 96, PixelFormats.Pbgra32);
  rtb.Render(container);

  return rtb;
}

参考までに、StaticResource ルックアップはどのような状況でも遅延することはありません。XAML が読み込まれた瞬間に処理され、すぐに ResourceDictionary から取得した値に置き換えられます。StaticResource が関連付けられる可能性がある唯一の方法は、2 つのリソースが同じキーを持っていたために間違ったリソースを選択した場合です。これを説明する必要があると思いました-実際の問題とは何の関係もありません。

于 2010-06-18T22:12:16.960 に答える
0

インライン化するには、次のようにするだけです。

<Grid>
    <Grid.Background>
        <VisualBrush>
            <VisualBrush.Visual>
                <!-- blah blah -->
            </VisualBrush.Visual>
        </VisualBrush>
    </Grid.Background>
</Grid>

それが機能しない場合は、Visual使用しているインスタンスに固有のものである必要があると思います (より適切に診断するには、さらにコードが必要になります)。

于 2010-05-17T18:23:13.680 に答える