I'm using the richtextbox to show some Html content in windows phone 7.1.
The html source-code is like:
Paragraph1</p>
<img src="http://www.ifanr.com/wp-content/uploads/2011/11/DSC_332401.jpg" alt="" width="600" height="338" /></p>
Paragraph2。</p>
<h3>Title h3</h3>
Paragraph3。
</p>
Then I use the
"string[] sArray = Regex.Split(html, "</p>", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);"
to split them into a Array. Finally, I use the code:
foreach (string array in sArray)
{
Paragraph parag = new Paragraph();
Run run = new Run();
Bold bold = new Bold();
if (!Regex.IsMatch(array.ToString(), #"<img\b[^<>]*?\bsrc\s*=\s*[""']?\s*(?<imgUrl>[^\s""'<>]*)[^<>]*?/?\s*>"))
{
//h3
if (array.ToString().Contains("</h3>"))
{
string hString = array.ToString();
hString = Regex.Replace(hString, "<h3>", "");
string[] hArray = Regex.Split(hString, "</h3>", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
bold.Inlines.Add(hArray[0].ToString());
parag.Inlines.Add(bold);
run.Text = hArray[1].ToString();
parag.Inlines.Add(run);
}
else
{
if(array.ToString().Contains("<blockquote>"))
{
run.Text = Regex.Replace(array.ToString(), "<blockquote>", "blockquote:");
run.FontSize = 18;
}
else
run.Text = array.ToString();
parag.Inlines.Add(run);
}
rtb.Blocks.Add(parag);
}
else
{
//insert the image into richtextbox
Regex regImg = new Regex(#"http://[^\[^>]*?(gif|jpg|png|jpeg|bmp|bmp)", RegexOptions.IgnoreCase);
MatchCollection matches = regImg.Matches(array.ToString());
string result = null;
foreach (Match match in matches)
result = match.Value;
Image image = new Image();
image.Stretch = Stretch.Uniform;
image.Source = new BitmapImage(new Uri(result, UriKind.RelativeOrAbsolute));
InlineUIContainer iuc = new InlineUIContainer();
iuc.Child = image;
parag.Inlines.Add(iuc);
rtb.Blocks.Add(parag);
}
to add some Paragraph or images into the richtextbox, everything goes well in the beginning, but when I Scroll down the richtextbox, the rest paragraph disappear. It confused me all day long, as I could't find out what's wrong with the richtextbox.
Is it just a bug in Windows phone? Any thoughts?
ScreenShot1:
ScreenShot2:
p.s:it doesn't matter whether the html source-code contains some non-english characters or not. This happens when the html source-code is in a large amount of words. These two ScreenShots just show the problem.
The phone applies a restriction that any UIElement can't be larger than 2048 pixels in any direction. This is enforced to avoid performance issues relating to memory and having to draw very large objects. This is to protect you from doing something that greatly affects performance but also has some other reasoning behind it. For example, a phone is a poor device for reading large pieces of text. This applies even more so for dense bodies of text. This size restriction therefore forces you to think about how, or if you should, display large pieces of text within your application.
There are some solutions though.
Rather than using a single Paragrpah or TextBlock for a large "unit" of text, you could consider using something like this: http://blogs.msdn.com/b/priozersk/archive/2010/09/08/creating-scrollable-textblock-for-wp7.aspx
Related
Is it possible to highlight a part of a text without selecting this part of the text preferably with a different color in Textbox or Rich TextBox? In fact, I mean, a part of the text is highlighted by another color differing from the color assigned for text selection. To clarify, I have attached an image showing this behavior. (The image is from a website, not WPF).
The bold and dark green part is a text which is just highlighted, and the gray region is a selected part.
Using the RichTextBox element allows for more styling options which, to my knowledge, aren't available for the regular TextBox element.
Here is an approach that I have created:
// Generate example content
FlowDocument doc = new FlowDocument();
Run runStart = new Run("This is an example of ");
Run runHighlight = new Run("text highlighting in WPF");
Run runEnd = new Run(" using the RichTextBox element.");
// Apply highlight style
runHighlight.FontWeight = FontWeights.Bold;
runHighlight.Background = Brushes.LightGreen;
// Create paragraph
Paragraph paragraph = new Paragraph();
paragraph.Inlines.Add(runStart);
paragraph.Inlines.Add(runHighlight);
paragraph.Inlines.Add(runEnd);
// Add the paragraph to the FlowDocument
doc.Blocks.Add(paragraph);
// Apply to RichTextBox
YourRichTextBoxHere.Document = doc;
View Screenshot
I found this article to be helpful.
Highlight Searched Text in WPF ListView
While the article is about highlighting searched text in a ListView, I have easily adapted it in my own code to work with pretty much any control.
Starting with the control you pass in, it will recursively look for TextBlocks and will find the text you want, extract it as an inline, and will change it's Background / Foreground properties.
You can easily adapt the code to be a behavior if your want.
Here is an example:
private void HighlightText(object controlToHighlight, string textToHighlight)
{
if (controlToHighlight == null) return;
if (controlToHighlight is TextBlock tb)
{
var regex = new Regex("(" + textToHighlight + ")", RegexOptions.IgnoreCase);
if (textToHighlight.Length == 0)
{
var str = tb.Text;
tb.Inlines.Clear();
tb.Inlines.Add(str);
return;
}
var substrings = regex.Split(tb.Text);
tb.Inlines.Clear();
foreach (var item in substrings)
{
if (regex.Match(item).Success)
{
var run = new Run(item)
{
Background = (SolidColorBrush) new BrushConverter().ConvertFrom("#FFFFF45E")
};
tb.Inlines.Add(run);
}
else
{
tb.Inlines.Add(item);
}
}
}
else
{
if (!(controlToHighlight is DependencyObject dependencyObject)) return;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i++)
{
HighlightText(VisualTreeHelper.GetChild(dependencyObject, i), textToHighlight);
}
}
}
I hope this is helpful!
I have a RichTextBox. I have added RTF formatting (mainly a color table) to this RichTextBox. When I first Append text to it, it loops through all* the colors of the color table.
*It starts with applying color0, then color1, then color2, etc until all the colors in the color table have been applied OR if the text that is being output has one of those colors already - in that case it stops this "looping" and continues as intended. See screenshot for example.
Here is the code:
private void populateColorCodeDictionary() {
startRTFString = #"{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fswiss\fcharset0;}}" +
#"{\colortbl
;
\red0\green0\blue0;
\red170\green0\blue0;
\red0\green170\blue0;
\red128\green128\blue0;
\red0\green0\blue128;
\red128\green0\blue128;
\red0\green128\blue128;
\red127\green127\blue127;
\red85\green85\blue85;
\red255\green0\blue0;
\red0\green255\blue0;
\red255\green255\blue0;
\red0\green0\blue255;
\red255\green0\blue255;
\red0\green255\blue255;
\red255\green255\blue255;
}";
colorCodeDictionary.Add("\x1b[0;30m", #"\cf1");
colorCodeDictionary.Add("\x1b[0;31m", #"\cf2");
colorCodeDictionary.Add("\x1b[0;32m", #"\cf3");
colorCodeDictionary.Add("\x1b[0;33m", #"\cf4");
colorCodeDictionary.Add("\x1b[0;34m", #"\cf5");
colorCodeDictionary.Add("\x1b[0;35m", #"\cf6");
colorCodeDictionary.Add("\x1b[0;36m", #"\cf7");
colorCodeDictionary.Add("\x1b[0;37m", #"\cf8");
colorCodeDictionary.Add("\x1b[1;30m", #"\cf9");
colorCodeDictionary.Add("\x1b[1;31m", #"\cf10");
colorCodeDictionary.Add("\x1b[1;32m", #"\cf11");
colorCodeDictionary.Add("\x1b[1;33m", #"\cf12");
colorCodeDictionary.Add("\x1b[1;34m", #"\cf13");
colorCodeDictionary.Add("\x1b[1;35m", #"\cf14");
colorCodeDictionary.Add("\x1b[1;36m", #"\cf15");
colorCodeDictionary.Add("\x1b[1;37m", #"\cf16");
/*
\x1b[0;30m = cf1 = black
\x1b[0;31m = cf2 = red
\x1b[0;32m = cf3 = green
\x1b[0;33m = cf4 = brown
\x1b[0;34m = cf5 = blue
\x1b[0;35m = cf6 = purple
\x1b[0;36m = cf7 = cyan
\x1b[0;37m = cf8 = gray
\x1b[1;30m = cf9 = darkGray
\x1b[1;31m = cf10 = light Red
\x1b[1;32m = cf11 = light green
\x1b[1;33m = cf12 = yellow
\x1b[1;34m = cf13 = light blue
\x1b[1;35m = cf14 = indigo
\x1b[1;36m = cf15 = light cyan
\x1b[1;37m = cf16 = white
*/
}
The above method sets up the variables. The most interesting part is the startRTFString variable.
private void updateOutputWindow(string text) {
string newText = string.Empty;
if (InvokeRequired) {
Invoke(new MethodInvoker(delegate () {
updateOutputWindow(text);
}));
}
else {
newText = startRTFString;
newText += rtb_outputWindow.Rtf;
newText += replaceAnsiColorCodes(text);
rtb_outputWindow.Rtf = newText;
}
}
The above method outputs text to the RichTextBox.
private string replaceAnsiColorCodes(string inData) {
string returnString = inData;
foreach (KeyValuePair<string, string> entry in colorCodeDictionary) {
returnString = returnString.Replace(entry.Key, entry.Value);
}
returnString = returnString.Replace("\r", #"\line"); //Newline
returnString = returnString.Replace("\x1b[0;1m", ""); //Bold
returnString = returnString.Replace("\x1b[0m", #"\cf16 "); //Reset
return returnString;
}
The above method converts ANSI codes into RTF color codes. (As well as newline and bold. I have chosen to set bold to be nothing, as of now.)
for (int i = 0; i < 15; i++) {
updateOutputWindow("\x1b[0mline" + i.ToString());
}
The above is just a little loop that I run as soon as the application has started. It is for testing purposes and can be seen in the following screenshot:
Above is a screenshot of the issue. As you can see, the first 15 lines of text all have different colors. They should all have the same, default, color (in my case white.) After the "looping" is done, it continues to work as intended; applying the correct color to the text.
It should be noted that the line above the first (line0) is empty. The color table starts with black, so line0 should be black colored, and line1 should be dark red. Don't know why it's not following it's own rules.
Here is another test I ran:
for (int i = 0; i < 15; i++) {
updateOutputWindow("\x1b[0;34mline" + i.ToString()); //This time I changed the color to be dark blue
}
As you can see: the "looping" happened until it ran into the same color, and it kept going with that color.
I have no idea why this happens or what causes it. My guess is that I have messed up the RTF "script" somehow. Does anyone know of a solution? (I don't want ANY of this color table looping to happen. I want it to output the default color, unless there is a color code present - in that case I want that color to be presented.)
EDIT:
I added this bit of code to the method updateOutputWindow MessageBox.Show(newText);. Below is the screenshot of the result:
As you can see from the screenshot above the RichTextBox has some kind of "default RTF code" already in-place. -This default code gets added ontop of my "custom RTF code". It doesn't seem to interfere with the color table, though. (Unless this is what is casing the issue at hand, in that case it most certainly is interfering, but in a very specific, one-time way.)
EDIT #2: If I continue to run this method over and over again, the RTF-code doesn't get added for each additional run. It gets added once (at the very top) and then no more. I think that is a good thing, and I believe it is caused by how RichTextBoxes natively handles RTF code.
You are converting terminal codes (or as you call them ANSI codes) to RTF format. Text that you give to updateOutputWindow contains \x1b[0;34m, but in updateOutputWindow there is no Replace line for that terminal code. You have something similar, but not exactly that. As a result a terminal code is now part of RTF, so who knows what happens. Must deal with all terminal codes (replace or remove).
Also, the following code seems strange to me:
newText = startRTFString;
newText += rtb_outputWindow.Rtf;
newText += replaceAnsiColorCodes(text);
rtb_outputWindow.Rtf = newText;
It seems that rtb_outputWindow.Rtf will grow with every function call, adding new startRTFString every time. Instead of the code above I propose the following (myStuff is a property, like startRTFString):
myStuff = myStuff + replaceAnsiColorCodes(text);
rtb_outputWindow.Rtf = startRTFString + myStuff;
I am working on a custom C# TreeView and I would like to do some custom draw to highlight the keywords appear in the name of nodes.
I did :
DrawMode = TreeViewDrawMode.OwnerDrawText;
in the constructor of the custom TreeView and override the OnDrawNode:
protected override void OnDrawNode(DrawTreeNodeEventArgs e)
{
if (!e.Node.IsVisible) { return; }
if (e.Node.Bounds.IsEmpty) { return; }
e.DrawDefault = false;
...draw calls...
But it worked strangely after I coded like this, the perceived behaviors include:
OnDrawNode being call on the child nodes which is not expanded and invisible
When the content of the TreeView updates, the user would see the old content and new content at the same time overlapping with each other. The old content would disappear not until about half second or longer.
The rendering speed is much slower than the original draw call.
Another modification I did is the code snippet I found here to suppress the flickering happens when the TreeView is updating:
http://dev.nomad-net.info/articles/double-buffered-tree-and-list-views
But it seems not directly related to the problem since I can still see the text overlapping after removing it.
I wonder if anyone have any idea about this issue?
Any thought would be appreciated.
Thank you.
edit:
The content of the OnDrawNode is like:
string pattern = keyword;
if (!string.IsNullOrWhiteSpace(pattern))
{
Regex regularExpressionnew = Regex(pattern);
Match match = regularExpression.Match(e.Node.Text);
while (match.Success)
{
CaptureCollection captureCollection = match.Groups[0].Captures;
foreach (Capture capture in captureCollection)
{
int highlightStartIndex = capture.Index;
int highlightEndIndex = capture.Index + pattern.Length;
e.Graphics.FillRectangle(nodeHightLightColor, GetTextBoundsBetweenIndex(e.Graphics, e.Node.Text, highlightStartIndex, highlightEndIndex, e.Bounds));
}
match = match.NextMatch();
}
Brush drawBrush = new SolidBrush(Color.Black);
e.Graphics.DrawString(e.Node.Text, Font, drawBrush, e.Bounds);
GetTextBoundsBetweenIndex is essentially calculating the square area covering the characters between highlightStartIndex and highlightEndIndex.
But the lag and overlap would happen event the regular expression is commented out and only the text rendering left.
StringBuilder sb = new StringBuilder();
sb.Append(
string.Format("{0} |{1} ", Name, Value)
);
Display.Text = sb.ToString(); // Display is a WP7 TextBlock control
I want to make "Name" as bold. Is it possible to do that ?
ChrisF offers the RichTextBox as a solution but its less well known that simple font variation is acheivable with the simple TextBlock:-
myTextBlock.Inlines.Add(new Run() { Text = "Hello " });
myTextBlock.Inlines.Add(new Run() { Text = "World", FontWeight= FontWeights.Bold });
A StringBuilder only contains character data, not formatting. You can't, basically. Unless you are actually generating html or rtf etc.
In the same way that notepad.exe doesn't have bold/italics/etc.
I'm not a WP7 expert, but maybe there is a different control you can use here, more aimed at formatted text.
You'll need to put the text into a RichTextBox and have the name as a separate Run in the Paragraph as in this example from the MSDN:
// Create a Run of plain text and some bold text.
Run myRun1 = new Run();
myRun1.Text = "A RichTextBox with ";
Bold myBold = new Bold();
myBold.Inlines.Add("initial content ");
Run myRun2 = new Run();
myRun2.Text = "in it.";
// Create a paragraph and add the Run and Bold to it.
Paragraph myParagraph = new Paragraph();
myParagraph.Inlines.Add(myRun1);
myParagraph.Inlines.Add(myBold);
myParagraph.Inlines.Add(myRun2);
// Add the paragraph to the RichTextBox.
MyRTB.Blocks.Add(myParagraph);
I'm using the RichTextBox.AppendText function to add a string to my RichTextBox. I'd like to set this with a particular colour. How can I do this?
Just try this:
TextRange tr = new TextRange(rtb.Document.ContentEnd, rtb.Document.ContentEnd);
tr.Text = "textToColorize";
tr.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.Red);
If you want, you can also make it an extension method.
public static void AppendText(this RichTextBox box, string text, string color)
{
BrushConverter bc = new BrushConverter();
TextRange tr = new TextRange(box.Document.ContentEnd, box.Document.ContentEnd);
tr.Text = text;
try
{
tr.ApplyPropertyValue(TextElement.ForegroundProperty,
bc.ConvertFromString(color));
}
catch (FormatException) { }
}
This will make it so you can just do
myRichTextBox.AppendText("My text", "CornflowerBlue");
or in hex such as
myRichTextBox.AppendText("My text", "0xffffff");
If the color string you type is invalid, it simply types it in the default color (black).
Hope this helps!
Be Aware of TextRange's Overhead
I spent a lot of time tearing my hair out, because TextRange wasn't fast enough for my use-case. This method avoids the overhead. I ran some barebones tests, and its faster by a factor of ~10 (but don't take my word for it lol, run your own tests)
Paragraph paragraph = new Paragraph();
Run run = new Run("MyText");
paragraph.Inlines.Add(run);
myRichTextBox.Document.Blocks.Add(paragraph);
Credit
Note: I think most use cases should work fine with TextRange. My use-case involved hundreds of individual appends, and that overhead stacks up.
Just a complete example which mixes original question with previous remark from Tony
var paragraph = new Paragraph();
var run = new Run(message)
{
Foreground = someBrush
};
paragraph.Inlines.Add(run);
myRichTextBox.Document.Blocks.Add(paragraph);
Now, it is fast and coloured :)
Note that (unlike the TextRange solution) this solution also solved me a line break issue occurring at the first line of the RichTextBox.
I ended up synthesising Omni and Kishores' answers and creating an extension method as so:
public static void AppendText(this System.Windows.Controls.RichTextBox box, string text, SolidColorBrush brush)
{
TextRange tr = new TextRange(box.Document.ContentEnd, box.Document.ContentEnd);
tr.Text = text;
tr.ApplyPropertyValue(TextElement.ForegroundProperty, brush);
}
Which can be called as so:
MyTextBox.AppendText("Some Text\n", Brushes.Green);
the above single line answer:-
myRichTextBox.AppendText("items", "CornflowerBlue")
is not working.The correct way it should be writen is (i am using VS 2017) :-
Dim text1 As New TextRange(myRichTextBox.Document.ContentStart, myRichTextBox.Document.ContentEnd)
myRichTextBox.AppendText("items")
text1.ApplyPropertyValue(TextElement.ForegroundProperty, Brushes.CornflowerBlue)