渲染目标位图 +资源化 VisualBrush = 不完整的图像

发布于 2024-09-02 07:42:01 字数 1708 浏览 9 评论 0原文

我发现了“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 不会显示。显而易见的谷歌解决方案已经被尝试过,但都失败了。即使是那些特别提到 VisualBrushes 的 RTB 位图也缺失了。

我偷偷怀疑这可能是由于它是一个资源而导致的,而惰性资源没有被内联。因此,一个可能的修复方法是,以某种方式(???)在渲染之前强制解析所有静态资源引用。但我完全不知道该怎么做。

有人能解决这个问题吗?

I've found a new twist on the "Visual to RenderTargetBitmap" question!

I'm rendering previews of WPF stuff for a designer. That means I need to take a WPF visual and render it to a bitmap without that visual ever being displayed. Got a nice little method to do it like to see it here it goes

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;
}

Works fine, except for one leeetle thing... if my FrameworkElement has a VisualBrush, that brush doesn't end up in the final rendered bitmap. Something like this:

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

Everything else renders to the bitmap, but that VisualBrush just won't show. The obvious google solutions have been attempted and have failed. Even the ones that specifically mention VisualBrushes missing from RTB'd bitmaps.

I have a sneaky suspicion this might be caused by the fact that its a Resource, and that lazy resource isn't being inlined. So a possible fix would be to, somehow(???), force resolution of all static resource references before rendering. But I have absolutely no idea how to do that.

Anybody have a fix for this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

南城旧梦 2024-09-09 07:42:01

您有两个问题:

  1. 您没有在视觉对象上设置PresentationSource,因此不会触发Loaded 事件。
  2. 您没有刷新调度程序队列。如果不刷新调度程序队列,任何使用调度程序回调的功能都将无法工作。

问题的直接原因是无法刷新调度程序队列,因为 VisualBrush 使用它,但不久您可能会遇到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 可能关联的唯一方式是它是否因为两个资源具有相同的密钥而选择了错误的资源。我只是想我应该解释一下——这与你的实际问题无关。

You have two problems:

  1. You didn't set a PresentationSource on your visual so Loaded events won't fire.
  2. You didn't flush the Dispatcher queue. Without flushing the Dispatcher queue, any functionality that uses Dispatcher callbacks won't work.

The immediate cause of your problem is failure to flush the Dispatcher queue, since VisualBrush uses it, but you will probably run into the PresentationSource problem before long so I would fix both of these.

Here is how I do it:

// 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;
}

FYI, StaticResource lookup is never delayed under any circumstances: It is processed the moment the XAML is loaded and immediately replaced with the value retrieved from the ResourceDictionary. The only way StaticResource could possibly be related is if it picked up the wrong resource because two resources had the same key. I just thought I should explain this -- it has nothing to do with your actual problem.

柒夜笙歌凉 2024-09-09 07:42:01

好吧,要内联它,您可以这样做:

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

如果这不起作用,我的猜测是它一定是您正在使用的 Visual 实例特定的东西(并且这将需要进一步的代码以更好地诊断)。

Well to inline it, you could just do something like this:

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

If that doesn't work, my guess would be that it must be something specific with the Visual instance you are using (and that will require further code to better diagnose).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文