I'm trying to use the Silverlight RichTextEditor in our website. Now we'd like to translate the content in richtextbox into HTML code to save and load.
However, as we know, the richtextbox control does not support the UIelements output. When we insert an image in the richtextbox, the richtextbox would use a inlineUIcontainer to show this image. The property Richtextbox.xaml does not include any information about the image. It just shows the code like "".
Does anyone have this problem and handle it before?
RichTextBox.Xaml strips out a lot of things, as a security safeguard (more for the setter than the getter as far as I recall, but it does it both ways so there are no round-trip surpises).
I recommend looking at the XAML Serializer written by David Poll on his blog (here: http://www.davidpoll.com/2010/07/25/to-xaml-with-love-an-experiment-with-xaml-serialization-in-silverlight/ ) as it can serialize RTB awesomely well (it's in fact one of the test cases he shows). David was a PM on the Silverlight XAML Parser in SL4, so he knows an awful lot about XAML.
But be careful when setting the .Xaml property, as you could mistakenly end up spinning up InlineUIContainer elements which load resources into your AppDomain that you don't want in there, so make sure you control the inputs or you strip them yourself very carefully.
Related
I need to write XAML of a WPF control to string. I used XamlWriter.Save(). It works good. Also I need to format, indent and show the string in a editor (read only) like VS Xaml code view. I dont know how to do this.
Please share your valuable ideas.
You can save to a XmlWriter instance, create a XmlWriter, create a XmlWriterSettings instance and set the Indent property to true and set this on your XmlWriter. That way you should have the indentation.
Now for a control to display it, you could use the RichTextBox in wpf, or look for a custom control around the web.
I have a RichTextBox and need to serialize its content to my database purely for storage purposes. It would appear that I have a choice between serializing as XAML or as RTF, and am wondering if there are any advantages to serializing to XAML over RTF, which I would consider as more "standard".
In particular, am I losing any capability by serializing to RTF instead of XAML? I understand XAML supports custom classes inside the FlowDocument, but I'm not currently using any custom classes (though the potential for extensibility might be enough reason to use XAML).
Update: I ended up going with RTF because of its support for text-encoded embedded images. XAML doesn't seem to include image data in its encoding, and XamlPackage encodes to binary, so RTF just works better for me. So far I haven't noticed any lack in capability.
If all your users are doing is typing in the RichTextBox and doing character formatting, RTF is as good as XAML. However there are many FlowDocument capabilities you may expose in your UI that are not convertible to RTF.
Here are some examples of FlowDocument (and RichTextBox) features that are not expressable in RTF or are implemented differently:
A Block can have an arbitrary BorderBrush, including gradient brushes with stops, VisualBrush
A Section has the HasTrailingParagraphBreakOnPaste property
Floater / ClearFloaters is implemented differently
Hyphenation can be enabled/disabled per block, not just per paragraph
WPF Styles and ResourceDictionaries can be included in the Resources property
Arbitrary WPF UI such as bound CheckBoxes, etc, can be embedded inside the RichTextBox and can be cut-and-pasted from other windows.
For example, suppose you want to allow users to drag or cut/paste in a "current date/time" field into your RichTextBox that would always show the current date and time. This could be done by adding a second read-only RichTextBox that has the InlineUIContainer and the already-bound control. This even works when cutting and pasting from other applications and does not require custom controls.
Another consideration is that the code to convert between FlowDocument and RTF is relatively complex so it may have lower performance than going with XAML. Of course loose XAML doesn't include images and such - for that you need to use XamlPackage. I store my XamlPackage in the database as a byte[], but you can also choose to Base64 encode it for storage as a string.
The bottom line is that it really depends on whether you want the user to be able to use features not available in RTF. Even if your application doesn't include tools to generate FlowDocuments that use these features, it is possible to cut-and-paste them from other applications.
Be aware that in Wpf RichTextBox's method called TextRange.Save has a bug whereby it loses any end of line terminator. Microsoft will not fix.
https://connect.microsoft.com/WPF/feedback/ViewFeedback.aspx?FeedbackID=478640&wa=wsignin1.0#tabs
I need to host WPF control inside IE, therefore I'm trying to implement IHTMLPainter and IElementBehavior interfaces. I'd like to build my custom behavior and use it inside IE, but the problem is how to draw WPF control by just having IntPtr hdc parameter.
Probably I can get Drawing.Graphics by the following code:
Graphics.FromHdc(hdc);
But I'm not sure that this is the best way. Please advise
I'm assuming you want to be able to make use of the advanced features of WPF within a MSHTML context. In that case, Graphics.FromHdc(hdc); will not do the trick for you. The resulting Graphics object will have no way to receive WPF content because WPF uses a retained-mode system and its MILCore rendering engine uses Direct3D not GDI+.
I'll give you one sure way to use WPF features inside a IHTMLPainter, plus pointers to another way that would likely be faster if you can get it to work.
Bitmap copying solution
An easy solution is to simply copy the background provided by MSHTML into an ImageBrush, use RenderTargetBitmap to let WPF render to a bitmap, then copy it back to the device.
To do this, construct your WPF content in any Visual subclass with a Background property (eg Grid or Border), then in your IHTMLPainter.Draw() method, just do the following:
Create a System.Drawing.Bitmap corresponding to rcUpdate
BitBlt from the given DC into the System.Drawing.Bitmap
Construct an ImageSoure from the System.Drawing.Bitmap (see recent SO answers for details)
Construct an ImageBrush from the BitmapSource using a viewport/viewbox that will lay it behind the portion of the visual corresponding to rcUpdate
Set your root visual's background to the ImageBrush
Set the RenderTransform on the root visual so that the rcUpdate portion starts at (0,0)
Render the root visual to a RenderTargetBitmap of rcUpdate size
BitBlt the RenderTargetBitmap to the rcUpdate area of the DC
This should work well, be simple to implement, and work for any WPF content including advanced features such as 3D, BitmapEffects, etc. The only disadvantage is that those two bitmap copies might slow things down somewhat.
Note that if you know your WPF Visual is totally opaque you can completely skip steps 1-5 and simply render your Visual to a RenderTargetBitmap and BitBlt it to the device.
Direct3D possibility (partial solution)
Obviously it would be faster to avoid all this bitmap copying during render. This is most likely possible, but I can only give you some ideas to point the way -- it will take a lot of trial and error and probably some undocumented calls to make it work.
Since WPF renders using Direct3D, obviously you would prefer to get a Direct3D surface from MSHTML and paint on it. Doing this requires two things: Getting the surface from MSHTML, and getting MILCore to draw on it.
IHTMLPainter has a flag HTMLPAINTER_3DSURFACE to request a Direct3D surface in its GetPainterInfo call, but I couldn't find any examples of how to use HTMLPAINTER_3DSURFACE. I suspect it could be figured out with a little trial and error.
I did not find any way to get WPF's native component "MILCore" to accept a Direct3D surface to paint on instead of a hWnd. There is no documentation on MILCore, and the only public API for setting up rendering tree, HwndSource, doesn't seem to be able to do the job.
Rendering behaviors through IHTMLPainter and IElementBehavior are meant to alter or supplement the display of existing elements in a page, not to render content for user controls. If you're looking to use WPF controls in a page, this is not the path to take. Instead, consider creating a blank windowed UserControl with ActiveX support, then do either of the following.
Add your WPF control at runtime as a member of the UserControl.
Perform WPF activities using the window handle (HWND) of the control.
Alternatively, you could just use Silverlight to make user controls. Silverlight has a pretty good subset of WPF display features, and even manually constructed Silverlight content is easier to manage than trying to get .NET Windows + ActiveX Hosting + WPF working.
If I've mistaken your question and you're truly intent on using WPF to perform drawing activities in an element behavior, Graphics.FromHdc() is an acceptable way to get a usable Graphics object. You should attach to the HDC specified in the Draw() callback.
Draw Method (IHTMLPainter) # MSDN
You could also attach to the window handle (HWND) of the document view (retrieved via IOleWindow), if your WPF activities involve the entire viewport. The window object can be cast to IOleWindow for this purpose (see IHTMLWindow2).
IOleWindow Interface # MSDN
IHTMLWindow2 Interface # MSDN
What would be a good approach to display and edit large amount of unformatted text (just like notepade does) using WPF? Loading a big string into a TextBox makes the UI unresponsive. The overall performance is not nearly comparable with TextBox Controls of previous Microsoft UI Frameworks.
What options do I have to solve this problem. I do not want to block the UI thread while the text control loads the text. Also I might need some sort of "virtualization" because it might not be a good idea to load the whole text into the control (I guess that 20MB of text would create a lot of glyphs even if they are not visible). It seems that TextBox doesn't even have an AppenText() Method anymore so I don't even have a way to control asynchronous loading of the text.
Isn't this a common problem? It seems that WPF does not provide anything for this out of the box. Why is this so?
AvalonEdit, the text editor in SharpDevelop, was written completely from scratch in WPF and is optimized for large amounts of text. It doesn't support rich text (although it does support syntax highlighting and other cool features such as folding). I think this might fit your bill perfectly.
Here is an article on the editor written by the developer:
http://www.codeproject.com/KB/edit/AvalonEdit.aspx
I am not sure if this helps, but have you tried using FlowDocumentPageViewer and FlowDocumentReader?
It also has very good annotations support and looks ideal for loading documents in text format.
The problem is that the TextBox is a single container element. List controls, such as ListBox virtualize very well because of container recycling. There really isn't anything simple that you can do to speed up the TextBox.
But the TextBox control does have an AppendText() method:
TextBox tb = new TextBox();
tb.AppendText("Hello");
So yes, you can use this to dynamicly add some text just like you mentioned.
You can just use a textbox with a style that gives the user more room to view the text. There are probably more advanced controls from Telerik and others but if you don't require editing options that should suffice.
You could always mix and match technologies: you could drop a WinForms TextBox onto a WPF parent. You lose things like styling, opacity, animation, transforms, etc., but if all that matters is editing text, the WinForms TextBox does that just fine.
Have you tried the WPF RichTextBox? You'll definitely want to read up on the FlowDocument information if you go this route.
You could use FlowDocument, but this doesn't work out of the box to bind to the Document property of a FlowDocument in MVVM.
Another solution is using FlowDocumentScrollViewer and bind to its Document property.
(or you could even use a FlowDocumentReader and bind its Document property, similar to the FlowDocumentScrollViewer. This gives you a different UI.)
The View:
<FlowDocumentScrollViewer Document="{Binding FlowDocument, Mode=OneWay}" />
The ViewModel:
FlowDocument fd = new FlowDocument();
Paragraph p = new Paragraph();
Run r = new Run();
r.Text = "large text";
p.Inlines.Add(r);
fd.Blocks.Add(p);
FlowDocument = fd;
private FlowDocument _FlowDocument;
public FlowDocument FlowDocument
{
get{ return _FlowDocument; }
set
{
_FlowDocument = value;
NotifyOfPropertyChange(nameof(FlowDocument));
}
}
see also this for extra performance tips: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/optimizing-performance-text#flowdocument-textblock-and-label-controls
How about trying something like this:
Keep the whole string in memory but show only a 'slice' of it in the textbox. Size of the that sliced string would be dynamically calculated depending on the size of textbox, font size etc.
Of course this involves a lot of not trivial code for proper displaying, synchronizing and so on, but it seems the way to go.
One other option is to use Scintilla.NET. It has the best performance for large amounts of texts I have seen so far. Loading large files is almost instantaneous. Even though it is a WinForms control, you can embed it into your WPF window with WindowsFormsHost container. There is syntax highlighting and some other features that should be more than enough for displaying unformatted text. From the downsides - since this is a WinForms control, it overlaps other WPF UI elements. Also there may be issues with resizing the control. Unfortunately, I don't see any better option than using a non-WPF control instead of the default TextBox and RichTextBox.
How would you render a tag cloud within a .NET 2.0+ WinForm application?
One solution that I am thinking of would be using the WebBrowser control and generating to some ad-hoc HTML, but that seems to be a pretty heavy solution.
Am I missing something more simple?
How about creating a user control that implements the Flow layout control? You could have a method for "Add(string tagName)" that would create a link label on the fly and add it to the Flow Layout control. The Flow Layout works just like the web, in that controls added to it are put in the order of creation.
Then you only have to add some logic to resize the Link Label based on hit count for that tag.
Well, you'll want a control with these major features:
Automatic layout of variable sized string snippets
Automatic mouse hit testing
Those are a bit hard to come by in WF controls. A RichTextBox with ReadOnly = true gives you the automatic layout, but not the hit testing. A ListBox with DrawItem can give you variable sized strings and hit testing, but not a natural layout.
I think I would use RTB and make hit testing work with the MouseDown event and GetCharIndexFromPosition(), reading back the tag at the clicked location. You'll need a bit of logic to find the starting and ending white space around the word.