Lor*_*nVS 6 data-binding wpf image .net-3.5
所以我正在尝试使用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>
Run Code Online (Sandbox Code Playgroud)
我已经向我的用户控件添加了一个类型为"Booking"的依赖项属性,我希望它将成为数据绑定值的来源:
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();
}
}
Run Code Online (Sandbox Code Playgroud)
我正在使用以下代码来呈现控件:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:
我应该补充一点,这个过程没有任何错误,但我最终得到一个充满纯白图像的目录,而不是文本或任何东西......我已经确认使用调试器,我的预订对象正在填充正确的数据...
编辑2:
做了我应该做的事很久以前,在我的画布上设置了一个背景,但这根本没有改变输出图像,所以我的问题绝对与我的绘图代码有关(尽管可能有些错误我的数据绑定也是)
Ray*_*rns 15
RenderTargetBitmap呈现控件的当前状态.在您的情况下,您的控件尚未初始化,因此它仍然显示为白色.
要在Render()之前正确初始化代码,您需要做三件事:
如果您执行这三项操作,则在RenderTargetBitmap将控件添加到窗口时,控件的显示方式与您的操作方式完全相同.
强制控制/安排你的控制
这很简单:
template.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
template.Arrange(new Rect(template.DesiredSize));
Run Code Online (Sandbox Code Playgroud)
此代码强制衡量/安排.最简单的方法是传递double.PositiveInfinity的宽度和高度,因为它允许UserControl选择自己的宽度和高度.如果您明确设置宽度/高度并不重要,但这样您的UserControl可以选择使用WPF的布局系统在必要时自动增长,如果数据大于预期.出于同样的原因,最好使用template.DesiredSize作为Arrange而不是传递特定的大小.
附加PresentationSource
仅当控件或控件中的元素依赖于Loaded事件时,才需要这样做.
using(var source = new HwndSource(new HwndSourceParameters())
{ RootVisual = template })
{
...
}
Run Code Online (Sandbox Code Playgroud)
创建HwndSource时,会通知模板的可视化树已"加载"."using"块确保模板在"using"语句(最后一个大括号)的末尾处为"Unloaded".using()语句的替代方法是使用GC.KeepAlive:
GC.KeepAlive(new HwndSource(...) { ... });
Run Code Online (Sandbox Code Playgroud)
将Dispatcher队列刷新到DispatcherPriority.Render
只需使用Dispatcher.Invoke:
Dispatcher.Invoke(DispatcherPriority.Loaded, new Action(() => {}));
Run Code Online (Sandbox Code Playgroud)
这会导致在完成所有渲染和更高优先级操作后调用空操作.Dispatcher.Invoke方法处理调度程序队列,直到它为空,直到Loaded级别(位于Render下方).
这是必要的原因是许多WPF UI组件使用Dispatcher队列来延迟处理,直到控件准备好呈现为止.这显着减少了在绑定和其他操作期间不必要的视觉特性的重新计算.
在哪里添加此代码
在设置数据上下文(template.Booking = ...)之后和调用之前,添加所有这三个步骤RenderTargetBitmap.Render.
其他建议
有一种更简单的方法来进行绑定工作.在代码中,只需将预订设置为DataContext即可.这消除了使用ElementName和Booking属性的需要:
foreach(var booking in Booking.GetSome())
{
var template = new ImageTemplate { DataContext = booking };
... code from above ...
... RenderTargetBitmap code ...
}
Run Code Online (Sandbox Code Playgroud)
通过使用DataContext,TextBox绑定大大简化:
<UserControl ...>
<Canvas>
<TextBlock ... Text="{Binding Customer}" />
<TextBlock ... Text="{Binding Location}" />
<TextBlock ... Text="{Binding ItemNumber}" />
<TextBlock ... Text="{Binding Description}" />
Run Code Online (Sandbox Code Playgroud)
如果您有使用Booking DependencyProperty的特殊原因,您仍然可以通过在<UserControl>级别设置DataContext 而不是使用ElementName以下内容来简化绑定:
<UserControl ...
DataContext="{Binding Booking, RelativeSource={RelativeSource Self}}">
<Canvas>
<TextBlock ... Text="{Binding Customer}" />
Run Code Online (Sandbox Code Playgroud)
为了这个目的,我还建议您使用a StackPanel而不是a 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>
Run Code Online (Sandbox Code Playgroud)
请注意,给定UserControl大小以及指定的高度和边距,所有布局都由WPF的布局完成.另请注意,TextBlock只需要指定Text - 其他所有内容都由样式处理.