将控件导出到 png WPF C# 时数据上下文不更新元素
Data Context not updating elements when exporting control to a png WPF C#
我正在开发一个可以为纸牌游戏打印纸牌的程序。在程序中,每种卡类型都有一个 class 和一个用户控件。我有一个包含我所有卡片的大清单。为了打印卡片,我在后台代码中动态创建控件,将它们添加到打印文档中,然后进行打印。这行得通!下面是我使用的控件的生成示例:
AttackCardControl cpTop = new AttackCardControl();
cpTop.DataContext = StateManager.CardsToPrint.ElementAt(i);
Viewbox vb = new Viewbox() { Width = 240, Height = 336 };
vb.Child = cpTop;
sp.Children.Add(vb);
sp 是我用来在页面上排列卡片的堆栈面板,i 是包含它的 for 循环的迭代器。我 运行 通过一系列 for 循环以及其他一些无关紧要的小东西,它工作得很好。
现在我正在创建一项新功能,允许用户将卡片导出为 PNG。我决定以与我打印卡片的方式非常相似的方式来做这件事。这是我使用的:
for (int i = 0; i < StateManager.CardsToPrint.Count; ++i)
{
Canvas cv = new Canvas();
cv.Width = 825;
cv.Height = 1125;
if (StateManager.CardsToPrint.ElementAt(i).GetType() == typeof(AttackCard))
{
AttackCardControl cardControl = new AttackCardControl();
cardControl.DataContext = StateManager.CardsToPrint.ElementAt(i);
cv.Children.Add(cardControl);
}
FileHandling.ExportToPng(new Uri(path + "/" + StateManager.CardsToPrint.ElementAt(i).Name + ".png"), cv);
}
这应该将我在列表中的所有攻击卡导出为 png,确实如此。然而,绑定到数据上下文的元素没有得到更新(名称、效果文本、风味文本等)。所以最后,我只得到一张有正确文件名的空白攻击卡。当我在调试时按照它进行操作时,DataContext 仍然填充了所有正确的数据,但是当它导出到 png 时,用户控件的 none 元素显示它们的值。这是我的 ExportToPng 方法的代码:
public static void ExportToPng(Uri path, Canvas surface)
{
if (path == null) return;
// Save current canvas transform
Transform transform = surface.LayoutTransform;
// reset current transform (in case it is scaled or rotated)
surface.LayoutTransform = null;
// Get the size of canvas
System.Windows.Size size = new System.Windows.Size(surface.Width, surface.Height);
// Measure and arrange the surface
// VERY IMPORTANT
surface.Measure(size);
surface.Arrange(new Rect(size));
// Create a render bitmap and push the surface to it
RenderTargetBitmap renderBitmap =
new RenderTargetBitmap(
(int)size.Width,
(int)size.Height,
96d,
96d,
PixelFormats.Pbgra32);
renderBitmap.Render(surface);
// Create a file stream for saving image
using (FileStream outStream = new FileStream(path.LocalPath, FileMode.Create))
{
// Use png encoder for our data
PngBitmapEncoder encoder = new PngBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
}
// Restore previously saved layout
surface.LayoutTransform = transform;
}
我只是不明白为什么它适用于打印,但不适用于导出到 PNG。任何帮助将不胜感激。
更新
我刚刚创建了一个有这个问题的示例项目,似乎找不到解决它的方法。示例项目有 3 个东西:MainWindow、UserControl1 和 Model.cs.
MainWindow 是我用来控制和显示问题的。后面的代码包含错误发生位置的所有逻辑。对于这个简单的例子,我没有理会 MVVM。
这是 XAML:
<Window x:Class="ExportTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ExportTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel Name="sp" Margin="10">
<Button Content="Export Control to PNG" Click="Button_Click" Width="150" Height="30"/>
</StackPanel>
</Window>
下面是代码:
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace ExportTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Model m = new Model("Testing");
UserControl1 uc1 = new UserControl1();
uc1.DataContext = m;
sp.Children.Add(uc1);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var dialog = new System.Windows.Forms.FolderBrowserDialog();
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string path = dialog.SelectedPath;
Canvas cv = new Canvas();
cv.Width = 825;
cv.Height = 1125;
Model m = new Model("Testing");
UserControl1 uc1 = new UserControl1();
uc1.DataContext = m;
cv.Children.Add(uc1);
// The card control is losing it's data context
ExportToPng(new Uri(path + "/" + m.Name + ".png"), cv);
}
}
public void ExportToPng(Uri path, Canvas surface)
{
if (path == null) return;
//// Save current canvas transform
//Transform transform = surface.LayoutTransform;
//// reset current transform (in case it is scaled or rotated)
//surface.LayoutTransform = null;
// Get the size of canvas
Size size = new Size(surface.Width, surface.Height);
// Measure and arrange the surface
// VERY IMPORTANT
surface.Measure(size);
surface.Arrange(new Rect(size));
// Create a render bitmap and push the surface to it
RenderTargetBitmap renderBitmap =
new RenderTargetBitmap(
(int)size.Width,
(int)size.Height,
96d,
96d,
PixelFormats.Pbgra32);
renderBitmap.Render(surface);
// Create a file stream for saving image
using (FileStream outStream = new FileStream(path.LocalPath, FileMode.Create))
{
// Use png encoder for our data
PngBitmapEncoder encoder = new PngBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
}
//// Restore previously saved layout
//surface.LayoutTransform = transform;
}
}
}
这是 UserControl1(我们试图导出到 PNG 的用户控件)的 XAML:
<UserControl x:Class="ExportTest.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ExportTest"
mc:Ignorable="d">
<StackPanel Orientation="Horizontal" Background="Red">
<TextBlock Text="Name: "/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</UserControl>
此用户控件不需要隐藏代码。
最后,我们有模型类。这将是 UserControl1 的数据上下文模型。这是 class:
namespace ExportTest
{
public class Model
{
private string _Name;
public string Name { get { return _Name; } set { _Name = value; } }
public Model()
{
Name = "";
}
public Model(string name)
{
Name = name;
}
}
}
这将重现我的确切问题。在 Main Window 中,我们得到如下所示的内容:
如您所见,UserControl1 正在捕获 DataContext 并显示名称 Testing。但是,如果我单击“将控制导出到 PNG”按钮,这就是我导出的 PNG 文件的结果:
不知何故,我的数据丢失了。有什么线索吗?感谢您的帮助!
// Save current canvas transform
Transform transform = surface.LayoutTransform;
// reset current transform (in case it is scaled or rotated)
surface.LayoutTransform = null;
之后
// Restore previously saved layout
surface.LayoutTransform = transform;
并不像您认为的那样工作。 Transform 是 class,不是结构。当您将 surface.LayoutTransform 设置为 null 时,您也将 transform 设置为 null。
尝试注释掉所有这些行,看看会发生什么。如果它有效但卡片被旋转,那么您必须深度克隆布局转换,或者只保存确实发生变化的属性。
所以我刚刚在这里找到了答案:
Saving FrameworkElement with its DataContext to image file does no succeed
在我们将控件呈现为图像之前,需要调用 UpdateLayout() 来修复绑定。
我正在开发一个可以为纸牌游戏打印纸牌的程序。在程序中,每种卡类型都有一个 class 和一个用户控件。我有一个包含我所有卡片的大清单。为了打印卡片,我在后台代码中动态创建控件,将它们添加到打印文档中,然后进行打印。这行得通!下面是我使用的控件的生成示例:
AttackCardControl cpTop = new AttackCardControl();
cpTop.DataContext = StateManager.CardsToPrint.ElementAt(i);
Viewbox vb = new Viewbox() { Width = 240, Height = 336 };
vb.Child = cpTop;
sp.Children.Add(vb);
sp 是我用来在页面上排列卡片的堆栈面板,i 是包含它的 for 循环的迭代器。我 运行 通过一系列 for 循环以及其他一些无关紧要的小东西,它工作得很好。
现在我正在创建一项新功能,允许用户将卡片导出为 PNG。我决定以与我打印卡片的方式非常相似的方式来做这件事。这是我使用的:
for (int i = 0; i < StateManager.CardsToPrint.Count; ++i)
{
Canvas cv = new Canvas();
cv.Width = 825;
cv.Height = 1125;
if (StateManager.CardsToPrint.ElementAt(i).GetType() == typeof(AttackCard))
{
AttackCardControl cardControl = new AttackCardControl();
cardControl.DataContext = StateManager.CardsToPrint.ElementAt(i);
cv.Children.Add(cardControl);
}
FileHandling.ExportToPng(new Uri(path + "/" + StateManager.CardsToPrint.ElementAt(i).Name + ".png"), cv);
}
这应该将我在列表中的所有攻击卡导出为 png,确实如此。然而,绑定到数据上下文的元素没有得到更新(名称、效果文本、风味文本等)。所以最后,我只得到一张有正确文件名的空白攻击卡。当我在调试时按照它进行操作时,DataContext 仍然填充了所有正确的数据,但是当它导出到 png 时,用户控件的 none 元素显示它们的值。这是我的 ExportToPng 方法的代码:
public static void ExportToPng(Uri path, Canvas surface)
{
if (path == null) return;
// Save current canvas transform
Transform transform = surface.LayoutTransform;
// reset current transform (in case it is scaled or rotated)
surface.LayoutTransform = null;
// Get the size of canvas
System.Windows.Size size = new System.Windows.Size(surface.Width, surface.Height);
// Measure and arrange the surface
// VERY IMPORTANT
surface.Measure(size);
surface.Arrange(new Rect(size));
// Create a render bitmap and push the surface to it
RenderTargetBitmap renderBitmap =
new RenderTargetBitmap(
(int)size.Width,
(int)size.Height,
96d,
96d,
PixelFormats.Pbgra32);
renderBitmap.Render(surface);
// Create a file stream for saving image
using (FileStream outStream = new FileStream(path.LocalPath, FileMode.Create))
{
// Use png encoder for our data
PngBitmapEncoder encoder = new PngBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
}
// Restore previously saved layout
surface.LayoutTransform = transform;
}
我只是不明白为什么它适用于打印,但不适用于导出到 PNG。任何帮助将不胜感激。
更新
我刚刚创建了一个有这个问题的示例项目,似乎找不到解决它的方法。示例项目有 3 个东西:MainWindow、UserControl1 和 Model.cs.
MainWindow 是我用来控制和显示问题的。后面的代码包含错误发生位置的所有逻辑。对于这个简单的例子,我没有理会 MVVM。 这是 XAML:
<Window x:Class="ExportTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ExportTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<StackPanel Name="sp" Margin="10">
<Button Content="Export Control to PNG" Click="Button_Click" Width="150" Height="30"/>
</StackPanel>
</Window>
下面是代码:
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace ExportTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Model m = new Model("Testing");
UserControl1 uc1 = new UserControl1();
uc1.DataContext = m;
sp.Children.Add(uc1);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var dialog = new System.Windows.Forms.FolderBrowserDialog();
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string path = dialog.SelectedPath;
Canvas cv = new Canvas();
cv.Width = 825;
cv.Height = 1125;
Model m = new Model("Testing");
UserControl1 uc1 = new UserControl1();
uc1.DataContext = m;
cv.Children.Add(uc1);
// The card control is losing it's data context
ExportToPng(new Uri(path + "/" + m.Name + ".png"), cv);
}
}
public void ExportToPng(Uri path, Canvas surface)
{
if (path == null) return;
//// Save current canvas transform
//Transform transform = surface.LayoutTransform;
//// reset current transform (in case it is scaled or rotated)
//surface.LayoutTransform = null;
// Get the size of canvas
Size size = new Size(surface.Width, surface.Height);
// Measure and arrange the surface
// VERY IMPORTANT
surface.Measure(size);
surface.Arrange(new Rect(size));
// Create a render bitmap and push the surface to it
RenderTargetBitmap renderBitmap =
new RenderTargetBitmap(
(int)size.Width,
(int)size.Height,
96d,
96d,
PixelFormats.Pbgra32);
renderBitmap.Render(surface);
// Create a file stream for saving image
using (FileStream outStream = new FileStream(path.LocalPath, FileMode.Create))
{
// Use png encoder for our data
PngBitmapEncoder encoder = new PngBitmapEncoder();
// push the rendered bitmap to it
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
// save the data to the stream
encoder.Save(outStream);
}
//// Restore previously saved layout
//surface.LayoutTransform = transform;
}
}
}
这是 UserControl1(我们试图导出到 PNG 的用户控件)的 XAML:
<UserControl x:Class="ExportTest.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:ExportTest"
mc:Ignorable="d">
<StackPanel Orientation="Horizontal" Background="Red">
<TextBlock Text="Name: "/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</UserControl>
此用户控件不需要隐藏代码。
最后,我们有模型类。这将是 UserControl1 的数据上下文模型。这是 class:
namespace ExportTest
{
public class Model
{
private string _Name;
public string Name { get { return _Name; } set { _Name = value; } }
public Model()
{
Name = "";
}
public Model(string name)
{
Name = name;
}
}
}
这将重现我的确切问题。在 Main Window 中,我们得到如下所示的内容:
如您所见,UserControl1 正在捕获 DataContext 并显示名称 Testing。但是,如果我单击“将控制导出到 PNG”按钮,这就是我导出的 PNG 文件的结果:
不知何故,我的数据丢失了。有什么线索吗?感谢您的帮助!
// Save current canvas transform
Transform transform = surface.LayoutTransform;
// reset current transform (in case it is scaled or rotated)
surface.LayoutTransform = null;
之后
// Restore previously saved layout
surface.LayoutTransform = transform;
并不像您认为的那样工作。 Transform 是 class,不是结构。当您将 surface.LayoutTransform 设置为 null 时,您也将 transform 设置为 null。
尝试注释掉所有这些行,看看会发生什么。如果它有效但卡片被旋转,那么您必须深度克隆布局转换,或者只保存确实发生变化的属性。
所以我刚刚在这里找到了答案: Saving FrameworkElement with its DataContext to image file does no succeed
在我们将控件呈现为图像之前,需要调用 UpdateLayout() 来修复绑定。