I'm using c#, visual studio 2017, winforms and
I'm having a problem with a combobox which is loading some text from a text file and when I select another line of text from the combobox, a linefeed (\r) is added there, and it looks like it's somewhat invisible or better saying, it looks like a newline (\n).
This is the combobox in question and the invisible linefeed (\r).
https://i.stack.imgur.com/Xhymg.png
When I debug the application I can see \r added after that line of text.
https://i.stack.imgur.com/km4F3.png
I've tried to use Encoding.Unicode when saving the text, but to no avail.
//This is how I save text to a file
private void SaveVarNameToFile()
{
using (var writer = File.AppendText("savedVarName.txt"))
{
writer.Write(comboBox1.Text, Encoding.Unicode);
}
}
//This is how I load the text to combobox
private void LoadStrTextFromFile(string fileName, ComboBox cb)
{
if (!File.Exists(fileName))
return;
using (StreamReader reader = new StreamReader(fileName))
{
string x = reader.ReadToEnd();
string[] y = x.Split('\n');
foreach (string s in y)
{
cb.Items.Add(s);
}
reader.Close();
}
}
Contents of the text file:
BOOST_ROOT
NUMBER_OF_PROCESSORS
OS
PROCESSOR_LEVEL
I'm having a hard time figuring out how to remove that pesky little thing. Perhaps there's an easy fix.
If someone can help me find a way or remove it or modify the code so it won't load the \r, I would be very grateful. Thanks.
Windows uses \r\n to mark the end of a line of text. *NIX and Macs use different markers. You can see how different systems handle this here.
Instead of handling the splitting of lines manually, I recommend using built-in functionality for doing this (i.e. File.ReadLines()):
private void LoadStrTextFromFile(string fileName, ComboBox cb)
{
if (!File.Exists(fileName))
return;
foreach (string line in File.ReadLines(fileName))
cb.Items.Add(line);
}
My approach
// remember to use double back slash on the path
string[] text = System.IO.File.ReadAllLines("C:\\test.txt").Where(line => !string.IsNullOrWhiteSpace(line)).Distinct().ToArray(); // read the file into a string array with removing the duplicates and empty lines
comboBox1.Items.AddRange(text); // finally fill in the combobox with the array
Related
I need to paste some rtf text from a datagrid into a flow document. I want to paste it at the caret position in the richtextbox. This works except when i have a bullet or Numbered list. It will paste it before the bullet or number.
I have searched every where for this with no luck.
private void dgStandardText_MouseDoubleClick(object sender, MouseButtonEventArgs e) {
if (dgStandardText.SelectedItems.Count > 0) {
foreach(DataRowView row in dgStandardText.SelectedItems) {
byte[] byteArray = Encoding.ASCII.GetBytes(row[3].ToString());
using(MemoryStream ms = new MemoryStream(byteArray)) {
TextRange tr = new TextRange(txtAreaText.CaretPosition, txtAreaText.CaretPosition);
tr.Load(ms, DataFormats.Rtf);
}
}
}
NeedSave = true;
dgStandardText.SelectedItems.Clear();
}
Before Paste
After Paste
This seems to happen when the content you're pasting includes block elements, as opposed to inline elements. For plain text, WPF seems to treat newlines (e.g., \r\n) as paragraph breaks, so multi-line content would be translated into multiple blocks. What you want to do is insert just the inline content from each of those source blocks, such that it goes into the current block of the target document (in this case, a list item). Do this for the first source block, then insert a paragraph break, update the caret position, and move on to the next block.
Is the content of the grid row actually RTF content, or is it plain text? If it's plain text, then this should be pretty straightforward. Try changing your foreach loop body to something like this:
var text = row[3].ToString();
var lines = text.Split(
new[] { '\r', '\n' },
StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var p = txtAreaText.CaretPosition;
txtAreaText.BeginChange();
p.InsertTextInRun(line);
p = p.InsertParagraphBreak();
txtAreaText.EndChange();
txtAreaText.CaretPosition = p;
}
Now, if the source content really is RTF, then things will get more complicated. I suspect you'll need to load it into an in-memory flow document and walk through the elements to figure out where the paragraph breaks are. For each paragraph, locate the inline contents that intersect the selection range, and insert them into the target document. Insert a paragraph break when you reach the end of each paragraph in the source range, just like in the example above.
I'm afraid the Documents API is the one area of WPF that I haven't explored very thoroughly, so I can only give you a general idea of what to do. Perhaps someone else can provide more detail.
For some reason when I read in clipboard data and write it to a file, read that file in and set it to a list after delineating it with ¢ it loads up my listbox just fine on the first load as in when my form first loads up. However I have the following trigger on a button click, it is for some reason splitting up multiple line sections into separate list items, which is not what I want and not what the same code does when the form first loads. It's a little frustrating as it's writing to the text file and everything the same way.
private void button2_Click_1(object sender, EventArgs e)
{
// until next comment this is the same as what I have run at the start of the
// program, it loads up multiple lines into one list item as it should
string checkForDupe = File.ReadAllText(#"C:\temp\testfile.txt");
string checkResponses = File.ReadAllText(#"C:\temp\testfile2.txt");
if (Clipboard.ContainsText() && !checkForDupe.Contains(Clipboard.GetText()))
{
if (!checkResponses.Contains(Clipboard.GetText()))
{
var text = "\n" + Clipboard.GetText() + "¢";
File.AppendAllText(#"C:\temp\testfile.txt", text);
}
}
//The following has no affect on the issue stated in my question I have tried with out it.
string[] responseTags2 = File.ReadAllLines(#"C:\temp\testfile.txt");
List<string> _responseTags2 = new List<string>(responseTags2);
var count = _responseTags2.Count;
// Perform a reverse tracking.
for (var i = count - 1; i > -1; i--)
{
if (_responseTags2[i] == string.Empty) _responseTags2.RemoveAt(i);
}
// Keep only the unique list items.
_responseTags2 = _responseTags2.Distinct().ToList();
listBox1.BeginUpdate();
listBox1.DataSource = _responseTags2;
listBox1.EndUpdate();
}
input example:
"This is multiple lines in
a text file that is for testing
this application"
right output example (what I get when the same code is running at the start of the program before the form is loaded):
"This is multiple lines in
a text file that is for testing
this application" ,
"This is ANOTHER multiple lines in
a text file that is for testing
this application" ,
"This is a single line"
WRONG output (what I get when I run off the button click that eventual updates the UI):
"This is multiple lines in",
"a text file that is for testing",
"this application"
You are getting multiple lines in your listbox because you are not breaking the text by ¢ but just reading them into lines. This is your code:
string[] responseTags2 = File.ReadAllLines(#"C:\temp\testfile.txt");
Do this instead. Read all the text:
var contents = File.ReadAllText(#"C:\temp\testfile.txt");
Now split them using the character ¢:
var lines = s.Split('¢');
Here is the final linq to remove any empties and return distinct items:
var lines = s.Split('¢')
.Where(item => item != string.Empty)
.Distinct()
.ToList();
Then set lines to be the datasource:
listBox1.BeginUpdate();
listBox1.DataSource = lines;
listBox1.EndUpdate();
This code works fine for me, but I think it's basically what you're doing, only slightly cleaned up (and I didn't use the second file since you're not using it in the example).
My suggestion would be to create a single method that does this operation, and then call that method from wherever you need it. This way you will ensure that you're doing the exact same thing in both (all) places.
UPDATE
I updated the method to save the clipboard text as-is, plus adding a ¢ character to the end, when saving to the file. Then, when reading the file the second time, first join all the lines with a space character, then split on the ¢ character, and use that list for your data source.
private const string DefaultSaveFile = #"C:\temp\testfile.txt";
private void CopyUniqueClipboardTextToFile(string filePath = null,
bool updateListbox = true)
{
// Use global default file if nothing was passed
if (filePath == null) filePath = DefaultSaveFile;
// Ensure our files exist
if (!File.Exists(filePath)) File.CreateText(filePath).Close();
string fileContents = File.ReadAllText(filePath);
string clipboardText = Clipboard.GetText();
// Update the file with any new clipboard text
if (Clipboard.ContainsText() && !fileContents.Contains(clipboardText))
{
// Save the lines to our file, with a '¢' character at the end
File.AppendAllText(filePath, $"{Environment.NewLine}{clipboardText}¢");
}
// Re-read the new file into a single string
string entireFileAsOneLine = string.Join(" ",
File.ReadAllLines(filePath).Distinct().ToList());
// Now split that string on the '¢' character
string[] listItems = entireFileAsOneLine.Split('¢');
// Update listbox if necessary
if (updateListbox)
{
listBox1.BeginUpdate();
listBox1.DataSource = listItems;
listBox1.EndUpdate();
}
}
private void button2_Click(object sender, EventArgs e)
{
CopyUniqueClipboardTextToFile();
}
What I ended up doing is the following;
string responseTags2 = checkForDupe.Replace('\n', '╜');
I replaced new lines with a rarely used character, then split by ¢ to fill the list.
To replace the special character so that it would be formatted correctly again I just replaced it when setting the clipboard text with the \n again.
Here's the code.
private void SaveButton_Click(object sender, EventArgs e)
{
string textBoxText = TextBox.Text;
var lines = File.ReadAllLines(#"F:\Bioshock2SP.ini");
foreach(string line in lines)
{
if (line.Contains("VoVolume="))
{
//This is where I get confused.
string settingLine = line;
string replaceline = (line.Replace(line, textBoxText));
File.WriteAllText(#"F:\Bioshock2SP.ini", settingLine);
}
break;
}
MessageBox.Show("Setting saved!");
}
The idea is to replace part of a setting in a Settings.ini file for a game I play, using the user input of a textbox in my form. The user types in a number for example, "1.56" and then hit the Save button to replace the existing line with their input. In this case that setting is the volume.
The application runs completely fine, but after hitting save and going into the settings file my input isn't saved.
There should be a change in the way you save the file.
Save each line of the file as you get it, editing if required.
void SaveButton_Click(object sender, EventArgs e)
{
var textBoxText = TextBox.Text;
var lines = File.ReadAllLines(#"F:\Bioshock2SP.ini");
using (var file = new StreamWriter(#"F:\Bioshock2SP.ini"))
foreach(string line in lines)
{
if (line.Contains("VoVolume="))
file.WriteLine(line.Substring(0, 9) + textBoxText); // Writes something like 'VoVolume=1.56'
else file.WriteLine(line); // No editing required
}
MessageBox.Show("Setting saved!");
}
I think there are a couple of separate issues with your code:
Replace Function
string replaceline = (line.Replace(line, textBoxText));
Replace accepts a string to look for and a string to replace it with. Your first argument is 'line', so it would replace the whole line with the value in textBoxText. I assume you only want to replace a portion of the line with that value. In that case, you need to use something like line.Replace(searchString, textBoxText) where you have previously defined searchString as the text you want to replace. If you don't know what that value is, but there is a pattern, you might want to look into using regular expressions which will let you define a pattern to search and replace.
WriteAllText Function
File.WriteAllText(#"F:\Bioshock2SP.ini", settingLine);
This line will replace the entire contents of BioShock2SP.ini with the value in settingLine. There are two problems here.
One is that settingLine was the saved value before you did the replacement - so it has not included the results of your replace operation. You should use replaceline (assuming it has been correctly modified).
Even if you do that, though, the other is that File.WriteAllText will replace the whole file with the value in settingLine - which is probably not what you want. You'd be better off modifying the line in the array and using File.WriteAllLines to re-output the whole array - assuming the file has multiple lines in it.
The hints above may help you resolve this - to properly answer the question though, I'd need to see a sample of what the file looks like, and the patterns you are trying to replace.
So i am attempting to teach myself C#, I have a program that I originally wrote in batch and am attempting to recreate in C# using WPF. I have a button that allows a user to set a directory, that directory selected is then displayed in a text box above a listbox which adds every subfolder, only first level, to the listbox. Now all this works fine but it writes out the entire directory path in the listbox. I have been trying to figure out how to strip the leading directory path off the list box entries for over an hour to no avail. Here is what I have so far:
private void btn_SetDirectory_Click(object sender, RoutedEventArgs e)
{
//Create a folder browser dialog and set the selected path to "steamPath"
var steamPath = new FolderBrowserDialog();
DialogResult result = steamPath.ShowDialog();
//Update the text box to reflect the selected folder path
txt_SteamDirectory.Text = steamPath.SelectedPath;
//Clear and update the list box after choosing a folder
lb_FromFolder.Items.Clear();
string folderName = steamPath.SelectedPath;
foreach (string f in Directory.GetDirectories(folderName))
{
lb_FromFolder.Items.Add(f);
}
}
Now I tried changing the last line to this, and it did not work it just crashed the program:
foreach (string f in Directory.GetDirectories(folderName))
{
lb_FromFolder.Items.Add(f.Substring(f.LastIndexOf("'\'")));
}
I am fairly certain that the LastIndexOf route is probably the right one but I am at a dead end. I apologize if this is a dumb question but this is my first attempt at using C#. Thanks in advance.
This can solve your issue
string folderName = steamPath.SelectedPath;
foreach (string f in Directory.GetDirectories(folderName))
{
// string[] strArr = f.Split('\\');
lb_FromFolder.Items.Add(f.Split('\\')[f.Split('\\').Length-1]);
}
You can use this code:
string folderName = steamPath.SelectedPath;
foreach (string f in Directory.GetDirectories(folderName))
{
lb_FromFolder.Items.Add(f.Remove(0,folderName.Length));
}
When using the Microsoft RichTextBox control it is possible to add new lines like this...
richtextbox.AppendText(System.Environment.NewLine); // appends \r\n
However, if you now view the generated rtf the \r\n characters are converted to \par not \line
How do I insert a \line control code into the generated RTF?
What does't work:
Token Replacement
Hacks like inserting a token at the end of the string and then replacing it after the fact, so something like this:
string text = "my text";
text = text.Replace("||" "|"); // replace any '|' chars with a double '||' so they aren't confused in the output.
text = text.Replace("\r\n", "_|0|_"); // replace \r\n with a placeholder of |0|
richtextbox.AppendText(text);
string rtf = richtextbox.Rtf;
rtf.Replace("_|0|_", "\\line"); // replace placeholder with \line
rtf.Replace("||", "|"); // set back any || chars to |
This almost worked, it breaks down if you have to support right to left text as the right to left control sequence always ends up in the middle of the placeholder.
Sending Key Messages
public void AppendNewLine()
{
Keys[] keys = new Keys[] {Keys.Shift, Keys.Return};
SendKeys(keys);
}
private void SendKeys(Keys[] keys)
{
foreach(Keys key in keys)
{
SendKeyDown(key);
}
}
private void SendKeyDown(Keys key)
{
user32.SendMessage(this.Handle, Messages.WM_KEYDOWN, (int)key, 0);
}
private void SendKeyUp(Keys key)
{
user32.SendMessage(this.Handle, Messages.WM_KEYUP, (int)key, 0);
}
This also ends up being converted to a \par
Is there a way to post a messaged directly to the msftedit control to insert a control character?
I am totally stumped, any ideas guys? Thanks for your help!
Adding a Unicode "Line Separator" (U+2028) does work as far as my testing showed:
private void Form_Load(object sender, EventArgs e)
{
richText.AppendText("Hello, World!\u2028");
richText.AppendText("Hello, World!\u2028");
string rtf = richText.Rtf;
richText.AppendText(rtf);
}
When I run the program, I get:
Hello, World!
Hello, World!
{\rtf1\ansi\ansicpg1252\deff0\deflang1031{\fonttbl{\f0\fnil\fcharset0 Courier New;}}
{\colortbl ;\red255\green255\blue255;}
\viewkind4\uc1\pard\cf1\f0\fs17 Hello, World!\line Hello, World!\line\par
}
It did add \line instead of \par.
Since you want to use a different RTF code, I think you may need to forget about the simplistic AppendText() method and manipulate the .Rtf property of your RichTextBox directly instead. Here is a sample (tested) to demonstrate:
RichTextBox rtb = new RichTextBox();
//this just gets the textbox to populate its Rtf property... may not be necessary in typical usage
rtb.AppendText("blah");
rtb.Clear();
string rtf = rtb.Rtf;
//exclude the final } and anything after it so we can use Append instead of Insert
StringBuilder richText = new StringBuilder(rtf, 0, rtf.LastIndexOf('}'), rtf.Length /* this capacity should be selected for the specific application */);
for (int i = 0; i < 5; i++)
{
string lineText = "example text" + i;
richText.Append(lineText);
//add a \line and CRLF to separate this line of text from the next one
richText.AppendLine(#"\line");
}
//Add back the final } and newline
richText.AppendLine("}");
System.Diagnostics.Debug.WriteLine("Original RTF data:");
System.Diagnostics.Debug.WriteLine(rtf);
System.Diagnostics.Debug.WriteLine("New Data:");
System.Diagnostics.Debug.WriteLine(richText.ToString());
//Write the RTF data back into the RichTextBox.
//WARNING - .NET will reformat the data to its liking at this point, removing
//any unused colors from the color table and simplifying/standardizing the RTF.
rtb.Rtf = richText.ToString();
//Print out the resulting Rtf data after .NET (potentially) reformats it
System.Diagnostics.Debug.WriteLine("Resulting Data:");
System.Diagnostics.Debug.WriteLine(rtb.Rtf);
Output:
Original RTF data:
{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
\viewkind4\uc1\pard\f0\fs17\par
}
New RTF Data:
{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
\viewkind4\uc1\pard\f0\fs17\par
example text0\line
example text1\line
example text2\line
example text3\line
example text4\line
}
Resulting RTF Data:
{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
\viewkind4\uc1\pard\f0\fs17\par
example text0\line example text1\line example text2\line example text3\line example text4\par
}
if you are using paragraphs to write to richtextbox you can use the LineBreak() same code shown below
Paragraph myParagraph = new Paragraph();
FlowDocument myFlowDocument = new FlowDocument();
// Add some Bold text to the paragraph
myParagraph.Inlines.Add(new Bold(new Run(#"Test Description:")));
myParagraph.Inlines.Add(new LineBreak()); // to add a new line use LineBreak()
myParagraph.Inlines.Add(new Run("my text"));
myFlowDocument.Blocks.Add(myParagraph);
myrichtextboxcontrolid.Document = myFlowDocument;
Hope this helps!