0

我的要求:

  • 处理自定义图像逻辑的持久性UserControl,例如地图或绘图
  • 一组容器,用于在缩放或平移移动期间对图像进行缓存
  • VisualBrushUserControl我可以添加到容器中以与效果一起使用的副本

我目前使用 RenderTargetBitmap 实现图像缓存,但这似乎与我正在使用的VisualBrush-covered对象有问题。Rectangle

我的问题:我可以在此代码中添加/更改什么以使VisualBrush对象在使用后正确呈现RenderTargetBitmap?是什么奇怪的事情RenderTargetBitmap使人VisualBrush看不见?

如果没有大量代码,我无法重现这个问题。

在我的 xaml 文件中,我有:

<Window x:Class="ElementRender.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="350" Width="525">
  <Grid>
    <Grid Name="_contentContainer">
      <Rectangle Fill="White"/>
      <Grid Name="_content">
        <Grid Name="_back"/>
        <Grid Name="_body"/>
      </Grid>
    </Grid>
    <StackPanel VerticalAlignment="Bottom" Orientation="Horizontal">
      <Button Content="New" Name="New"/>
      <Button Content="Move" Name="Move"/>
      <Button Content="Update" Name="Update"/>
    </StackPanel>
  </Grid>
</Window>

和 .xaml.cs:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

public partial class MainWindow : Window
{

  private const int imageWidth = 150;
  private const int imageHeight = 150;
  private readonly UserControl Control;

  public MainWindow()
  {
     InitializeComponent();

     // User Control setup
     Control = new UserControl() {
        Width = imageWidth, Height = imageHeight,
        Content = BuildImage()
     };
     _body.Children.Add(SoftCopy(Control));

     // event setup
     Move.Click += (sender, e) => _content.RenderTransform = new TranslateTransform(50, 50);
     New.Click += (sender, e) => {
        HardCopy();
        _content.RenderTransform = null;
        Control.Content = BuildImage();
     };
  }

  private FrameworkElement BuildImage()
  {
     return new Rectangle{Fill=Brushes.Blue};
  }
  private void HardCopy()
  {
     int width = (int) _contentContainer.ActualWidth;
     int height = (int) _contentContainer.ActualHeight;

     // render the current image
     var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
     DrawingVisual dv = new DrawingVisual();
     using (var context = dv.RenderOpen())
     {
        var brush = new VisualBrush(_contentContainer) { Opacity = .5 };
        context.DrawRectangle(brush, null, new Rect(0, 0, width, height));
     }
     rtb.Render(dv);
     var lastRender = new Image
     {
        Source = rtb,
        Stretch = Stretch.None,
        HorizontalAlignment = HorizontalAlignment.Center,
        VerticalAlignment = VerticalAlignment.Center,
        Width = width,
        Height = height
     };
     _back.Children.Clear();
     _back.Children.Add(lastRender);
  }
  private FrameworkElement SoftCopy(FrameworkElement element)
  {
     return new Rectangle{Fill= new VisualBrush(element), Width=element.Width, Height=element.Height};
  }
}

关于代码的一些帮助说明:

  • xaml_contentContainer用于HardCopy()将当前图像复制到图像缓存中,_back.
  • SoftCopy返回一个与过去一模一样的 FrameworkElement,但没有任何变换、效果或视觉父级。这个非常重要。
  • BuildImage模拟在初始图像以某种方式转换后构建要粘贴到缓存上的新图像。

如果您构建并运行SoftCopy()从 中删除 的应用程序_body.Children.Add(SoftCopy(Control));,您会看到我想要获得的效果:新元素粘贴在旧元素上方,而旧元素似乎保留了它的变换。

var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);或者,如果您从中删除该行HardCopy,缓存功能将被破坏,但 SoftCopy 会正确显示。

但是,如果您按原样运行应用程序,您会注意到新的 BlueRectangle(通过 VisualBrush 呈现)根本不会显示,直到您再次点击“新建”按钮,将图像推送到缓存,并且仍然没有向您显示新创建的图像。

4

2 回答 2

3

我会很自负地称其为 WPF 中的错误。我最终发现了如何解决我遇到的奇怪行为:

var visual = visualBrush.Visual;
visualBrush.Visual = null;
visualBrush.Visual = visual;

这本质上应该是一个空操作:到最后,视觉画笔具有与开始时相同的视觉效果。VisualBrush但是,在渲染后添加此代码段RenderTargetBitmap修复了我遇到的问题。

于 2012-11-01T17:16:22.363 回答
0

我不太明白这篇文章,但有几件重要的事情:

如果您将 RenderTransform/Margins 应用于元素并对其进行拍照(RenderTargetBItmap),那么您将度过一段糟糕的时光。它会被抵消,你只会得到子图片。

这个想法是在没有任何渲染转换的情况下拍照,然后从旧的复制 RenderTransform。如果需要的话。

于 2012-09-15T17:02:30.510 回答