How to convert RichTextBox.Selection.Start to int? - c#

How to convert RichTextBox.Selection.Start to int?
In winforms, you can do this :
int i = richTextBox.SelectionStart;
How to do the same in WPF?
All I need is the int value of the start position of the selection.
I tried to find information on this issue, but all was in vain. There are quite a few methods in RichTextBox.Selection.Start in wpf, but none seem to give the desired result. It is very strange that so many difficulties arose with such a simple task.

Get both the start of the selection and the start of the document as a TextPointer. You can then use the GetOffsetToPosition method to calculate the distance between the two in text symbols.
TextPointer selStart = richTextBox.Selection.Start;
TextPointer docStart = richTextBox.Document.ContentStart;
int offset = docStart.GetOffsetToPosition(selStart);

It's not entirely clear from your question what you want to achieve.
The RichTextBox in the WinForms and WPF have totally different approach to manipulate with the flow document content.
But to get text offset from the beginning of document to the current selection you can try the following:
var tr = new TextRange(richTextBox.Selection.Start, richTextBox.Document.ContentStart);
var index = tr.Text.Length;
Take to account, that lines in WinForms RichTextBox are ended with \n and in the WPF with \r\n.

Related

How to verify a big text in RichTextBox without freezing the screen?

I'm currently implementing a Custom Spell Check in WPF using NHunspell, because the native solution of .Net Framework doesn't fit my needs. But I'm having trouble when checking the words in a big text, such as a Lorem Ipsum with 10 pargraphs, because i need to check each word, see if it contains in the dictionary the Hunspell uses, and if not, I need to Underline that Word.
I have this current method, that checks all the text everytime the KeyUp is a Backspace or a Space Key.
var textRange = new TextRange(SpellCheckRichTextBox.Document.ContentStart,
SpellCheckRichTextBox.Document.ContentEnd);
textRange.ApplyPropertyValue(Inline.TextDecorationsProperty, null);
_viewModel.Text = textRange.Text;
var zzz = _viewModel.Text.Split(' ');
var kfeofe = zzz.Where(x => _viewModel.MisspelledWords.Contains(x));
foreach (var item in kfeofe)
{
TextPointer current = textRange.Start.GetInsertionPosition(LogicalDirection.Forward);
while (current != null)
{
string textInRun = current.GetTextInRun(LogicalDirection.Forward);
if (!string.IsNullOrWhiteSpace(textInRun))
{
int index = textInRun.IndexOf(item.ToString());
if (index != -1)
{
TextPointer selectionStart = current.GetPositionAtOffset(index, LogicalDirection.Forward);
TextPointer selectionEnd = selectionStart.GetPositionAtOffset(item.ToString().Length, LogicalDirection.Forward);
TextRange selection = new TextRange(selectionStart, selectionEnd);
selection.ApplyPropertyValue(Inline.TextDecorationsProperty, TextDecorations.Underline);
}
}
current = current.GetNextContextPosition(LogicalDirection.Forward);
}
}
But, I think I need a Async solution, so it doesn't block my main thread and the typing of the user.
- In theory I was thinking about running a parallel thread if the user spends more than 2 seconds without typing and then returning the checked TextRange to my RichTextBox (SpellCheckRichTextBox).
Can somebody suggest any solution so I can make the verification less slow when working with big texts? I'm really stuck at that, any help would be appreciated.
Thanks in advance!
The first improvement would be
zzz.AsParallel().Where(x => _viewModel.MisspelledWords.Contains(x)).ToList();
That obviously assumes that your .MisspelledWords.Contains(x) is something that can be done in parallel. It might be a ConcurrentDictionary already.
The fact that you have a collection of misspelled words, makes me believe you already parsed the text once. So why parse it twice? Why can't you combine those two passes? That would be another possible optimization.
And yes, doing all of this in another thread when the user stops typing would be preferable.

How to get the maxlength of the string a specific RichTextBox control can show on the same line?

I'm using the regular System.Windows.Form.RichTextBox control for a WinForm application running on the .NET Framework 2.0 to show a status log. Since the Form and several other child controls, have AutoSize=True, the application does not always look the same way on different setups. I have no way to know in advance the exact size of the control and anyway I guess there are some implications related to the ratio (font appearence)/(gui dimensions) of each particular configuration.
So now let's depict the most dynamic scenario. I want to know what's the exact maximum length of the string, a given RichTextBox can show on the same line (without exceeding the border nor word wrapping) where such a RichTextBox is using a generic and known, monospaced font and size.
In case there's no any straightforward way to accomplish this result, does anyway know if I may use any kind of trick like injecting an incrementally growing test string till some weird event gets fired?
That's what I got till now. I used the TextRenderer class and the strategy I anticipated on my question above. Of course it makes sense only if the control uses a monospaced font. Despite it works for my problem, I'm still curious to know if anyone knows a better way to reach the same goal. So the question will still be open for a while.
int maxStringLength = GetMaxStringLengthPerLine(myRichTextBox);
int GetMaxStringLengthPerLine(RichTextBox textbox) {
return GetMaxStringLength(textbox.Size.Width, textbox.Font);
}
int GetMaxStringLength(int width, Font font) {
int i = 0;
while(TextRenderer.MeasureText(new string('A',++i), font).Width<=width);
return --i;
}

Using Imageresizer Watermark plugin to write text: Centered, width and line feeds

I'm trying to use the watermark plugin to write text on images for my project. Right now I'm trying to find out how to set a "width" for a writing box so I can get automatic line returns. Is there a way to do this with the watermark plugin?
Also I'm trying to see if I can get a "text-align: center" effect when I'm writing my text (possibliy in relation to that set width), how could I get that setup?
I'm thinking that the alternative to this would be to have code driven line returns and centering, but this would mean that I would have to count the width of my characters and this seems like a world of pain hehe
Here is a code sample that shows what I'm doing (this currently works):
var c = Config.Current;
var wp = c.Plugins.Get<WatermarkPlugin>();
var t = new TextLayer();
t.Text = panty.Message;
t.TextColor = (System.Drawing.Color) color;
t.Font = fonts[myFunObject.Font];
t.FontSize = fontSize[myFunObject.LogoPosition];
t.Left = new DistanceUnit(5, DistanceUnit.Units.Pixels);
t.Top = new DistanceUnit(5, DistanceUnit.Units.Pixels);
wp.NamedWatermarks["myFunObjectMessage"] = new Layer[] { t };
EDIT: I also have to mention that the text I'm writing is user submitted so it's different everytime. If you want a similar case, think about thos funny cat images with funny text captions on them. This project is quite similar to that. (Minus the cats)
Thanks for the help!
Basically, System.Drawing (and therefore the current version of Watermark) are very primitive about line wrapping.
As you mentioned, you can do hacky stuff with character counting and separate MeasureString calls with loops, but the results are only barely acceptable.
You may try to fork the Watermark source code and hack support for your use case. I don't see a way to improve Watermark in a generic way without replacing the underlying graphics engine first (which may happen anyway).
System.Drawing has unsurpassed image resampling quality. Text wrapping, though, it kind of stinks at.

Edit Range NumberFormat in existing document

I can't change column format in an existing Excel document (xlsx). Columns content are numbers actually but shown as text and therefore green triangle appear telling that cells shown as text.
So I open this document in C# app and do the following thing:
sheet_.Range[sheet_.Cells[1, 2], sheet_.Cells[rowNum, 2]].EntireColumn.NumberFormat = "0";
But it doesn't change column to appear content as numbers (they remain aligned by left side)
I know this is an old post, but I've been dealing with the same problem. I receive .xlsx files that already have green triangles denoting "Number as Text" errors. I couldn't find a way to programmatically run the Excel error-checking command "Convert to Number" that you can do by clicking in Excel, and changing the NumberFormat on cells with these errors didn't work for me, but I was able to "refresh" the cell format by using the TextToColumns method.
int lastCol = sheet.UsedRange.Columns.Count;
if(lastCol > 1)
{
for (int i = 1; i <= lastCol; i++)
{
sheet.Columns[i].TextToColumns(Type.Missing, XlTextParsingType.xlDelimited, XlTextQualifier.xlTextQualifierNone);
}
And from there you can change the NumberFormat. I happened to have long integers that were getting put into scientific notation, so I used this to make them regular integers again:
sheet.Cells.NumberFormat = "#";
(PS, if anyone finds a definitive guide on the symbols to use for customized NumberFormats, I'm still trying to find one!)
Try to access to cell value over get_Range method! for example and for what number format you want, lets say that you have in your excel cell this number : 1546,65
sheet_.get_Range("P10", "Q10").NumberFormat = "0"; // returns 1546
sheet_.get_Range("P10", "Q10").NumberFormat = "0,00"; // returns 1546,65
sheet_.get_Range("P10", "Q10").NumberFormat = "#.##0,00"; // returns 1.546,65
And you can play with these number formats!
Hope it helps you
I didn't find ideal solution to this issue and ended with the following:
sheet_.get_Range("A1", "A100").NumberFormat = "0";
for(int i = 1; i <= 100; i++)
{
sheet_.Cells[1, i].Value = sheet_.Cells[1, i].Value;
}
I know this is a old post but I stumbled over this and have a solution for this. Have you tried to not assign any NumberFormat? by default excel decides based on the cell content so you wouldnt get green triangle if you have numbers which are stored as text. If you want read values based on data type then refer this post
http://www.codeproject.com/Articles/335589/Export-Multiple-Datasets-to-Multiple-Excel-sheets
For me using the Style and the NumberFormatLocal solved the problem:
sheet_.get_Range("A1", "A100").Style.NumberFormatLocal = "0";

Highlighting in a RichTextBox is taking too long

I have a large list of offsets which I need to highlight in my RichTextBox. However this process is taking too long. I am using the following code:
foreach (int offset in offsets)
{
richTextBox.Select(offset, searchString.Length);
richTextBox.SelectionBackColor = Color.Yellow;
}
Is there a more efficient way to do so?
UPDATE:
Tried using this method but it doesn't highlight anything:
richTextBox.SelectionBackColor = Color.Yellow;
foreach (int offset in offsets)
{
richTextBox.Select(offset, searchString.Length);
}
I've googled your issue and I found that RichTextBox is getting very slow when having many lines. In my opinion, you have either buy a third part control which you can be satisfied by its performance or you may need threads to devide the whole selection task. I think they can accelerate things up.
Hope it helps !
I've had this same problem before. I ended up disregarding all of the methods they give you and manipulated the underlying RTF data. Also, the reason that your second block of code doesnt work is that RTF applies formatting as it goes, so if you call a function (or Property in this case) to change the selection color, it will only apply it for the currently selected block. Any changes made to the selection after that call become irrelavent.
You can play around with the RGB values, or here is a great source on how to do different things within the RTF control. Pop this function in your code and see how well it works. I use it to provide realtime syntax highlighting for SQL code.
public void HighlightText(int offset, int length)
{
String sText = richTextBox.Text.Trim();
sText = sText.Insert(offset + length - 1, #" \highlight0");
sText = sText.Insert(offset, #" \highlight1");
String s = #"{\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset0 Courier New;}}
{\colortbl ;\red255\green255\blue0;}\viewkind4\uc1\pard";
s += sText;
s += #"\par}";
richTextBox.Rtf = s;
}
Does it make any difference if you set the SelectionBackColor outside of the loop?
Looking into the RichTextBox with Reflector shows, that a WindowMessage is sent to the control every time when the color is set. In the case of large number of offsets this might lead to highlighting the already highlighted words again and again, leading to O(n^2) behavior.

Categories