I'm trying to use FlowDocument with bindings to have separate template which is able to be filled with actual data from data model. Then I convert it to image and print or save on hard-drive.
For binding FlowDocument's Runs with data model I use the code from this article: https://msdn.microsoft.com/en-us/magazine/dd569761.aspx
The FlowDocument template is following:
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:p="clr-namespace:Labels;assembly=DataModel.Impl"
PageWidth="200" MinPageWidth="200" PageHeight="200" MinPageHeight="200">
<Section>
<Paragraph>
<p:BindableRun BoundText="{Binding Path=Text}"/>
</Paragraph>
</Section>
</FlowDocument>
Code for BindableRun:
public class BindableRun : Run
{
public static readonly DependencyProperty BoundTextProperty = DependencyProperty.Register("BoundText", typeof(string), typeof(BindableRun),
new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure, OnBoundTextChanged, CoerceText));
public BindableRun()
{
FlowDocumentHelpers.FixupDataContext(this);
}
private static void OnBoundTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((Run)d).Text = (string)e.NewValue;
}
private static object CoerceText(DependencyObject d, object value)
{
return value;
}
public String BoundText
{
get { return (string)GetValue(BoundTextProperty); }
set { SetValue(BoundTextProperty, value); }
}
}
Then I load template and set DataContext in it:
private class DataClass
{
public string Text { get; set; }
}
private static FlowDocument LoadFlowDocument(string path)
{
using (var xamlFile = new FileStream(path, FileMode.Open, FileAccess.Read))
{
return XamlReader.Load(xamlFile) as FlowDocument;
}
}
private static void FlowDoc2Image(FlowDocument document, DataClass dataContext, Stream imageStream)
{
var flowDocumentScrollViewer = new FlowDocumentScrollViewer
{
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden,
DataContext = dataContext
};
flowDocumentScrollViewer.Document = document;
flowDocumentScrollViewer.Measure(new Size(999999999, 999999999));
//1st pass
flowDocumentScrollViewer.Arrange(new Rect(0, 0, flowDocumentScrollViewer.ActualWidth, flowDocumentScrollViewer.ActualHeight));
//2nd pass. It's not code duplication! Do not remove!
flowDocumentScrollViewer.Arrange(new Rect(0, 0, flowDocumentScrollViewer.ActualWidth, flowDocumentScrollViewer.ActualHeight));
var bitmapRenderer =
new RenderTargetBitmap((int)flowDocumentScrollViewer.ActualWidth, (int)flowDocumentScrollViewer.ActualHeight, 96, 96, PixelFormats.Pbgra32);
bitmapRenderer.Render(flowDocumentScrollViewer);
var pngEncoder = new PngBitmapEncoder { Interlace = PngInterlaceOption.On };
pngEncoder.Frames.Add(BitmapFrame.Create(bitmapRenderer));
pngEncoder.Save(imageStream);
}
public void Test()
{
var doc = LoadFlowDocument("C:\\Experiments\\DocWithBinding.xaml");
var context = new DataClass {Text = "SomeText"};
doc.DataContext = context;
using (var imageStream = new FileStream("C:\\Experiments\\image.png", FileMode.OpenOrCreate, FileAccess.Write))
{
FlowDoc2Image(doc, context, imageStream);
}
}
But nothing happens. I tried to set break points in BindableRun on changing it's value. And I never get there. Changing DataContext doesn't affect the document.
There is no need anymore for the BindableRun class. From the Remarks section in Run.Text:
Starting in the .NET Framework 4, the Text property of the Run object
is a dependency property, which means that you can bind the Text
property to a data source.
So your FlowDocument file could look like this:
<FlowDocument xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
PageWidth="200" MinPageWidth="200" PageHeight="200" MinPageHeight="200">
<Section>
<Paragraph>
<Run Text="{Binding Text}"/>
</Paragraph>
</Section>
</FlowDocument>
I've loaded this like shown in your question, assigned a DataClass instance to its DataContext and sucessfully displayed it in a RichTextBox:
<Grid>
<RichTextBox x:Name="rtb"/>
</Grid>
Code behind:
private class DataClass
{
public string Text { get; set; }
}
public MainWindow()
{
InitializeComponent();
var doc = LoadFlowDocument("DocWithBinding.xaml");
doc.DataContext = new DataClass { Text = "Hello, World." };
rtb.Document = doc;
}
private static FlowDocument LoadFlowDocument(string path)
{
using (var xamlFile = new FileStream(path, FileMode.Open, FileAccess.Read))
{
return XamlReader.Load(xamlFile) as FlowDocument;
}
}
EDIT Although you are able to successfully put the FlowDocument into a FlowDocumentScrollViewer, it appears that synchronously rendering this viewer into a RenderTargetBitmap does not create the desired output. It feels like the binding is not yet established, as hard-coded text in the document will render synchronously.
I've tried a few things, but I can't seem to get around adding a short delay before rendering to the bitmap. I did this by making the FlowDoc2Image method async and calling await Task.Delay(100). It is a hack, but it creates the PNG.
private async Task FlowDoc2Image(
FlowDocument document, DataClass dataContext, Stream imageStream)
{
var flowDocumentScrollViewer = new FlowDocumentScrollViewer
{
VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden,
Document = document,
DataContext = dataContext,
};
flowDocumentScrollViewer.Measure(
new Size(double.PositiveInfinity, double.PositiveInfinity));
flowDocumentScrollViewer.Arrange(new Rect(flowDocumentScrollViewer.DesiredSize));
await Task.Delay(100);
var renderTargetBitmap = new RenderTargetBitmap(
(int)flowDocumentScrollViewer.DesiredSize.Width,
(int)flowDocumentScrollViewer.DesiredSize.Height,
96, 96, PixelFormats.Default);
renderTargetBitmap.Render(flowDocumentScrollViewer);
var pngEncoder = new PngBitmapEncoder { Interlace = PngInterlaceOption.On };
pngEncoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));
pngEncoder.Save(imageStream);
}
Related
I'm trying to load RTF text from database and display it into datatemplate of custom RichTextBox .
I would like to get the text with style , with code the matter is simple but when I'm trying to use <GridViewDataColumn.CellTemplate> <GridViewDataColumn.CellTemplate/> , it got difficult
Xaml code:
<telerik:GridViewDataColumn DataMemberBinding="{Binding document}" Width="*" x:Name="Description" IsReadOnly="True">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate >
<local:UC_Description x:Name="richtext">
</local:UC_Description>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
C# code:
List= new Controller().GetAll();
foreach (Model item in List)
{
RtfFormatProvider provider = new RtfFormatProvider();
DocumentFormatProvidersManager.RegisterFormatProvider(provider);
byte[] byteArray = Encoding.ASCII.GetBytes(item.Description);
document = provider.Import(byteArray);
FlowDocument flow = new FlowDocument();
}
GridViewList.ItemsSource = null;
GridViewList.ItemsSource = List;
this.DataContext = this;
}
public RadDocument ImportXaml(string content)
{
RtfFormatProvider provider = new RtfFormatProvider();
return provider.Import(text);
}
public string RtfToPlainText(string rtf)
{
byte[] byteArray = Encoding.ASCII.GetBytes(rtf);
var flowDocument = new FlowDocument();
TextRange tr;
using (MemoryStream ms = new MemoryStream(byteArray))
{
tr = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd);
tr.Load(ms, DataFormats.Rtf);
}
return tr.Text;
}
How can I display text from RTF content in a data template?
A template is a template and there is no UC_Description or RichTextBox until it has been applied at runtime.
What you could do is to handle the Loaded event for the UserControl and then set the Document property of the RichTextBox to your document:
XAML:
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate >
<local:UC_Description Loaded="OnLoaded" />
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
Code:
private void OnLoaded(object sender, RoutedEventArgs e)
{
UC_Description uc = (UC_Description)sender;
uc.richTextBox.Document = ...;
}
Is there a way to implement an image within a TextBlock rather than outside of it? Android has a feature called SpaanableStringBuilder which allows the user to create text with different appearances. Does UWP have something similar?
MyTextBlock.Text = "Google" + ? + "icon";
As Raymond said, in UWP, RichTextBlock is needed for image and text mixing.
With Xaml
<RichTextBlock>
<Paragraph>
<Run Text="Something..."/>
<InlineUIContainer>
<Image Source="your_image_url" Width="20" Height="20"/>
</InlineUIContainer>
<Run Text="Something..."/>
</Paragraph>
</RichTextBlock>
With Code
var richTextBlock = new RichTextBlock();
var para = new Paragraph();
para.Inlines.Add(new Run() { Text = "Something..." });
var imgContainer = new InlineUIContainer();
var img = new Image();
img.Source = new BitmapImage(new Uri("your_image_url"));
imgContainer.Child = img;
para.Inlines.Add(imgContainer);
para.Inlines.Add(new Run() { Text = "Something..." });
richTextBlock.Blocks.Add(para);
Or you could write it this way, which more closely mirrors the XAML.
var richTextBlock = new RichTextBlock()
{
Blocks = {
new Paragraph {
Inlines = {
new Run { Text = "Something" },
new InlineUIContainer {
Child = new Image {
Source = new BitmapImage(new Uri("your_image_url"))
}
},
new Run { Text = "Something..."}
}
}
}
};
Here is the pdf sample with texts on the layer. If I turn off the layer all the text belong to this layer will be invisible also.
I need to get all the texts from the specific layer. Any body know how to archive this.
Here is my sample PDF file: https://drive.google.com/file/d/1TcRyE8MQRhw-j89BbovV7fFIwZ0yks0N/view?usp=sharing
My code can get all texts. But I don't know how to get texts belong any specific layer only.
public CreateHyperLinkButton(string inPutPDF, string outPutPDF, List<ViewPortInfo> ViewportInfos)
{
using (FileStream pdf = new FileStream(outPutPDF, FileMode.Create))
{
using (PdfReader pdfReader = new iTextSharp.text.pdf.PdfReader(inPutPDF))
{
using (PdfStamper pdfStamper = new iTextSharp.text.pdf.PdfStamper(pdfReader, pdf))
{
//Get Text list on 2D PDF
List<TextRenderInfo> listTextInfor = GetAllTextInfor(inPutPDF, pdfReader);
listTextInfor.ForEach(item =>{
string btnName = item.GetText().Trim();
//Check btnName exist in ViewportInfos
for (var i = 0; i < ViewportInfos.Count; i++)
{
string szRes = GetTextContained(ViewportInfos[i].Hyperlinks.Keys.ToList(), btnName);
if (!string.IsNullOrEmpty(szRes))
{
iTextSharp.text.Rectangle box = GetRectOfText(item);
iTextSharp.text.pdf.PushbuttonField btnField = new iTextSharp.text.pdf.PushbuttonField(pdfStamper.Writer, box, szRes);
iTextSharp.text.pdf.PdfAnnotation pushbutton = btnField.Field;
//Add JS function and button in annotation
string js = "mapView('" + szRes + "');";
pushbutton.SetAdditionalActions(iTextSharp.text.pdf.PdfName.U, iTextSharp.text.pdf.PdfAction.JavaScript(js, pdfStamper.Writer));
pdfStamper.AddAnnotation(pushbutton, 1);
}
}
});
pdfStamper.Close();
}
pdfReader.Close();
}
pdf.Close();
}
}
private static List<TextRenderInfo> GetAllTextInfor(string inPutPDF, PdfReader pdfReader)
{
List<TextRenderInfo> listTextInfor = new List<TextRenderInfo>();
TextExtractionStrategy allTextInfo = new TextExtractionStrategy();
for (int i = 1; i <= pdfReader.NumberOfPages; i++)
{
PdfTextExtractor.GetTextFromPage(pdfReader, i, allTextInfo);
}
listTextInfor = allTextInfo.textList;
return listTextInfor;
}
public class TextExtractionStrategy : ITextExtractionStrategy
{
public List<TextRenderInfo> textList = new List<TextRenderInfo>();
public void BeginTextBlock()
{
}
public void EndTextBlock()
{
}
public string GetResultantText()
{
return "";
}
public void RenderImage(ImageRenderInfo renderInfo)
{
var a = renderInfo;
}
public void RenderText(TextRenderInfo renderInfo)
{
textList.Add(renderInfo);
}
}
You could use ironpdf for this purpose. Parse/open the pdf as per the docs on their site and examine it in debug, then you can develop some code to retrieve text from that layer only.
i need to display an image which is from a class attribute inside a button, the binding doesn't work.
This is how i proceed:
i have a class Products that contains ProductImage of type system.drawable.image and some other strings.
XAML code :
<Button Content="{Binding ProductImage}" Name="ImageButton"></Button>
the button is showing with Content : System.Drawing.Bitmap in it.
any help Please.
EDIT : i found important to add the Window2.XAML.cs file
MySqlConnection connection = new MySqlConnection(ConnectionManager.ConnectionString);
try
{
connection.Open();
MySqlCommand command = new MySqlCommand("SELECT * FROM products", connection);
MySqlDataReader reader = command.ExecuteReader();
var list = new List<ProductsBLL>();
while (reader.Read())
{
ProductsBLL product = new ProductsBLL();
product.ProductId = (int) reader[0];
product.ProductName = (string) reader[1];
product.ProductReference = (string) reader[2];
product.ProductColor = (string) reader[3];
var imagems = (byte[]) reader[4];
MemoryStream ms = new MemoryStream(imagems);
product.ProductImage = System.Drawing.Image.FromStream(ms);
product.ProductDescription = (string) reader[5];
product.ProductUnitPrice = (decimal) reader[6];
product.ProductUnitInStock = (int) reader[7];
product.ProductUnitInCommand = (int) reader[8];
list.Add(product);
product = null;
}
ProductsListView.ItemsSource = list;
}
catch (Exception e)
{
MessageBox.Show(this, e.Message);
}
finally
{
if(connection.State == ConnectionState.Open)
connection.Close();
}
The type of your ProductImage property is currently System.Drawing.Image, which is WinForms, not WPF. A System.Drawing.Image can not be drawn directly by a WPF Image control.
You should change the property type to System.Windows.Media.ImageSource:
public ImageSource ProductImage { get; set; }
Now you would create an instance of either System.Windows.Media.Imaging.BitmapImage or
System.Windows.Media.Imaging.BitmapFrame as value of the property. Note that the BitmapCacheOption.OnLoad must be set, because you want to dispose of the stream right after loading the image.
byte[] imageBuffer = ...
using (var memoryStream = new MemoryStream(imageBuffer))
{
product.ProductImage = BitmapFrame.Create(
memoryStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
}
or
using (var memoryStream = new MemoryStream(imageBuffer))
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = memoryStream;
bitmapImage.EndInit();
product.ProductImage = bitmapImage;
}
Finally (as already said in the other answer) you would have to use an Image control as the Button's Content:
<Button Name="ImageButton">
<Image Source="{Binding ProductImage}" />
</Button>
Try to use Image control as place holder for Bitmap :
<Button Name="ImageButton">
<Image Source="{Binding ProductImage}" />
</Button>
I've made a application that uses live tiles. I get it to work with the backgroundtask, it shows correct message but the image is completely black...
Here is my code where I save the image to /Shared/ShellContent/:
public MainPage()
{
InitializeComponent();
CurrentPlaceList = new ObservableCollection<CurrentPlaceListItemModel>();
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
var bmp = new WriteableBitmap(173, 173);
var weatherImage = new BitmapImage(new Uri("/Images/WeatherIcons/01d.png", UriKind.Relative));
var img = new Image { Source = weatherImage };
weatherImage.CreateOptions = BitmapCreateOptions.None;
var tt = new TranslateTransform();
tt.X = 0;
tt.Y = 0;
bmp.Render(img, tt);
bmp.Invalidate();
var filename = "/Shared/ShellContent/01d.jpg";
using (var st = new IsolatedStorageFileStream(filename, FileMode.OpenOrCreate, FileAccess.Write, store))
{
bmp.SaveJpeg(st, 173, 173, 0, 100);
}
}
StartAgent();
}
And where is my code where my ScheduledTask is supposed to update text and image, the images is completely black :(
protected override void OnInvoke(ScheduledTask task)
{
//TODO: Add code to perform your task in background
UpdateAppTile("-99");
NotifyComplete();
}
private void UpdateAppTile(string message)
{
ShellTile appTile = ShellTile.ActiveTiles.FirstOrDefault();
if (appTile != null)
{
StandardTileData tileData = new StandardTileData
{
Title = message,
BackgroundImage = new System.Uri("isostore:/Shared/ShellContent/01d.jpg", System.UriKind.Absolute)
};
appTile.Update(tileData);
}
}
This is taken from several tutorials, could anyone put a finger on what is wrong?
For me it looks like the image doesn't have enough time to load. I think the better way here is to do something like:
StreamResourceInfo streamImage = Application.GetResourceStream(uri.Uri);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(streamImage.Stream);
Image image = new Image() { Width = uri.Width, Height = uri.Height, Source = bitmapImage };
Also, you can check out a MSP Toolkit that could generate tile for you (msptoolkit on NuGet).