gpt4 book ai didi

wpf - 使用 DataBinding 将 WPF UserControl 绘制到图像

转载 作者:行者123 更新时间:2023-12-04 13:17:58 25 4
gpt4 key购买 nike

所以我正在尝试使用 WPF 用户控件从数据集中生成大量图像,其中数据集中的每个项目都会生成图像......
我希望我可以以这样的方式设置它,我可以使用 WPF 数据绑定(bind),并为数据集中的每个项目,创建我的用户控件的实例,设置对应于我的数据项的依赖属性,然后绘制用户控制图像,但我无法让它全部工作(不确定数据绑定(bind)或绘制到图像是我的问题)
对不起,大量的代码转储,但我一直试图让它工作几个小时,而 WPF 就是不喜欢我(虽然必须在某个时候学习......)
我的用户控件如下所示:

<UserControl x:Class="Bleargh.ImageTemplate"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:Bleargh"
x:Name="ImageTemplateContainer"
Height="300" Width="300">

<Canvas>
<TextBlock Canvas.Left="50" Canvas.Top="50" Width="200" Height="25" FontSize="16" FontFamily="Calibri" Text="{Binding Path=Booking.Customer, ElementName=ImageTemplateContainer}" />
<TextBlock Canvas.Left="50" Canvas.Top="100" Width="200" Height="25" FontSize="16" FontFamily="Calibri" Text="{Binding Path=Booking.Location, ElementName=ImageTemplateContainer}" />
<TextBlock Canvas.Left="50" Canvas.Top="150" Width="200" Height="25" FontSize="16" FontFamily="Calibri" Text="{Binding Path=Booking.ItemNumber, ElementName=ImageTemplateContainer}" />
<TextBlock Canvas.Left="50" Canvas.Top="200" Width="200" Height="25" FontSize="16" FontFamily="Calibri" Text="{Binding Path=Booking.Description,ElementName=ImageTemplateContainer}" />
</Canvas>

</UserControl>
我已经在我的用户控件中添加了一个“Booking”类型的依赖属性,我希望它会成为数据绑定(bind)值的来源:
public partial class ImageTemplate : UserControl
{
public static readonly DependencyProperty BookingProperty = DependencyProperty.Register("Booking", typeof(Booking), typeof(ImageTemplate));
public Booking Booking
{
get { return (Booking)GetValue(BookingProperty); }
set { SetValue(BookingProperty, value); }
}

public ImageTemplate()
{
InitializeComponent();
}
}
我正在使用以下代码来呈现控件:
List<Booking> bookings = Booking.GetSome();
for(int i = 0; i < bookings.Count; i++)
{
ImageTemplate template = new ImageTemplate();
template.Booking = bookings[i];

RenderTargetBitmap bitmap = new RenderTargetBitmap(
(int)template.Width,
(int)template.Height,
120.0,
120.0,
PixelFormats.Pbgra32);

bitmap.Render(template);

BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));

using (Stream s = File.OpenWrite(@"C:\Code\Bleargh\RawImages\" + i.ToString() + ".png"))
{
encoder.Save(s);
}
}
编辑:
我应该补充一点,该过程没有任何错误,但我最终得到一个充满纯白色图像的目录,而不是文本或任何东西......我已经使用调试器确认我的 Booking 对象正在填充正确的数据...
编辑2:
做了一件我很久以前就应该做的事情,在我的 Canvas 上设置了一个背景,但这根本没有改变输出图像,所以我的问题肯定与我的绘图代码有关(尽管可能有问题也用我的数据绑定(bind))

最佳答案

RenderTargetBitmap呈现控件的当前状态。在您的情况下,您的控件尚未初始化,因此它仍然显示为白色。

要让您的代码在 Render() 之前正确初始化,您需要做三件事:

  • 确保您的控制已被测量和安排。
  • 如果您的控件使用 Loaded 事件,请确保您已附加到 PresentationSource。
  • 确保所有 DispatcherPriority.Render 及以上事件均已完成。

  • 如果你做这三件事你的 RenderTargetBitmap将与您将控件添加到窗口时的显示方式相同。

    在您的控件上强制测量/排列

    这很简单:
    template.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
    template.Arrange(new Rect(template.DesiredSize));

    此代码强制测量/排列。为宽度和高度传递 double.PositiveInfinity 是最简单的,因为它允许您的 UserControl 选择自己的宽度和高度。如果您显式设置宽度/高度,则无关紧要,但这样您的 UserControl 可以选择使用 WPF 的布局系统在数据大于预期时在必要时自动增长。出于同样的原因,最好将 template.DesiredSize 用于 Arrange,而不是传入特定的大小。

    附加一个 PresentationSource

    仅当您的控件或控件中的元素依赖于 Loaded 事件时,才需要这样做。
    using(var source = new HwndSource(new HwndSourceParameters())
    { RootVisual = template })
    {
    ...
    }

    当 HwndSource 创建时,模板的可视化树会被通知它已“加载”。 “using” block 确保模板在“using”语句的末尾是“Unloaded”(最后一个右花括号)。 using() 语句的替代方法是使用 GC.KeepAlive:
    GC.KeepAlive(new HwndSource(...) { ... });

    将 Dispatcher 队列刷新到 DispatcherPriority.Render

    只需使用 Dispatcher.Invoke:
    Dispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => {}));

    这会导致在所有渲染和更高优先级操作完成后调用一个空操作。 Dispatcher.Invoke 方法处理调度程序队列,直到它空到 Loaded 级别(位于 Render 的正下方)。

    这是必要的原因是许多 WPF UI 组件使用 Dispatcher 队列来延迟处理,直到控件准备好呈现。这显着减少了绑定(bind)和其他操作期间不必要的视觉属性重新计算。

    在哪里添加此代码

    在设置数据上下文 ( template.Booking = ... ) 和调用 RenderTargetBitmap.Render 之前添加所有这三个步骤.

    其他建议

    有一种更简单的方法可以使您的绑定(bind)工作。在代码中,只需将预订设置为 DataContext。这消除了使用 ElementName 和 Booking 属性的需要:
    foreach(var booking in Booking.GetSome())
    {
    var template = new ImageTemplate { DataContext = booking };

    ... code from above ...
    ... RenderTargetBitmap code ...
    }

    通过使用 DataContext,TextBox 绑定(bind)大大简化:
    <UserControl ...>
    <Canvas>
    <TextBlock ... Text="{Binding Customer}" />
    <TextBlock ... Text="{Binding Location}" />
    <TextBlock ... Text="{Binding ItemNumber}" />
    <TextBlock ... Text="{Binding Description}" />

    如果您有使用 Booking DependencyProperty 的特殊原因,您仍然可以通过将 DataContext 设置为 <UserControl> 来简化绑定(bind)。水平而不是使用 ElementName :
    <UserControl ...
    DataContext="{Binding Booking, RelativeSource={RelativeSource Self}}">
    <Canvas>
    <TextBlock ... Text="{Binding Customer}" />

    我还建议您使用 StackPanel而不是 Canvas为此,您还应该考虑使用样式来设置字体、文本大小和间距:
    <UserControl ...
    Width="300" Height="300">

    <UserControl.Resources>
    <Style TargetType="TextBlock">
    <Setter Property="FontSize" Value="16" />
    <Setter Property="FontFamily" Value="Calibri" />
    <Setter Property="Height" Value="25" />
    <Setter Property="Margin" Value="50 25 50 0" />
    </Style>
    </UserControl.Resources>

    <StackPanel>
    <TextBlock Text="{Binding Customer}" />
    <TextBlock Text="{Binding Location}" />
    <TextBlock Text="{Binding ItemNumber}" />
    <TextBlock Text="{Binding Description}" />
    </StackPanel>
    </UserControl>

    请注意,所有布局都由 WPF 的布局完成,给定 UserControl 大小和指定的高度和边距。另请注意,TextBlock 只需要指定文本——其他一切都由样式处理。

    关于wpf - 使用 DataBinding 将 WPF UserControl 绘制到图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2557183/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com