converting html to FlowDocument - wpf

I try to create a wpf core application that uses text imported from a ms Access database.
Some of the fields are Access Text fields with text set as RTF text but actually they look like html. like this:
10630981 bla bla bla bla bla {bla 25-09}
I was thinking to use a FlowDocumentScrollViewer to display this field and a RichTextBox to edit.
since I like to work in a MVVM pattern I would need a convertor to convert this 'Html' to a flowdocument and back.
I have been playing for several days no to get this, but did not succeed yet.
I feel I am getting near with following Code:
FlowDocument document = new FlowDocument();
string xaml = "<p> The <b> Markup </b> that is to be converted.</p>";
using (MemoryStream msDocument = new MemoryStream((new ASCIIEncoding()).GetBytes(xaml)))
{
TextRange textRange = new TextRange(document.ContentStart, document.ContentEnd);
textRange.Load(msDocument, DataFormats.Xaml);
}
But still I get an exception saying XamlParseException: Cannot create unknown type 'p'.
Can somebody give me a push in the right direction?

You are using the wrong dataformat. Your content is not a valid XAML string. Simply use DataFormats.Text instead.
If you use DataFormats.Xaml the input is expected to be a valid document based on XAML elements like <Run> and <Paragraph>.
You can also assign the string value directly to the TextRange.Text property:
FlowDocument document = new FlowDocument();
string html = "<p> The <b> Markup </b> that is to be converted.</p>";
TextRange textRange = new TextRange(document.ContentStart, document.ContentEnd);
textRange.Text = html;

Related

WPF Dynamic HyperLinks RichTextbox

I have seen several posts on various forms varying in times from 2006 to now about how to add hyperlinks to RichTextBox, but they all seem overly complex for what I want. I am creating a desktop chat client, and I receive input as strings, now among these strings may be some urls, I need those urls to be clickable. Which from what I gather means they need to be HyperLink objects.
Navigating through the RichTextBox and replacing the urls with HyperLinks seems to be no small feat. Does anyone have a relatively simple solution for this?
In my web client it's a simple one liner
value = value.replace(/(http:\/\/[^\s]+)/gi, '$1');
Never thought I'd see the day where C# actually makes it harder.
If you want to do an equivalent of value.replace(/(http:\/\/[^\s]+)/gi, '$1') in WPF:
<RichTextBox x:Name="MyRichTextBox" IsDocumentEnabled="True" IsReadOnly="True" />
And the code that converts the string is the following:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var htmlText = "Google's website is http://www.google.com";
MyRichTextBox.Document = ConvertToFlowDocument(htmlText);
}
private FlowDocument ConvertToFlowDocument(string text)
{
var flowDocument = new FlowDocument();
var regex = new Regex(#"(http:\/\/[^\s]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
var matches = regex.Matches(text).Cast<Match>().Select(m => m.Value).ToList();
var paragraph = new Paragraph();
flowDocument.Blocks.Add(paragraph);
foreach (var segment in regex.Split(text))
{
if (matches.Contains(segment))
{
var hyperlink = new Hyperlink(new Run(segment))
{
NavigateUri = new Uri(segment),
};
hyperlink.RequestNavigate += (sender, args) => Process.Start(segment);
paragraph.Inlines.Add(hyperlink);
}
else
{
paragraph.Inlines.Add(new Run(segment));
}
}
return flowDocument;
}
}
It uses the same regular expression you provided, which is lacking if you properly want to recognize URLs with a regular expression. This one doesn't recognize https ones and the last dot in the following sentence would be a part of the URL: "This is a URL: http://www.google.com/."
What the code does is to split the text based on the regular expression, iterate it and adds the correct elements to the FlowDocument constructed on the fly.
Clicking the Hyperlink should open your default browser.
Result:
That said, this is only good for read only usage of the RichTextBox (as indicated by the question in the comment).

Printing RTF with headers and footers - FlowDocument or something else?

I've been looking at importing an RTF into a flowdocument, obstinately for the purposes of unattended printing; after a lot of poking, using a FlowDocument seemed to be the right approach. I've got the RTF to generally work, however footers disappear when loading the RTF into a FlowDocument.
The RTF was generated in Word, and when loaded into Wordpad, the footers are visible, so I can only assume I'm loading the document wrong, or it's an issue with the default paginator, or possibly both.
This is what I have so far, which loads the file and does the printing:
[STAThread]
public static int Main(string[] args)
{
var documentPath = #"C:\temp\Example.rtf";
var fileStream = File.Open(documentPath, FileMode.Open, FileAccess.Read, FileShare.Read);
// Load the RTF into the flow document
var flowDocument = new FlowDocument();
TextRange textRange = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd);
textRange.Load(fileStream, DataFormats.Rtf);
flowDocument.ColumnWidth = double.PositiveInfinity;
var t = new Thickness(72);
flowDocument.PagePadding = t; // Page margin
// Get printer
var queue = LocalPrintServer.GetDefaultPrintQueue();
var capa = queue.GetPrintCapabilities();
// Configure paginator
var paginator = ((IDocumentPaginatorSource)flowDocument).DocumentPaginator;
paginator.PageSize = new Size(capa.OrientedPageMediaWidth.Value, capa.OrientedPageMediaHeight.Value);
// ...and print.
var writer = System.Printing.PrintQueue.CreateXpsDocumentWriter(queue);
writer.Write(paginator);
return 0;
}
...beyond that, I'm at a loss. It's not clear whether the footers simply haven't loaded (and thus aren't part of the FlowDocument) - I suspect this is the case as we're loading into a TextRange that marks the start and end of the main FlowDocument content; but I'm also suspecting I might need a custom paginator as well.
Are there any pointers to the docs I'm missing - a google search for "RTF footer import flowdocument paginator" (and similar) has (so far) revealed no pertinent results. The closest I've so far found is this SO question, but this doesn't cover fetching the header/footer from an RTF file.
Take a look at this MSDN blogpost they are doing almost the same you are doing. With a custom DocumentPaginator to scale the the original pages. I think this should do the trick. I also used it a long time ago and headers and footers were no problem.
To provide the header and footer property during printing, we usually handle the PrintPage event and provide these features in this event handler. Here is a sample for your reference. Although this sample is a printer class for DataGridView, you can take the logic of add the header and footer features.
· http://www.codeproject.com/KB/cs/DGVPrinter.aspx
If you are using the WebBrowser to print the HTML, you can custom the header and footer like mentioned in the following article.
· http://www.codeproject.com/KB/miscctrl/tips.aspx#tip3.8.2

WPF Printing - Set the Printer Automataically on WPF PrintDialog

I am trying to print a WPF FlowDocument to a particular printer, without prompting the user. The printer is a PDF converter.
This works great except that it prints to the default printer:
PrintDialog pd = new PrintDialog();
var doc = ((IDocumentPaginatorSource) RTB.Document).DocumentPaginator;
// I would like to explicitly set the printer to print to here.
pd.PrintDocument(doc, "Print Document");
In WinForms there is a System.Drawing.Printing.PrinterSettings object on document which has a PrinterName property which can be set to the printer I want, but I don't see that in WPF.
You first need a reference in your project to System.Printing. Then you can use the following code right after you declare your PrintDialog object.
pd.PrintQueue = new PrintQueue(new PrintServer(), "The exact name of my printer");
The PrintQueue is an object that represents the printer and everything else about that print queue.
This worked for me, when I used a shared network printer:
xPrintDialog.PrintQueue = New PrintQueue(New PrintServer("\\computer name"), "printer name")

Providing the WPF designer with an image at DesignTime

This is a restatement of my question, the revision history contains the original mess.
What it boils down to is "How do I get the application's directory from my WPF application, at design time?"
Which duplicates the question here so if you happen to be passing by please vote to close, thanks.
Do you need the image to be "Content - Copy if newer"? If you switch it to "Resource" you can use the following path to reference the file:
"/MyImage.JPG"
or a longer version
"pack://application:,,,/MyImage.JPG"
given that the image is in the root of the project, otherwise just change the URI to
"/Some/Path/MyImage.JPG"
UPDATE 1:
For me, the longer pack uri syntax works with an image marked as "Content - Copy if newer" as well. However, the shorter syntax does not work. I.e:
This works:
"pack://application:,,,/MyImage.JPG"
This does NOT work:
"/MyImage.JPG"
I my example I added the image to the root of the project, and marked it as "Content". I then bound the design time data context to a view model with a property returning the longer pack URI above. Doing that results in the Content image being shown correctly at design time.
UPDATE 2:
If you want to load a bitmap source from a pack uri, you can do so by using another overload of the BitmapFrame.Create which takes an URI as the first parameter.
If I understand your problem correctly you get the string with the pack uri as the first item in the object array that is passed to your converter. From this string you want to load a BitmapSource.
Since the string contains a pack URI, you can create an actual URI from the string and then use that URI to load the BitmapSource:
var imagePath = values[0] as string;
// ...
try
{
var packUri = new Uri(imagePath);
BitmapSource bitmap = BitmapFrame.Create(packUri, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
{
// ...
}
}
}
Just return the exact path of the image from your entity in ImagePath property, such as ..
"C:\MyImage.JPG"
..
OR
..
"C:\MyApp\bin\Debug\MyImage.JPG"
Then your binding (i.e. <Image Source="{Binding ImagePath}" />) in .xaml will start working..
I solved it by leveraging the clevers found in this stackoverflow answer.
public class DMyViewModel : PhotoViewModelBase
{
public override string ImagePath
{
get
{
string applicationDirectory =
(from assembly in AppDomain.CurrentDomain.GetAssemblies()
where assembly.CodeBase.EndsWith(".exe")
select System.IO.Path.GetDirectoryName(assembly.CodeBase.Replace("file:///", ""))
).FirstOrDefault();
return applicationDirectory + "\\MyImage.JPG";
}
}
}

Showing Bitmap Images in WPF via C#

What I am trying to do is so simple but I am having a hard time making it work. I saw some posts along the same lines but I still have questions.
I have a MenuItem object called mnuA. All I want is set the icon property programatically in C#. I have tried the following
a) mnuA.Icon = new BitmapImage{UriSource = new Uri(#"c:\icons\A.png")};
Results: Instead of showing the actual icon, I get the class name (System.Windows.Media.Imaging.BitmapImage)
b) mnuA.Icon = new BitmapImage(new Uri(#"c:\icons\A.png"));
Results: Instead of showing the actual icon, I get the path of the image (file:///c:/icons/A.png)
What am I doing wrong? Do I really need a converter class for something simple like this?
Try this:
Image img = new Image();
img.Source = new BitmapImage(new Uri(#"c:\icons\A.png"));
mnuA.Icon = img;
Might be a long shot, but try something like:
Uri u = new Uri(...); mnuA.Icon = new
BitmapImage(u);
What it seems its happening is that your icon is getting converted to a string.

Resources