How to perform DataBinding outside of and instance of System.Windows.Application
So here is the senerio.
I want to build up an image library with text embedded on the image from a database.
I am trying to do this by having a template.xaml file and instantiating it in a ClassLibrary project. So the xaml file is being instantiated outside of the scope of a WPF app开发者_如何转开发lication.
I am trying to create a PivotCollection kind of like explained in this link
http://martimedia.blogspot.com/2010/07/creating-pivot-collection.html
I ended up inheriting from System.Windows.Application in my class library to get this working but this is far from ideal as it raises questions about the AppDomain that is used.
I have knoced up some simple sample code to show simply what I am trying to achive.
Here is a simple POCO that I would like to bind to.
public class DataObject {
public DataObject(string value) {
Property = value;
}
public string Property { get; set; }
}
Here is a simple 'Image template' I would like to bind from.
<UserControl x:Class="XamlRenderingExample.Template"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding Property}"></TextBlock>
</Grid>
</UserControl>
Here is a simple class library to create an image from xaml
public class CreateBitmapImagesFromXamlTemplate {
public CreateBitmapImagesFromXamlTemplate() {
Template template = new Template();
foreach (DataObject obj in GetDataObjects())
{
template.DataContext = obj;
RenderXamlAsBitMap(template);
}
}
private void RenderXamlAsBitMap(Template template) {
template.Arrange(new Rect(0, 0, 300, 300));
RenderTargetBitmap bitmap = new RenderTargetBitmap((int)template.RenderSize.Width,
(int)template.RenderSize.Height,
96,
96, PixelFormats.Default);
bitmap.Render(template);
// Save away the bitmap to file.
}
private static List<DataObject> GetDataObjects() {
return new List<DataObject>()
{
new DataObject("Hello"),
new DataObject("Another string")
};
}
}
The problem is that without adding the Xaml to a rendered window in a WPF application setting the datacontext will not perform the desired binding and the result is that the textBlock will not display the data from the DataObject.
As I understand if we were in a WPF application and the template was added to a rendered window when I set the dataContext of the template a job is qued up in the Dispatcher to actually perform the databidning and pull the values from the DataObject to the Template.
What I want to do is be able to perform this routine manually in my class library without the need to instantiate a System.Windows.Application object.
Anyone got any ideas?
After some further tinkering on this and realising what made my first solution work wasn't the addition the user control into the visual tree of the application object, but the queing of jobs on the Dispatcher.
I have modified the above class to que up the jobs which now performs the desired 'databinding' on the saved image using the following code:
public class CreateBitmapImagesFromXamlTemplate {
Template template;
RenderTargetBitmap bitmap;
public CreateBitmapImagesFromXamlTemplate() {
template = new Template();
foreach (DataObject obj in GetDataObjects()) {
RenderXamlAsBitMap(template, obj);
}
}
private void RenderXamlAsBitMap(Template template, DataObject dataObject) {
var renderFrame = new DispatcherFrame(true);
QueueDataBinding(dataObject, renderFrame);
QueueRenderBitmap(renderFrame);
System.Windows.Threading.Dispatcher.PushFrame(renderFrame);
SaveRenderedBitmap(bitmap);
}
private static List<DataObject> GetDataObjects() {
return new List<DataObject>() {
new DataObject("Hello"),
new DataObject("Another string")
};
}
private void SaveRenderedBitmap(RenderTargetBitmap renderedBitmap) {
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(renderedBitmap));
using (FileStream fs = File.Open(@"C:\temp\imageTesting.jpg",
FileMode.OpenOrCreate)) {
encoder.Save(fs);
}
}
private void QueueDataBinding(DataObject dataObject,
DispatcherFrame renderFrame) {
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.DataBind,
(ThreadStart)delegate() {
template.DataContext = dataObject;
renderFrame.Continue = true;
});
}
private void QueueRenderBitmap(DispatcherFrame renderFrame) {
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
(ThreadStart)delegate() {
RenderBitmap();
renderFrame.Continue = false;
});
}
private void RenderBitmap() {
template.Arrange(new Rect(0, 0, 300, 300));
bitmap = new RenderTargetBitmap((int)template.RenderSize.Width,
(int)template.RenderSize.Height,
96,
96, PixelFormats.Default);
bitmap.Render(template);
SaveRenderedBitmap(bitmap);
}
}
精彩评论