I am trying to set the position of caret in richtextbox based on index position of a word. Even though I am able to change the caret position, the caret does not move to the correct location.
Here is my sample code:
private void Button_Click(object sender, RoutedEventArgs e)
{
RTB_Main.Document.Blocks.Clear();
for (int i = 0; i < 10; i++)
{
Paragraph para = new Paragraph(new Run(i + ""));
RTB_Main.Document.Blocks.Add(para);
}
TextRange richText = new TextRange(RTB_Main.Document.ContentStart, RTB_Main.Document.ContentEnd);
string searchText = tb_Search.Text; // 1 to 9
int position = Regex.Match(richText.Text, searchText).Index;
RTB_Main.CaretPosition = RTB_Main.Document.ContentStart;
RTB_Main.CaretPosition = RTB_Main.CaretPosition.GetPositionAtOffset(position);
RTB_Main.Focus();
}
What is wrong with this approach?
Also, Please let me know if there is a better way to set the caret position to an index?
The problem in my case was caused by new line characters \r\n. I just replaced these with another characters and it worked for me. Note that I am replacing them with not 2 characters but 4.
private void Button_Click(object sender, RoutedEventArgs e)
{
RTB_Main.Document.Blocks.Clear();
for (int i = 0; i < 10; i++)
{
Paragraph para = new Paragraph(new Run(i + ""));
RTB_Main.Document.Blocks.Add(para);
}
TextRange richText = new TextRange(RTB_Main.Document.ContentStart, RTB_Main.Document.ContentEnd);
string searchText = tb_Search.Text; // 1 to 9
string tmpStr = richText.Text.Replace("\r\n", "....");
int position = Regex.Match(tmpStr, searchText).Index;
RTB_Main.CaretPosition = RTB_Main.Document.ContentStart;
RTB_Main.CaretPosition = RTB_Main.CaretPosition.GetPositionAtOffset(position);
RTB_Main.Focus();
}
As Maciek noted, there are invisible formatting items that affects the count. My code adds a feedback loop because we are able to ask what the true caret position is. It feels hacky but I could not find anything better.
public static void SetCaretPositionOfRichTextBoxToCharIndex(
System.Windows.Controls.RichTextBox box, int charIndex)
{
// RichTextBox contains many formattings, and they, although invisible, count
// when setting CaretPosition. Calling GetPositionAtOffset with charIndex from
// DocumentStart can be less than the necessary CaretPosition. This code
// therefore has a feedback loop to see how much more offset is necessary.
box.CaretPosition = box.CaretPosition.DocumentStart;
int attemptedCharIndex = 0;
int fixerInc = 0;
while (attemptedCharIndex < charIndex)
{
box.CaretPosition = box.CaretPosition.GetPositionAtOffset(charIndex - attemptedCharIndex + fixerInc);
int temp = new TextRange(box.Document.ContentStart, box.CaretPosition).Text.Length;
if (attemptedCharIndex == temp)
{
fixerInc++;
}
else
{
fixerInc = 0
}
attemptedCharIndex = temp;
}
}
I want to Justify text Like Below Link Image in label there have word justifications not a character currently my code is doing this. And i am using another fonts as well and it is a multi-language application so the text justification is work some times in English language only but i need another fonts too.
MyCode is below, Please check this.
public void Justify(System.Windows.Forms.Label label)
{
string text = label.Text;
string[] lines = text.Split(new[] { "\r\n" }, StringSplitOptions.None).Select(l => l.Trim()).ToArray();
List<string> result = new List<string>();
foreach (string line in lines)
{
result.Add(StretchToWidth(line, label));
}
label.Text = string.Join("\r\n", result);
}
It is call next function
private string StretchToWidth(string text, Label label)
{
if (text.Length < 2)
return text;
// A hair space is the smallest possible non-visible character we can insert
const char hairspace = '\u200A';
// If we measure just the width of the space we might get too much because of added paddings so we have to do it a bit differently
double basewidth = TextRenderer.MeasureText(text, label.Font).Width;
double doublewidth = TextRenderer.MeasureText(text + text, label.Font).Width;
double doublewidthplusspace = TextRenderer.MeasureText(text + hairspace + text, label.Font).Width;
double spacewidth = doublewidthplusspace - doublewidth;
//The space we have to fill up with spaces is whatever is left
double leftoverspace = label.Width - basewidth;
//Calculate the amount of spaces we need to insert
int approximateInserts = Math.Max(0, (int)Math.Floor(leftoverspace / spacewidth));
//Insert spaces
return InsertFillerChar(hairspace, text, approximateInserts);
}
and last is this.
private static string InsertFillerChar(char filler, string text, int inserts)
{
string result = "";
int inserted = 0;
for (int i = 0; i < text.Length; i++)
{
//Add one character of the original text
result += text[i];
//Only add spaces between characters, not at the end
if (i >= text.Length - 1) continue;
//Determine how many characters should have been inserted so far
int shouldbeinserted = (int)(inserts * (i + 1) / (text.Length - 1.0));
int insertnow = shouldbeinserted - inserted;
for (int j = 0; j < insertnow; j++)
result += filler;
inserted += insertnow;
}
return result;
}
I am looking a way to create a HyperLink in a RichTextBox pointing to a line of the text of the same RichTextBox.
I just found how to do that with Internet Links but I don't find a way to do it with the same text inside of the control (It's like Hyperlinks in MS Word pointing to a header or bookmark).
Thanks in Advance. - CCB
No, this will not work unless you code the necessary stuff yourself.
Two suggestions:
A simple workaround with links always starting with www.
The nicer solution with arbitrary link text
Let's have a look at both options..:
Using the built-in functionality of recognizing an URL seems the right way to start, but the link will always have to look like a URL, not like a hyperlink to an anchor.. If you can live with a solution that has, say, links like this: www.goto.234 and anchors like this: #234# this is really rather simple..
A working example can be as simple as this:
private void richTextBox1_LinkClicked(object sender, LinkClickedEventArgs e)
{
var s = e.LinkText.Split('.');
string anchor = s[2];
int a = richTextBox1.Text.IndexOf("#" + anchor + "#" );
if (a >= 0) richTextBox1.SelectionStart = a; else return; // do add more checks!
richTextBox1.SelectionLength = 0;
Text = anchor + " # " + a;
//richTextBox1.ScrollToCaret(); <<--- this crashes on my machine!
// so I take the jump out of the click event and it works:
Timer ttt = new Timer() { Interval = 100 };
ttt.Tick += (ss, ee) => { richTextBox1.ScrollToCaret(); ttt.Stop(); };
}
Option two: If you'd rather have more choice of how the links should read you can do this:
Start by formatting each to
Start with a special character, say a tilde '~'
format it to look blue and underlined if you want to
Either make it one word or replace space by underlines and format those to have the forecolor equal to the backcolor
Now this can do the job:
public string delimiters = " ()[]{}!&?=/\\,;.\r\n";
private void richTextBox2_Click(object sender, EventArgs e)
{
int sstart = -1;
string s = getWordAt(richTextBox2.Text,
richTextBox2.SelectionStart, delimiters, out sstart);
if (s.Length < 3) return;
string char1 = s.Substring(0, 1);
if (char1 == "~")
{
int p = richTextBox2.Text.IndexOf("#" + s.Substring(1));
if (p >= 0) { richTextBox2.SelectionStart = p; richTextBox2.ScrollToCaret(); }
}
}
public static string getWordAt(string text, int cursorPos,
string delimiters, out int selStart)
{
int startPos = 0;
selStart = startPos;
if ((cursorPos < 0) | (cursorPos > text.Length) | (text.Length == 0)) return "";
if ((text.Length > cursorPos) & (delimiters.Contains(text[cursorPos]))) return "";
int endPos = text.Length - 1;
if (cursorPos == text.Length) endPos = text.Length - 1;
else { for (int i = cursorPos; i < text.Length; i++)
{ if (delimiters.Contains(text[i])) { endPos = i - 1; break; } } }
if (cursorPos == 0) startPos = 0;
else { for (int i = cursorPos; i > 0; i--)
{ if (delimiters.Contains(text[i])) { startPos = i + 1; break; } } }
selStart = startPos;
return text.Substring(startPos, endPos - startPos + 1);
}
Here are the two versions side by side, once at the top then after clicking on a link:
Both versions work fine, although both could do with some more checks.
Note that I was too lazy to format the pseudo-links in the second example, so they show their tildes and hashes..
Not hard to write a helper function that can insert the formatting; the search will still work as it searches in the Text, not the Rtf property..
I'm writing a simple c# console app that uploads files to sftp server. However, the amount of files are large. I would like to display either percentage of files uploaded or just the number of files upload already from the total number of files to be upload.
First, I get all the files and the total number of files.
string[] filePath = Directory.GetFiles(path, "*");
totalCount = filePath.Length;
Then I loop through the file and upload them one by one in foreach loop.
foreach(string file in filePath)
{
string FileName = Path.GetFileName(file);
//copy the files
oSftp.Put(LocalDirectory + "/" + FileName, _ftpDirectory + "/" + FileName);
//Console.WriteLine("Uploading file..." + FileName);
drawTextProgressBar(0, totalCount);
}
In the foreach loop I have a progress bar which I have issues with. It doesn't display properly.
private static void drawTextProgressBar(int progress, int total)
{
//draw empty progress bar
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = 32;
Console.Write("]"); //end
Console.CursorLeft = 1;
float onechunk = 30.0f / total;
//draw filled part
int position = 1;
for (int i = 0; i < onechunk * progress; i++)
{
Console.BackgroundColor = ConsoleColor.Gray;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw unfilled part
for (int i = position; i <= 31 ; i++)
{
Console.BackgroundColor = ConsoleColor.Green;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw totals
Console.CursorLeft = 35;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write(progress.ToString() + " of " + total.ToString() + " "); //blanks at the end remove any excess
}
The output is just [ ] 0 out of 1943
What am I doing wrong here?
EDIT:
I'm trying to display the progress bar while I'm loading and exporting XML files. However, it's going through a loop. After it finishes the first round it goes to the second and so on.
string[] xmlFilePath = Directory.GetFiles(xmlFullpath, "*.xml");
Console.WriteLine("Loading XML files...");
foreach (string file in xmlFilePath)
{
for (int i = 0; i < xmlFilePath.Length; i++)
{
//ExportXml(file, styleSheet);
drawTextProgressBar(i, xmlCount);
count++;
}
}
It never leaves the for loop...Any suggestions?
I was also looking for a console progress bar. I didn't find one that did what I needed, so I decided to roll my own. Click here for the source code (MIT License).
Features:
Works with redirected output
If you redirect the output of a console application (e.g., Program.exe > myfile.txt), most implementations will crash with an exception. That's because Console.CursorLeft and Console.SetCursorPosition() don't support redirected output.
Implements IProgress<double>
This allows you to use the progress bar with async operations that report a progress in the range of [0..1].
Thread-safe
Fast
The Console class is notorious for its abysmal performance. Too many calls to it, and your application slows down. This class performs only 8 calls per second, no matter how often you report a progress update.
Use it like this:
Console.Write("Performing some task... ");
using (var progress = new ProgressBar()) {
for (int i = 0; i <= 100; i++) {
progress.Report((double) i / 100);
Thread.Sleep(20);
}
}
Console.WriteLine("Done.");
I know this is an old thread, and apologies for the self promotion, however I've recently written an open source console library available on nuget Goblinfactory.Konsole with threadsafe multiple progress bar support, that might help anyone new to this page needing one that doesnt block the main thread.
It's somewhat different to the answers above as it allows you to kick off the downloads and tasks in parallel and continue with other tasks;
cheers, hope this is helpful
A
var t1 = Task.Run(()=> {
var p = new ProgressBar("downloading music",10);
... do stuff
});
var t2 = Task.Run(()=> {
var p = new ProgressBar("downloading video",10);
... do stuff
});
var t3 = Task.Run(()=> {
var p = new ProgressBar("starting server",10);
... do stuff .. calling p.Refresh(n);
});
Task.WaitAll(new [] { t1,t2,t3 }, 20000);
Console.WriteLine("all done.");
gives you this type of output
The nuget package also includes utilities for writing to a windowed section of the console with full clipping and wrapping support, plus PrintAt and various other helpful classes.
I wrote the nuget package because I constantly ended up writing lots of common console routines whenever I wrote build and ops console scripts and utilities.
If I was downloading several files, I used to slowly Console.Write to the screen on each thread, and used to try various tricks to make reading the interleaved output on the screen easier to read, e.g. different colors or numbers. I eventually wrote the windowing library so that output from different threads could simply be printed to different windows, and it cut down a ton of boilerplate code in my utility scripts.
For example, this code,
var con = new Window(200,50);
con.WriteLine("starting client server demo");
var client = new Window(1, 4, 20, 20, ConsoleColor.Gray, ConsoleColor.DarkBlue, con);
var server = new Window(25, 4, 20, 20, con);
client.WriteLine("CLIENT");
client.WriteLine("------");
server.WriteLine("SERVER");
server.WriteLine("------");
client.WriteLine("<-- PUT some long text to show wrapping");
server.WriteLine(ConsoleColor.DarkYellow, "--> PUT some long text to show wrapping");
server.WriteLine(ConsoleColor.Red, "<-- 404|Not Found|some long text to show wrapping|");
client.WriteLine(ConsoleColor.Red, "--> 404|Not Found|some long text to show wrapping|");
con.WriteLine("starting names demo");
// let's open a window with a box around it by using Window.Open
var names = Window.Open(50, 4, 40, 10, "names");
TestData.MakeNames(40).OrderByDescending(n => n).ToList()
.ForEach(n => names.WriteLine(n));
con.WriteLine("starting numbers demo");
var numbers = Window.Open(50, 15, 40, 10, "numbers",
LineThickNess.Double,ConsoleColor.White,ConsoleColor.Blue);
Enumerable.Range(1,200).ToList()
.ForEach(i => numbers.WriteLine(i.ToString())); // shows scrolling
produces this
You can also create progress bars inside a window just as easily as writing to the windows. (mix and match).
You might want to try https://www.nuget.org/packages/ShellProgressBar/
I just stumbled upon this progress bar implementation - its cross platform, really easy to use, quite configurable and does what it should right out of the box.
Just sharing because i liked it a lot.
This line is your problem:
drawTextProgressBar(0, totalCount);
You're saying the progress is zero in every iteration, this should be incremented. Maybe use a for loop instead.
for (int i = 0; i < filePath.length; i++)
{
string FileName = Path.GetFileName(filePath[i]);
//copy the files
oSftp.Put(LocalDirectory + "/" + FileName, _ftpDirectory + "/" + FileName);
//Console.WriteLine("Uploading file..." + FileName);
drawTextProgressBar(i, totalCount);
}
I quite liked the original poster's progress bar, but found that it did not display progress correctly with certain progress/total item combinations. The following, for example, does not draw correctly, leaving an extra grey block at the end of the progress bar:
drawTextProgressBar(4114, 4114)
I re-did some of the drawing code to remove the unnecessary looping which fixed the above issue and also sped things up quite a bit:
public static void drawTextProgressBar(string stepDescription, int progress, int total)
{
int totalChunks = 30;
//draw empty progress bar
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = totalChunks + 1;
Console.Write("]"); //end
Console.CursorLeft = 1;
double pctComplete = Convert.ToDouble(progress) / total;
int numChunksComplete = Convert.ToInt16(totalChunks * pctComplete);
//draw completed chunks
Console.BackgroundColor = ConsoleColor.Green;
Console.Write("".PadRight(numChunksComplete));
//draw incomplete chunks
Console.BackgroundColor = ConsoleColor.Gray;
Console.Write("".PadRight(totalChunks - numChunksComplete));
//draw totals
Console.CursorLeft = totalChunks + 5;
Console.BackgroundColor = ConsoleColor.Black;
string output = progress.ToString() + " of " + total.ToString();
Console.Write(output.PadRight(15) + stepDescription); //pad the output so when changing from 3 to 4 digits we avoid text shifting
}
I have copy pasted your ProgressBar method. Because your error was in the loop as the accepted answer mentioned. But the ProgressBar method has some syntax errors too. Here is the working version. Slightly modified.
private static void ProgressBar(int progress, int tot)
{
//draw empty progress bar
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = 32;
Console.Write("]"); //end
Console.CursorLeft = 1;
float onechunk = 30.0f / tot;
//draw filled part
int position = 1;
for (int i = 0; i < onechunk * progress; i++)
{
Console.BackgroundColor = ConsoleColor.Green;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw unfilled part
for (int i = position; i <= 31; i++)
{
Console.BackgroundColor = ConsoleColor.Gray;
Console.CursorLeft = position++;
Console.Write(" ");
}
//draw totals
Console.CursorLeft = 35;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write(progress.ToString() + " of " + tot.ToString() + " "); //blanks at the end remove any excess
}
Please note that #Daniel-wolf has a better approach: https://stackoverflow.com/a/31193455/169714
I've created this handy class that works with System.Reactive. I hope you find it lovely enough.
public class ConsoleDisplayUpdater : IDisposable
{
private readonly IDisposable progressUpdater;
public ConsoleDisplayUpdater(IObservable<double> progress)
{
progressUpdater = progress.Subscribe(DisplayProgress);
}
public int Width { get; set; } = 50;
private void DisplayProgress(double progress)
{
if (double.IsNaN(progress))
{
return;
}
var progressBarLenght = progress * Width;
System.Console.CursorLeft = 0;
System.Console.Write("[");
var bar = new string(Enumerable.Range(1, (int) progressBarLenght).Select(_ => '=').ToArray());
System.Console.Write(bar);
var label = $#"{progress:P0}";
System.Console.CursorLeft = (Width -label.Length) / 2;
System.Console.Write(label);
System.Console.CursorLeft = Width;
System.Console.Write("]");
}
public void Dispose()
{
progressUpdater?.Dispose();
}
}
Console Progress Bar in C# (Complete Code - Just copy and paste on your IDE)
class ConsoleUtility
{
const char _block = '■';
const string _back = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
const string _twirl = "-\\|/";
public static void WriteProgressBar(int percent, bool update = false)
{
if (update)
Console.Write(_back);
Console.Write("[");
var p = (int)((percent / 10f) + .5f);
for (var i = 0; i < 10; ++i)
{
if (i >= p)
Console.Write(' ');
else
Console.Write(_block);
}
Console.Write("] {0,3:##0}%", percent);
}
public static void WriteProgress(int progress, bool update = false)
{
if (update)
Console.Write("\b");
Console.Write(_twirl[progress % _twirl.Length]);
}
}
//This is how to call in your main method
static void Main(string[] args)
{
ConsoleUtility.WriteProgressBar(0);
for (var i = 0; i <= 100; ++i)
{
ConsoleUtility.WriteProgressBar(i, true);
Thread.Sleep(50);
}
Console.WriteLine();
ConsoleUtility.WriteProgress(0);
for (var i = 0; i <= 100; ++i)
{
ConsoleUtility.WriteProgress(i, true);
Thread.Sleep(50);
}
}
I just stumbled upon this thread looking for something else, and I thought I'd drop off my code that I put together that downloads a List of files using the DownloadProgressChanged. I find this super helpful so I not only see the progress, but the actual size as the file is coming through. Hope it helps someone!
public static bool DownloadFile(List<string> files, string host, string username, string password, string savePath)
{
try
{
//setup FTP client
foreach (string f in files)
{
FILENAME = f.Split('\\').Last();
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
wc.DownloadFileAsync(new Uri(host + f), savePath + f);
while (wc.IsBusy)
System.Threading.Thread.Sleep(1000);
Console.Write(" COMPLETED!");
Console.WriteLine();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
return false;
}
return true;
}
private static void ProgressChanged(object obj, System.Net.DownloadProgressChangedEventArgs e)
{
Console.Write("\r --> Downloading " + FILENAME +": " + string.Format("{0:n0}", e.BytesReceived / 1000) + " kb");
}
private static void Completed(object obj, AsyncCompletedEventArgs e)
{
}
Here's an example of the output:
Hope it helps someone!
I am still a little new to C# but I believe the below might help.
string[] xmlFilePath = Directory.GetFiles(xmlFullpath, "*.xml");
Console.WriteLine("Loading XML files...");
int count = 0;
foreach (string file in xmlFilePath)
{
//ExportXml(file, styleSheet);
drawTextProgressBar(count, xmlCount);
count++;
}
Based on all posts above, I did an improved version.
No jumping cursors. It's invisible now.
Improved performance (costs 1/5~1/10 time of the origin one).
Interface based. Easy to move to something else.
public class ConsoleProgressBar : IProgressBar
{
private const ConsoleColor ForeColor = ConsoleColor.Green;
private const ConsoleColor BkColor = ConsoleColor.Gray;
private const int DefaultWidthOfBar = 32;
private const int TextMarginLeft = 3;
private readonly int _total;
private readonly int _widthOfBar;
public ConsoleProgressBar(int total, int widthOfBar = DefaultWidthOfBar)
{
_total = total;
_widthOfBar = widthOfBar;
}
private bool _intited;
public void Init()
{
_lastPosition = 0;
//Draw empty progress bar
Console.CursorVisible = false;
Console.CursorLeft = 0;
Console.Write("["); //start
Console.CursorLeft = _widthOfBar;
Console.Write("]"); //end
Console.CursorLeft = 1;
//Draw background bar
for (var position = 1; position < _widthOfBar; position++) //Skip the first position which is "[".
{
Console.BackgroundColor = BkColor;
Console.CursorLeft = position;
Console.Write(" ");
}
}
public void ShowProgress(int currentCount)
{
if (!_intited)
{
Init();
_intited = true;
}
DrawTextProgressBar(currentCount);
}
private int _lastPosition;
public void DrawTextProgressBar(int currentCount)
{
//Draw current chunk.
var position = currentCount * _widthOfBar / _total;
if (position != _lastPosition)
{
_lastPosition = position;
Console.BackgroundColor = ForeColor;
Console.CursorLeft = position >= _widthOfBar ? _widthOfBar - 1 : position;
Console.Write(" ");
}
//Draw totals
Console.CursorLeft = _widthOfBar + TextMarginLeft;
Console.BackgroundColor = ConsoleColor.Black;
Console.Write(currentCount + " of " + _total + " "); //blanks at the end remove any excess
}
}
public interface IProgressBar
{
public void ShowProgress(int currentCount);
}
And some test code:
var total = 100;
IProgressBar progressBar = new ConsoleProgressBar(total);
for (var i = 0; i <= total; i++)
{
progressBar.ShowProgress(i);
Thread.Sleep(50);
}
Thread.Sleep(500);
Console.Clear();
total = 9999;
progressBar = new ConsoleProgressBar(total);
for (var i = 0; i <= total; i++)
{
progressBar.ShowProgress(i);
}
I'm making an application and i need get the same interval of space from a line in many items (strings). My code is something like that
p = "";
int contaletras = 0;
int maxPalavra = 14; int xpto;
contaletras = item.Length;
xpto = maxPalavra - contaletras;
for (int i = 0; i < xpto; i++)
{
p = p + " ";
}
StringBuilder m = new StringBuilder();
m.Append(item);
m.Insert(item.Length, p);
RTB_Exames.Text = RTB_Exames.Text + m.ToString() + " ";
So, my item.length is all the time 14 characteres. But
i dont know why i have this result when in textvisualizer in c# i have this. I will show some image in this link:
https://drive.google.com/file/d/0B5tH_Qo3-GvhSEJrOXVsTzhIQVk/edit?usp=sharing
https://drive.google.com/file/d/0B5tH_Qo3-GvhaTN5WUM1M2M3QVU/edit?usp=sharing
You can either set the font in the visual editor for the TextBox control or set it via:
RTB_Exames.FontFamily = new FontFamily("Consolas");