绘制WPF用户控件与数据绑定到图像

绘制WPF用户控件与数据绑定到图像

问题描述:

所以我试图用一个WPF用户控件生成一吨从数据集图像,其中在数据集中的每个项目将产生的图像...绘制WPF用户控件与数据绑定到图像

我希望我可以设置它,以便我可以使用WPF数据绑定,并为数据集中的每个项目创建一个用户控件的实例,设置与我的数据项相对应的依赖项属性,然后绘制用户控制图像,但我有问题得到它所有工作(不确定是否数据绑定或绘图到图像是我的问题)

对不起,大量的代码转储,但我一直在试图得到这个现在工作了几个小时,还有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> 

而且我已经添加型“预定”的依赖属性

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

    } 
0123是我希望将是数据绑定值的来源我的用户控制

编辑:

我要补充一点,过程工程没有任何错误,但我结束了一个完整的纯白色图像的目录,而不是文本或者什么...我已经确认使用调试器,我的预订的对象充满正确的数据...

编辑2:

做了一件我应该很久以前做过,设置我的画布的背景下,但这并没有改变输出图像所以我的问题是绝对不知何故与我的绘图代码(虽然也可能是我的数据绑定有问题)

RenderTargetBitmap呈现您的控件的当前状态。在你的情况下,你的控件没有初始化,所以它仍然显示为白色。

为了让您的代码正确初始化渲染()之前,你需要做三件事情:

  1. 确保您的控制进行了测量和安排。
  2. 如果您的控件使用Loaded事件,请确保您已连接到PresentationSource。
  3. 确保所有DispatcherPriority.Render和以上事件都已完成。

如果你做了这三件事,你的RenderTargetBitmap将出现在控件出现在窗口中时出现的方式。在你的控制

强制措施/安排这是非常简单:

template.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 
template.Arrange(new Rect(template.DesiredSize)); 

该代码强制措施/安排。对于宽度和高度传递double.PositiveInfinity是最简单的,因为它允许您的UserControl选择它自己的宽度和高度。如果明确设置宽度/高度并不重要,但这样,如果数据比预期的大,则您的UserControl可以选择使用WPF的布局系统在必要时自动增长。同样的道理,最好使用template.DesiredSize作为编配,而不是传递特定的大小。

附加一个PresentationSource

这只是必要的,如果你的控制或控制范围之内的元素依靠Loaded事件。

using(var source = new HwndSource(new HwndSourceParameters()) 
         { RootVisual = template }) 
{ 
    ... 
} 

当HwndSource创建模板的可视化树通知,它已被“装”。 “using”块确保模板在“using”语句(最后一个关闭大括号)末尾处为“Unloaded”。到使用()语句另一种方法是使用GC.KeepAlive:

GC.KeepAlive(new HwndSource(...) { ... }); 

法拉盛调度队列下降到DispatcherPriority.Render

只需使用Dispatcher.Invoke:

Dispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => {})); 

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

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

何处添加该代码

加入所有这三个步骤设置你的数据上下文(template.Booking = ...),并调用RenderTargetBitmap.Render之前后。

其他建议

有使你的绑定工作更简单的方法。在代码中,只需将预订设置为DataContext。这样就省去了使用的ElementName和预订属性:

foreach(var booking in Booking.GetSome()) 
{ 
    var template = new ImageTemplate { DataContext = booking }; 

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

通过使用DataContext的,文本框结合,大大简化:

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

如果您有使用预定的DependencyProperty你一个特别的原因通过在<UserControl>级别设置DataContext的,而不是使用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只需要指定文本 - 其他所有内容都由样式处理。

+0

哇!谢谢你提供的信息。我根本没有使用OnLoad,但其他两个提示解决了我的问题......非常感谢......我新推出的一些XAML完全没有午餐,但我只是试图让它渲染,我现在正在修复一些......再次感谢 – LorenVS 2010-04-08 05:00:42

+2

如果我看到它是一个很好的赏金 – RandomEngy 2010-04-08 15:55:33

+2

我不知道这个答案有点短,很清楚,也许我如果你修改并扩展它一些... – Will 2010-05-17 16:45:56

我认为问题在于绑定,正如您怀疑的那样。尝试设置ImageTemplate实例的DataContext,而不是创建属性Booking,然后将绑定上的路径设置为仅用于要使用的数据对象的属性名称。它可能不能解决你的问题,但它是一个更标准的绑定方式。

<TextBlock ... Text="{Binding Path=Customer}" /> 

应该是你所需要的,如果你设置为Booking实例的数据上下文,使装订工作。试试吧,让我们知道它是否有效。

+0

我已经设置了这种方式之前,但它似乎仍然没有正常工作......我不记得确切的xaml改回它:(我认为我还在做一些事情但是在绘图部分错误,因为我已经将usercontrol的背景设置为黑色,并且仍然会得到一个空的图像... – LorenVS 2010-04-01 01:08:43

+0

它必须是您的绘图部分,然而,不幸的是,我不知道任何有关成像等等。对不起。 – 2010-04-01 01:33:45

那么,你的问题之一是你需要在你的UserControl上调用Measure和Arrange,然后再尝试渲染。在创建RenderTargetBitmap对象之前先将其设置为:

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

这将至少让您的UserControl开始渲染。

第二个问题是数据绑定。我无法破解那个;你可能需要做一些额外的事情来获得绑定的评估。但是,您可以解决此问题:如果直接设置TextBlock内容而不是通过数据绑定,则确实可行。