Are there any automatic methods for trimming a path string in .NET?
For example:
C:\Documents and Settings\nick\My Documents\Tests\demo data\demo data.emx
becomes
C:\Documents...\demo data.emx
It would be particularly cool if this were built into the Label class, and I seem to recall it is--can't find it though!
Use TextRenderer.DrawText with TextFormatFlags.PathEllipsis flag
void label_Paint(object sender, PaintEventArgs e)
{
Label label = (Label)sender;
TextRenderer.DrawText(e.Graphics, label.Text, label.Font, label.ClientRectangle, label.ForeColor, TextFormatFlags.PathEllipsis);
}
Your code is 95% there. The only
problem is that the trimmed text is
drawn on top of the text which is
already on the label.
Yes thanks, I was aware of that. My intention was only to demonstrate use of DrawText method. I didn't know whether you want to manually create event for each label or just override OnPaint() method in inherited label. Thanks for sharing your final solution though.
# lubos hasko Your code is 95% there. The only problem is that the trimmed text is drawn on top of the text which is already on the label. This is easily solved:
Label label = (Label)sender;
using (SolidBrush b = new SolidBrush(label.BackColor))
e.Graphics.FillRectangle(b, label.ClientRectangle);
TextRenderer.DrawText(
e.Graphics,
label.Text,
label.Font,
label.ClientRectangle,
label.ForeColor,
TextFormatFlags.PathEllipsis);
Not hard to write yourself though:
public static string TrimPath(string path)
{
int someArbitaryNumber = 10;
string directory = Path.GetDirectoryName(path);
string fileName = Path.GetFileName(path);
if (directory.Length > someArbitaryNumber)
{
return String.Format(#"{0}...\{1}",
directory.Substring(0, someArbitaryNumber), fileName);
}
else
{
return path;
}
}
I guess you could even add it as an extension method.
What you are thinking on the label is that it will put ... if it is longer than the width (not set to auto size), but that would be
c:\Documents and Settings\nick\My Doc...
If there is support, it would probably be on the Path class in System.IO
You could use the System.IO.Path.GetFileName method and append that string to a shortened System.IO.Path.GetDirectoryName string.
Next code works for folders. I'm using it to display a download path!
public static string TrimPath(string path) {
string shortenedPath = "";
string[] pathParts = path.Split('\\');
for (int i = 0; i < pathParts.Length-1; i++) {
string part = pathParts[i];
if (pathParts.Length-2 != i) {
if (part.Length > 5) { //If folder name length is bigger than 5 chars
shortenedPath += "..\\";
}
else {
shortenedPath += part+"\\";
}
}
else {
shortenedPath += part+"\\";
}
}
return shortenedPath;
}
Example:
Input:
C:\Users\Sandra\Desktop\Proyectos de programaciĆ³n\Prototype\ServerClient\test
output:
C:\Users\..\..\..\..\..\test\
Related
I made a program that text is moving right to left(marquee).
But, I can't make use multi-line text to label.
Who can help me?
single line text is good work. But if multi-line, just get last sentence.
private void timer1_Tick(object sender, EventArgs e)
{
string screentext = clsBas.SCREEN_TEXT;//include in Multi-line text;
string[] result = screentext.Split(new string[] { "\r\n" }, StringSplitOptions.None);
string space = " ";
foreach (string news in result)
{
lblScreenText.Text = news + space;
if (lblScreenText.Left < 0 && (Math.Abs(lblScreenText.Left) >
lblScreenText.Width))
lblScreenText.Left = lblScreenText.Width;
lblScreenText.Left -= 2;
}
}
Try using Environment.NewLine and append to text.
Better way to do it, is to use textbox multi-line and make it read-only.
I have a textBox named "textBoxCliente" and I want it to appear suggestions when I writte in there from the .txt file.
The suggestions that I want to appear from the .txt file are in the position 1 "parts[1]", each position are separated with the caracter "|".
My .txt file is this:
1|Rui|Lisboa|rui#hotmail.com|912345324|14/01/2000|89564352|Empresa
2|Henrique|Evora|henrique#hotmail.com|914445324|17/05/2001|55464352|Particular
3|Andre|Agueda|andre#hotmail.com|932415374|12/11/1996|23456743|Particular
4|Pedro|Aveiro|pedro#hotmail.com|965342163|30/03/2002|98645372|Empresa
My code is:
public partial class Vender : UserControl
{
public Vender()
{
InitializeComponent();
}
string dir = (Environment.CurrentDirectory + "/Bd/clientes.txt");
string[] sug = new string[File.ReadAllLines(Environment.CurrentDirectory +
"/Bd/clientes.txt").Count()];
private void textBoxCliente_TextChanged(object sender, EventArgs e)
{
carrSug();
for (int i = 0; i < sug.Length; i++)
{
textBoxCliente.AutoCompleteCustomSource.Add(sug[i]);
}
textBoxCliente.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
}
private void carrSug()
{
string[] lines = File.ReadLines(dir).ToArray();
int nLine = File.ReadAllLines(dir).Count();
for (int j = 0; j <= nLine - 1; j++)
{
string[] parts = lines[j].Split(new char[] { '|' });
sug[j] = parts[1];
}
}
}
What I did was using the "string[] sug" to save the values of the position 1 of each line and then use it to show the suggestions.
As a programmer, get better at reading carefully. Here is the documentation for AutoCompleteCustomSource:
Gets or sets a custom System.Collections.Specialized.StringCollection to use when the System.Windows.Forms.TextBox.AutoCompleteSource property is set to CustomSource.
Emphasis Mine
See the bolded part in the above, make sure you do that:
textBoxCliente.AutoCompleteSource = AutoCompleteSource.CustomSource;
Also, you do not need to do that every time the user types. The event handler textBoxCliente_TextChanged will be called every time the text changes. Instead, put the code in the constructor or in the form's load event.
Some Suggestions
Give your methods meaningful names. For example, carrSug() is not very meaningful. Plus it does not follow the C# coding conventions--it looks like Java. Also, keep the method cohesive. You are doing some parts of the suggestion in the carrSug() and then some of it you are doing in textBoxCliente_TextChanged. Here is a more meaningful method:
private AutoCompleteStringCollection clientSuggestions;
private void LoadClientSuggestions()
{
this.clientSuggestions = new AutoCompleteStringCollection();
string[] suggestionsFromFile = File.ReadLines("YourPath.txt").Select(x => x.Split('|').Skip(1).First()).ToArray();
this.clientSuggestions.AddRange(suggestionsFromFile);
}
The above method uses Ling so make sure to import: using System.Linq;
Here is how to use it (Put this code in your form's constructor or Load method):
this.LoadSuggestions();
this.textBoxCliente.AutoCompleteSource = AutoCompleteSource.CustomSource;
this.textBoxCliente.AutoCompleteCustomSource = this.clientSuggestions;
this.textBoxCliente.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
Why did I put the word Load in the method name? Because, it returns nothing so Load adds meaning.
Also, stop writing the same string multiple times:
"/Bd/clientes.txt"
Make that a constant so if you need to change it, you change it in one place.
This has been an issue with many of my applications and I don't know why Windows doesn't have an elegant solution for this.
I am working with Winforms in .Net 4.5 in VS2013
For example, I would like to change the color of one line of text in a multiline RichTextBox.
For this I am required to set the selection using something like
rtb.Select(rtb.GetFirstCharIndexFromLine(r), str.Length);
Then, I would set the color using
rtb.SelectionColor = Color.Red;
And presumably, cancel the selection with
rtb.DeselectAll();
Now the problem is the cursor/caret has moved back to the beginning of the line,
I try to fix it by saving the previous Caret Position,
rtb.CaretPosition
However, CaretPosition is not a method of RichTextBox, and everywhere online this is the primary method everyone uses.
I tried adding PresentationFramework to my References and to my code I added
using System.Windows.Framework;
As suggested here: http://msdn.microsoft.com/en-us/library/system.windows.controls.richtextbox.caretposition(v=vs.110).aspx
but I still do not see the CaretPosition property, only the ScrollToCaret() method.
My 2 questions are:
How do I get the CaretPosition property in my RichTextBox?
How can I change the text color without using selections and affecting the caret position, having to write complex logic to restore it for the user.
My application checks serial numbers, one per line, and highlights them red if they do not match the format, as shown below.
private void rtb_TextChanged(object sender, EventArgs e)
{
string pattern = #"[A-Z]{2}[A-Z, 0-9]{2}\d{4}";
Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
TextReader read = new System.IO.StringReader(rtb.Text);
SerialNumbers.Clear();
int selectStart = 0;
for (int r = 0; r < rtb.Lines.Length; r++)
{
string str = read.ReadLine();
if (str != null)
{
selectStart += str.Length;
MatchCollection matches = rgx.Matches(str);
if (matches.Count == 1)
{
SerialNumbers.Add(str);
}
else
{
rtb.Select(rtb.GetFirstCharIndexFromLine(r), str.Length);
rtb.SelectionColor = Color.Red;
rtb.DeselectAll();
}
}
}
}
You should be using SelectionCaret (as #Mangist mentioned in a comment) because you're using WinForms and not WPF. The MSDN article you referenced only applies to WPF, which is very different from WinForms.
As an example, I use the following to easily log to a rich text box from anywhere in a WinForms app:
public static void Log(string text, ref RichTextBox rtbLogBox) {
//
if (text == null) return;
var timestamp = DateTime.Now.ToLongTimeString();
var logtext = string.Format("{0} - {1}\r\n\r\n", timestamp, text);
if (rtbLogBox.InvokeRequired) {
var logBox = rtbLogBox;
logBox.Invoke(new MethodInvoker(delegate {
logBox.AppendText(logtext);
logBox.Update();
logBox.SelectionStart = logBox.Text.Length;
logBox.ScrollToCaret();
}));
} else {
rtbLogBox.AppendText(logtext);
rtbLogBox.Update();
rtbLogBox.SelectionStart = rtbLogBox.Text.Length;
rtbLogBox.ScrollToCaret();
}
}
Notice how the ScrollToCaret() is called after setting SelectionStart to the length of text in the rich text box. This solves the 'issue' of AppendText not scrolling to the bottom after adding text.
In your case you will simply want to save the SelectionStart value before you format your text with the highlighting, and then restore it once you've finished.
Fixed it by saving SelectionStart position
int selectionStart = SNbox.SelectionStart;
SNbox.Select(SNbox.GetFirstCharIndexFromLine(r), str.Length);
SNbox.SelectionColor = Color.Red;
SNbox.DeselectAll();
SNbox.SelectionStart = selectionStart;
SNbox.SelectionLength = 0;
Is it possible to paste text into a Rich Text Box, while keeping the font being used in the Rich Text Box for the pasted content ?
In other words, I'd like to copy something from Word that is formated (i.e: a text that uses a font X and is underlined and in blue), and then paste it in my RichTextBox.
I would like the pasted content to have the same font as that of my RichTextBox but keep its original coloring and underlining.
Is such a thing possible ?
I use winforms.
Thanks
This is not possible out of the box. But you can do something like this:
public void SpecialPaste()
{
var helperRichTextBox = new RichTextBox();
helperRichTextBox.Paste();
for(int i=0;i<helperRichTextBox.TextLength;++i)
{
helperRichTextBox.SelectionStart = i;
helperRichTextBox.SelectionLength = 1;
helperRichTextBox.SelectionFont = new Font(richTextBox1.SelectionFont.FontFamily, richTextBox1.SelectionFont.Size,helperRichTextBox.SelectionFont.Style);
}
richTextBox1.SelectedRtf = helperRichTextBox.Rtf;
}
This changes the font of the pasted RTF to that of the character preceding the caret position at the time of the paste.
I assume that will get problematic pretty fast, if the text you paste is large(er). Additionally, this can be optimized in a way, that it sets the font only once for all characters in a row with the same base font as Hans suggests.
Update:
Here is the optimized version, that sets the font for a connected set of characters with the same original font:
public void SpecialPaste()
{
var helperRichTextBox = new RichTextBox();
helperRichTextBox.Paste();
helperRichTextBox.SelectionStart = 0;
helperRichTextBox.SelectionLength = 1;
Font lastFont = helperRichTextBox.SelectionFont;
int lastFontChange = 0;
for (int i = 0; i < helperRichTextBox.TextLength; ++i)
{
helperRichTextBox.SelectionStart = i;
helperRichTextBox.SelectionLength = 1;
if (!helperRichTextBox.SelectionFont.Equals(lastFont))
{
lastFont = helperRichTextBox.SelectionFont;
helperRichTextBox.SelectionStart = lastFontChange;
helperRichTextBox.SelectionLength = i - lastFontChange;
helperRichTextBox.SelectionFont = new Font(richTextBox1.SelectionFont.FontFamily, richTextBox1.SelectionFont.Size, helperRichTextBox.SelectionFont.Style);
lastFontChange = i;
}
}
helperRichTextBox.SelectionStart = helperRichTextBox.TextLength-1;
helperRichTextBox.SelectionLength = 1;
helperRichTextBox.SelectionFont = new Font(richTextBox1.Font.FontFamily, richTextBox1.Font.Size, helperRichTextBox.SelectionFont.Style);
richTextBox1.SelectedRtf = helperRichTextBox.Rtf;
}
It's pretty ugly code and I am sure it can be improved and cleaned. But it does what it should.
Clearly this won't work the way you want it if the RTF on the clipboard contains a fragment with a /font directive. Which is very likely. Filtering the RTF fragment is only practical by pasting into a helper RichTextBox. Use the SelectionFont property then copy it back to the clipboard and Paste(). Or do it directly:
int oldpos = richTextBox1.SelectionStart;
richTextBox1.SelectionLength = 0;
richTextBox1.Paste();
int newpos = richTextBox1.SelectionStart;
richTextBox1.SelectionStart = oldpos;
richTextBox1.SelectionLength = newpos - oldpos;
richTextBox1.SelectionFont = richTextBox1.Font;
richTextBox1.SelectionStart = newpos;
I know this is a bit late, but I ran into the same problem and here is my solution (hopefully this will help others):
First, handle the KeyDown event for the RichTextBox:
this.richTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.RichTextBoxKeyDown);
Next, check for paste key event and re-set the clipboard text (this is where the magic happens):
private void RichTextBoxKeyDown(object sender, KeyEventArgs e)
{
if (e.Control && e.KeyCode == Keys.V)
{
try
{
Clipboard.SetText(Clipboard.GetText());
}
catch (Exception)
{
}
}
}
Explanation:
I'll start by saying that this was only tested with .NET 4.0. Assuming that none of the functions being used were altered, this will also work with older versions of .NET.
Calling Clipboard.GetText() returns the content in a plain text format (excluding RTF tags). Then we alter the text that's going to be pasted by calling Clipboard.SetText() with the plain text we fetched from Clipboard.GetText(). Now, when the event is done and is passed to the control, it will perform the paste fetching the latest text from the clipboard (our altered version). The reason it is wrapped in a try/catch block is because SetText sometimes throws an exception even though it successfully copied the text to the clipboard. You can of course use the other methods provided by Clipboard to get/set the text, this is just a basic version of the solution.
The newly pasted text will inherit the format of the cursor position, similar to manually typing into the RTB.
Unfortunately this will remove the style of the text as well (bold, coloring, etc)
Hope this helps!
Old I know; Daniel's answer works for me, but only if I replace any instance of richTextBox1.Selection and simply make references to the font and size of the entire richTextBox1. In this case, any RTF I paste will inherit the fontfamily and fontsize currently being used by richTextBox1, while preserving and RTF styling.
public void SpecialPaste()
{
var helperRichTextBox = new RichTextBox();
helperRichTextBox.Paste();
helperRichTextBox.SelectionStart = 0;
helperRichTextBox.SelectionLength = 1;
Font lastFont = helperRichTextBox.SelectionFont;
int lastFontChange = 0;
for (int i = 0; i < helperRichTextBox.TextLength; ++i)
{
helperRichTextBox.SelectionStart = i;
helperRichTextBox.SelectionLength = 1;
if (!helperRichTextBox.SelectionFont.Equals(lastFont))
{
lastFont = helperRichTextBox.SelectionFont;
helperRichTextBox.SelectionStart = lastFontChange;
helperRichTextBox.SelectionLength = i - lastFontChange;
helperRichTextBox.SelectionFont = new Font(richTextBox1.Font.FontFamily, richTextBox1.Font.Size, helperRichTextBox.SelectionFont.Style);
lastFontChange = i;
}
}
helperRichTextBox.SelectionStart = helperRichTextBox.TextLength-1;
helperRichTextBox.SelectionLength = 1;
helperRichTextBox.SelectionFont = new Font(richTextBox1.Font.FontFamily, richTextBox1.Font.Size, helperRichTextBox.SelectionFont.Style);
richTextBox1.Rtf = helperRichTextBox.Rtf;
}
I've tried to copy text from a word document and pasted it to a RichTextBox in runtime. Everything works fine. I did not adjust anything specific. Just dropped the RichTextBox onto a form and copied the formatted text from the MS Word document.
I need to load a ~ 10MB range text file into a WPF RichTextBox, but my current code is freezing up the UI. I tried making a background worker do the loading, but that doesnt seem to work too well either.
Here's my loading code. Is there any way to improve its performance? Thanks.
//works well for small files only
private void LoadTextDocument(string fileName, RichTextBox rtb)
{
System.IO.StreamReader objReader = new StreamReader(fileName);
if (File.Exists(fileName))
{
rtb.AppendText(objReader.ReadToEnd());
}
else rtb.AppendText("ERROR: File not found!");
objReader.Close();
}
//background worker version. doesnt work well
private void LoadBigTextDocument(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
System.IO.StreamReader objReader = new StreamReader( ((string[])e.Argument)[0] );
StringBuilder sB = new StringBuilder("For performance reasons, only the first 1500 lines are displayed. If you need to view the entire output, use an external program.\n", 5000);
int bigcount = 0;
int count = 1;
while (objReader.Peek() > -1)
{
sB.Append(objReader.ReadLine()).Append("\n");
count++;
if (count % 100 == 0 && bigcount < 15)
{
worker.ReportProgress(bigcount, sB.ToString());
bigcount++;
sB.Length = 0;
}
}
objReader.Close();
e.Result = "Done";
}
WPF RichTextBox control use Flow Document to display Rich Text and then attach the Flow Document to RTB control,while Windows Form RichTextBox control display Rich Text directly.
that's what makes WPF RTB super slow.
if you are okay with using a WinForm RTB just host it in your wpf app.
the xaml :
<Window x:Class="WpfHostWfRTB.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<Grid>
<WindowsFormsHost Background="DarkGray" Grid.row="0" Grid.column="0">
<wf:RichTextBox x:Name="rtb"/>
</WindowsFormsHost>
</Grid>
</Grid>
</Window>
C# code
private void LoadTextDocument(string fileName, RichTextBox rtb)
{
System.IO.StreamReader objReader = new StreamReader(fileName);
if (File.Exists(fileName))
{
rtb.AppendText(objReader.ReadToEnd());
}
else rtb.AppendText("ERROR: File not found!");
objReader.Close();
}
Graphical controls just isn't designed to handle that kind of data, simply because it would become unworkable. Even if the control could handle the large string, what's visible in the control is so little compared to the entire text that the scroll bars would become practically useless. To locate a specific line in the text you would have to move the slider to the closest position that it could specify, then scroll a line at a time for minutes...
Instead of submitting your users to something useless like that, you should rethink how you display the data, so that you can do it in a way that would actually be possible to use.
I'm working on a very similar project.
The project entails loading a large text file (max size approx: 120MB but we want to go higher) and then constructing an outline of the text file in a tree. Clicking on a node in the tree will scroll the user to that portion of the text file.
After talking to a lot of people I think the best solution is to create a sort of "sliding window" viewer where you only load as much text as the user can see at a time into the rtb.Text.
So.. say load the entire file into a List but only put 100 of those lines into rtb.Text. If the user scrolls up remove the bottom line and add a line of text to the top. If they scroll down remove the top line and add a line of text to the bottom. I get pretty good performance with this solution. (50s to load a 120MB file)
I have notice using RichTextboxes that as you add more "lines" it starts to slow down. If you can do it without appending the '\n' it will speed up for you. Remember each '\n' is a new paragraph object block for the RichTextbox.
This is my method for loading a 10 MB file. It takes about to 30 seconds to load. I use a progress bar dialog box to let my user know it is going to take time to load.
// Get Stream of the file
fileReader = new StreamReader(File.Open(this.FileName, FileMode.Open));
FileInfo fileInfo = new FileInfo(this.FileName);
long bytesRead = 0;
// Change the 75 for performance. Find a number that suits your application best
int bufferLength = 1024 * 75;
while (!fileReader.EndOfStream)
{
double completePercent = ((double)bytesRead / (double)fileInfo.Length);
// I am using my own Progress Bar Dialog I left in here to show an example
this.ProgressBar.UpdateProgressBar(completePercent);
int readLength = bufferLength;
if ((fileInfo.Length - bytesRead) < readLength)
{
// There is less in the file than the lenght I am going to read so change it to the
// smaller value
readLength = (int)(fileInfo.Length - bytesRead);
}
char[] buffer = new char[readLength];
// GEt the next chunk of the file
bytesRead += (long)(fileReader.Read(buffer, 0, readLength));
// This will help the file load much faster
string currentLine = new string(buffer).Replace("\n", string.Empty);
// Load in background
this.Dispatcher.BeginInvoke(new Action(() =>
{
TextRange range = new TextRange(textBox.Document.ContentEnd, textBox.Document.ContentEnd);
range.Text = currentLine;
}), DispatcherPriority.Normal);
}
Why don't you add to a string variable (or perhaps even use StringBuilder) then assign the value to the .Text property when you're done parsing?
You can try this it worked for me.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Create new StreamReader
StreamReader sr = new StreamReader(openFileDialog1.FileName, Encoding.Default);
// Get all text from the file
string str = sr.ReadToEnd();
// Close the StreamReader
sr.Close();
// Show the text in the rich textbox rtbMain
backgroundWorker1.ReportProgress(1, str);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// richTextBox1.Text = e.ProgressPercentage.ToString() + " " + e.UserState.ToString();
richTextBox1.Text = e.UserState.ToString();
}
Have you considered trying to make the app multi-threaded?
How much of the text file do you need to see at once? You may want to look into lazy-loading in .NET or in your case C#
I'm not improve the performance of loading, but I use it to load my richtextbox asynchronously. I hope that could help you.
XAML :
<RichTextBox Helpers:RichTextBoxHelper.BindableSource="{Binding PathFileName}" />
Helper :
public class RichTextBoxHelper
{
private static readonly ILog m_Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public static readonly DependencyProperty BindableSourceProperty =
DependencyProperty.RegisterAttached("BindableSource", typeof(string), typeof(RichTextBoxHelper), new UIPropertyMetadata(null, BindableSourcePropertyChanged));
public static string GetBindableSource(DependencyObject obj)
{
return (string)obj.GetValue(BindableSourceProperty);
}
public static void SetBindableSource(DependencyObject obj, string value)
{
obj.SetValue(BindableSourceProperty, value);
}
public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var thread = new Thread(
() =>
{
try
{
var rtfBox = o as RichTextBox;
var filename = e.NewValue as string;
if (rtfBox != null && !string.IsNullOrEmpty(filename))
{
System.Windows.Application.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Background,
(Action)delegate()
{
rtfBox.Selection.Load(new FileStream(filename, FileMode.Open), DataFormats.Rtf);
});
}
}
catch (Exception exception)
{
m_Logger.Error("RichTextBoxHelper ERROR : " + exception.Message, exception);
}
});
thread.Start();
}
}