Dynamicaly write text in textbox while performing action - c#

I have simple WPF form in .NET, which allows mw to select directory and then perform some action on files in them
I have button, which triggers the action (it is transforming XML files to csv). This takes some time, so I would like to write into textbox information about the number of files processed etc.
I can do that, but all messages which are send to textbox during Click action are shown after the whole procedure is finished. What I want, is to send messages to textbox while processing the data in Click method.
This is method triggered on click:
private void processButton_Click(object sender, RoutedEventArgs e)
{
List<string> allXmlFiles = ProcessDirectory(selectedDir);
textbox.Text += String.Format("\n{0} files will be processed", allXmlFiles.Count);
if (allXmlFiles.Count > 0)
{
textbox.Text += "\nProcessing files...";
foreach (string filepath in allXmlFiles)
{
try
{
ParseFile(filepath);
}
catch
{
textbox.Text += String.Format("\nCannot process file {0}", filepath);
}
}
}
textbox.Text += "\nDone";
}
How can I make the messages ("X files will be processed", "Processing files...", "Cannot process file XYZ") appear while calculating and not after?

this because you are starting your parsing synchronously to overcome your issue try BackgroundWorker with something like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace WpfApplication2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
BackgroundWorker bckg = new BackgroundWorker();
private List<string> allXmlFiles;
public MainWindow()
{
InitializeComponent();
bckg.DoWork += new DoWorkEventHandler(bckg_DoWork);
bckg.ProgressChanged += new ProgressChangedEventHandler(bckg_ProgressChanged);
bckg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bckg_RunWorkerCompleted);
}
void bckg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error!=null)
{
textbox.Text += String.Format("\nCannot process file {0}", filepath);
}
}
void bckg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//here you can update your textblox
Dispatcher.Invoke(() =>
{
textbox.Text = e.UserState.ToString();
});
}
void bckg_DoWork(object sender, DoWorkEventArgs e)
{
allXmlFiles = ProcessDirectory(selectedDir);
if (allXmlFiles.Count > 0)
{
bckg.ReportProgress("here in percentage", "\nProcessing files...");
foreach (string filepath in allXmlFiles)
{
try
{
ParseFile(filepath);
}
catch
{
throw;
}
}
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
textbox.Text += String.Format("\n{0} files will be processed", allXmlFiles.Count);
bckg.RunWorkerAsync();
}
}
}

This is happening, because your operations are synchronous. Extract your processing files operation in another thread. If you are using .NET 4.5+, try adding the async word in the method:
private async void processButton_Click(object sender, RoutedEventArgs e)

You could have your 'Click' event call another method which handles this logic to end this method as soon as possible.
Otherwise a simple work around to what you already have would be to have a method to update your log information.
For example:
UpdateText(String newText)
{
textBox.Text += "\r\n" + newText;
}
With this, using your loop, you could report the number of files processed. You could do this in chunks to avoid spam.
Your for loop, with suggestions:
int numProcessed = 0;
foreach (string filepath in allXmlFiles)
{
try
{
ParseFile(filepath);
numProcessed++; // or you could use '++numProcessed' inline
if(numProcessed % 10 == 0 && numProcessed >= 10)
UpdatedText("Another 10 files processed!");
}
catch
{
UpdateText(String.Format("\nCannot process file {0}", filepath));
}
}
UpdatedText("Finished");

Related

C# display a variable in a Textbox

i am sending Sensor information with a NUCLEOF411RE to my PC. I receive this data on the COM98 with a BaudRate of 115200. Now i want to program a Windows Application that will split my string and put it on my textboxes. until now i display the data with a Button_click event. It puts values on the Textboxes that actually are the real values. But if i move my Sensor and klick the button again there should be a lot more different values, but there are the same values on the textboxes. In addition i want to refresh the textboxes automatically and not with a button click.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace BNO080
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
getAvailablePorts();
}
public string comport;
SerialPort serial = new SerialPort();
void getAvailablePorts()
{
String[] ports = SerialPort.GetPortNames();
comboBox1.Items.AddRange(ports);
comport = comboBox1.Text;
}
private void button1_Click(object sender, EventArgs e)
{
try
{
if(comboBox1.Text=="" || textBox6.Text=="")
{
MessageBox.Show("Please Select Port Settings");
}
else
{
serial.PortName = comboBox1.Text;
serial.BaudRate = Convert.ToInt32(textBox6.Text);
serial.Parity = Parity.None;
serial.StopBits = StopBits.One;
serial.DataBits = 8;
serial.Handshake = Handshake.None;
serial.Open();
MessageBox.Show("connected!");
}
}
catch (UnauthorizedAccessException)
{
MessageBox.Show("Unauthorised Access");
}
}
private void button2_Click(object sender, EventArgs e)
{
textBox1.Clear();
textBox2.Clear();
textBox3.Clear();
textBox4.Clear();
textBox5.Clear();
MessageBox.Show("connection closed!");
serial.Close();
}
private void button3_Click(object sender, EventArgs e)
{
try
{
textBox5.Text = serial.ReadLine();
/*String[] Stringsizes = A.Split(new char[] {' '});
textBox1.Text = Stringsizes[0];
textBox2.Text = Stringsizes[1];
textBox3.Text = Stringsizes[2];
textBox4.Text = Stringsizes[3];*/
// textBox5.Text = A;
//Array.Clear(Stringsizes, 0, 3);
}
catch (Exception) { }
}
}
}
can someone help me?
Can you give more information why you use the Button_Click Event to read the text? Maybe it is a possible way for you to subscribe for the DataReceived-Event of the COM-port?
It would look something like this:
serial.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
string receivedString = serial.ReadExisting();
//Do something here...
}
I'd do a couple things. First subscribe to the DataReceived event on the serial port. This event handler will get called when there is data available on the serial port. Then in the event handler you can read from the serial port and add it to your textbox. You can't add it directly (see the AppendText function) because the event handler is called with a different thread, only the UI thread can update UI components (or you'll get a cross-thread exception).
...
public Form1()
{
InitializeComponent();
getAvailablePorts();
// Subscribe to the DataReceived event. Our function Serial_DataReceived
// will be called whenever there's data available on the serial port.
serial.DataReceived += Serial_DataReceived;
}
// Appends the given text to the given textbox in a way that is cross-thread
// safe. This can be called by any thread, not just the UI thread.
private void AppendText(TextBox textBox, string text)
{
// If Invoke is required, i.e. we're not running on the UI thread, then
// we need to invoke it so that this function gets run again but on the UI
// thread.
if (textBox.InvokeRequired)
{
textBox.BeginInvoke(new Action(() => AppendText(textBox, text)));
}
// We're on the UI thread, we can append the new text.
else
{
textBox.Text += text;
}
}
// Gets called whenever we receive data on the serial port.
private void Serial_DataReceived(object sender,
SerialDataReceivedEventArgs e)
{
string serialData = serial.ReadExisting();
AppendText(textBox5, serialData);
}
Because i want to add an rotating 3D cube i decided to switch to WPF. I heard it is much easier to implement a 3D graphic there. So i copied my code to the new WPF project. But now i got already problems to visualize my values on the Textboxes. It doesnt work. It looks like the Evenhandler did not fire an event while receiving Data from the com port.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data;
using System.Drawing;
namespace cube
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
getAvailablePorts();
serial.DataReceived += Serial_DataReceived;
}
public bool button3clicked = false;
public bool button4clicked = false;
public bool button5clicked = false;
SerialPort serial = new SerialPort();
void getAvailablePorts()
{
List<string> Itemlist = new List<string>();
String[] ports = SerialPort.GetPortNames();
Itemlist.AddRange(ports);
comboBox1.ItemsSource = Itemlist;
}
private void button1_Click_1(object sender, EventArgs e)
{
try
{
if (comboBox1.Text == "" || textBox6.Text == "")
{
MessageBox.Show("Please Select Port Settings");
}
else
{
serial.PortName = comboBox1.Text;
serial.BaudRate = Convert.ToInt32(textBox6.Text);
serial.Parity = Parity.None;
serial.StopBits = StopBits.One;
serial.DataBits = 8;
serial.Handshake = Handshake.None;
serial.Open();
MessageBox.Show("connected!");
}
}
catch (UnauthorizedAccessException)
{
MessageBox.Show("Unauthorised Access");
}
}
private void button2_Click_1(object sender, EventArgs e)
{
textBox1.Clear();
textBox2.Clear();
textBox3.Clear();
textBox4.Clear();
textBox5.Clear();
MessageBox.Show("connection closed!");
serial.Close();
textBox1.Text = "test";
}
private void AppendText(string[] text)
{
try
{
textBox1.Text = text[0];
textBox2.Text = text[1];
textBox3.Text = text[2];
textBox4.Text = text[3];
}
catch (Exception) { }
}
private void Safe_Position1(TextBox tBr1, TextBox tBi1, TextBox tBj1, TextBox tBk1, string[] text)
{
if (button3clicked == true)
{
tBr1.Text = text[0];
tBi1.Text = text[1];
tBj1.Text = text[2];
tBk1.Text = text[3];
button3clicked = false;
}
}
private void Safe_Position2(TextBox tBr2, TextBox tBi2, TextBox tBj2, TextBox tBk2, string[] text)
{
if (button4clicked == true)
{
tBr2.Text = text[0];
tBi2.Text = text[1];
tBj2.Text = text[2];
tBk2.Text = text[3];
button4clicked = false;
}
}
private void Safe_Position3(TextBox tBr3, TextBox tBi3, TextBox tBj3, TextBox tBk3, string[] text)
{
if (button5clicked == true)
{
tBr3.Text = text[0];
tBi3.Text = text[1];
tBj3.Text = text[2];
tBk3.Text = text[3];
button5clicked = false;
}
}
private void Serial_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
string serialData = serial.ReadLine();
String[] text = serialData.Split(new char[] { ' ' });
AppendText(text);
Safe_Position1(textBox5, textBox7, textBox8, textBox9, text);
Safe_Position2(textBox10, textBox11, textBox12, textBox13, text);
Safe_Position3(textBox14, textBox15, textBox16, textBox17, text);
}
private void button3_Click(object sender, EventArgs e)
{
button3clicked = true;
}
private void button4_Click(object sender, EventArgs e)
{
button4clicked = true;
}
private void button5_Click(object sender, EventArgs e)
{
button5clicked = true;
}
}
}

Retrieving the File Name of a .mp3 file without the path

Reasoning:
I've seen this question duplicated a few times on this website, and each one seems to bring people to same conclusion.
Okay, so I have a music player located within a window. When the window opens, an OpenFileDialog method is ran, to where the user must pick a .mp3 file. Once done, they can play the file whilst navigating other windows in the program.
I'm trying to:
Enter the file name of the selected file into a label. With this, I want to show the file name as it is being played. I want to only show the file name, not the path to it as well.
What I've seen on duplicate questions:
Repeating fixes whereby I can use the following code to pick up the file name with or without the file extension:
label1.Content = Path.GetFileName(media.Source.ToString());
label1.Content = Path.GetFileNameWithoutExtension(media.Source.ToString());
Issue:
I've tried to enter this on my program, but I don't have the "Path" section (I'm not too sure of the actual name of this part of code).
The closest I can get to this is: class System.Windows.Shapes.Path | Draws a series of connected lines and curves.
I don't seem to have another "path" in my VS, so I'm not too sure what to do.
Code (Sorry about the code here, it was a little awkward to copy into the question box)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Microsoft.Win32;
using System.Windows.Threading;
namespace WolfAlbumCatalogue
{
/// <summary>
/// Interaction logic for MusicPlayer.xaml
/// </summary>
public partial class MusicPlayer : Window
{
private MediaPlayer mediaPlayer = new MediaPlayer();
AlbumCatalogue albumCatalogue;
public MusicPlayer(AlbumCatalogue parent)
{
InitializeComponent();
albumCatalogue = parent;
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "MP3 files (*.mp3)|*.mp3|All files(*.*)|*.*";
if (openFileDialog.ShowDialog() == true)
mediaPlayer.Open(new Uri(openFileDialog.FileName));
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += timer_Tick;
timer.Tick += miniTimer_Tick;
timer.Start();
albumCatalogue.img_musicPlayerWindow.IsEnabled = false;
lbl_songName.Content = Path
}
private void rec_closeHover_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.Close();
albumCatalogue.img_musicPlayerWindow.IsEnabled = true;
}
void timer_Tick(object sender, EventArgs e)
{
if (mediaPlayer.Source != null)
{
lbl_timer.Content = String.Format("{0} / {1}", mediaPlayer.Position.ToString(#"mm\:ss"), mediaPlayer.NaturalDuration.TimeSpan.ToString(#"mm\:ss"));
}
else
{
lbl_timer.Content = "No file selected...";
}
}
void miniTimer_Tick(object sender, EventArgs e)
{
if (mediaPlayer.Source != null)
{
lbl_miniTimer.Content = String.Format("{0} / {1}", mediaPlayer.Position.ToString(#"mm\:ss"), mediaPlayer.NaturalDuration.TimeSpan.ToString(#"mm\:ss"));
}
else
{
lbl_miniTimer.Content = "No file selected...";
}
}
private void btn_play_Click(object sender, RoutedEventArgs e)
{
mediaPlayer.Play();
lbl_play_pause.Content = "Playing...";
lbl_play_pause.Visibility = Visibility.Visible;
}
private void btn_pause_Click(object sender, RoutedEventArgs e)
{
mediaPlayer.Pause();
lbl_play_pause.Content = "Paused.";
lbl_play_pause.Visibility = Visibility.Visible;
}
private void btn_stop_Click(object sender, RoutedEventArgs e)
{
mediaPlayer.Stop();
lbl_play_pause.Visibility = Visibility.Hidden;
}
private void rec_titleBar_MouseDown(object sender, MouseButtonEventArgs e)
{
this.DragMove();
}
private void rec_windowBkg_MouseDown(object sender, MouseButtonEventArgs e)
{
this.DragMove();
}
private void rec_btnOpenAudioFile_MouseDown(object sender, MouseButtonEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "MP3 files (*.mp3)|*.mp3|All files(*.*)|*.*";
if (openFileDialog.ShowDialog() == true)
mediaPlayer.Open(new Uri(openFileDialog.FileName));
DispatcherTimer timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += timer_Tick;
timer.Start();
}
}
}
I would copy in the XAML too but I use blend animations and it's way too long for this question. If you need it, please let me know :)
That said, I think that's everything!
Thank you
Include the header System.IO in your application.
(using System.IO;) .If it is not found you need to add the reference System dll.

Dowloading Files One at a time in C# [duplicate]

This question already has answers here:
True Parallel Downloads
(2 answers)
Closed 8 years ago.
I am trying to download one file at a time. But when I add my value2 into the await Task.Delay(value2); It still downloads both files at the same instead of one at a time. Mind you that I will be putting checkboxes to the ones that I want to download and there are going to be about 20 to 50 downloads to which I would be able to choose which to download. But the main thing is how to download one at a time instead of all at the same time.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;
using System.Diagnostics;
namespace DownloadFileCSharp8
{
public partial class Form1 : Form
{
private Stopwatch workerTimeElaspsed;
public Form1()
{
InitializeComponent();
}
private async void btnGetDownload_Click(object sender, EventArgs e)
{
string text = label5.Text;
int value2;
//value2 = value2 + 5;
int.TryParse(text, out value2);
InitiateDownload("http://stie.text1.txt", #"E:\Files\text1.txt", wc_DownloadFileCompleted, "text1.txt");
await Task.Delay(value2);
InitiateDownload("http://site.text.docx", #"E:\Files\text2.docx", wc_DownloadFileCompleted, "text2.docx");
}
void InitiateDownload(string RemoteAddress, string LocalFile, AsyncCompletedEventHandler CompleteCallBack, object userToken)
{
WebClient wc = new WebClient();
wc.DownloadProgressChanged += wc_DownloadProgressChanged;
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadTimer);
wc.DownloadFileCompleted += wc_DownloadFileCompleted;
wc.DownloadFileAsync(new Uri(RemoteAddress), LocalFile, userToken);
workerTimeElaspsed = new Stopwatch();
workerTimeElaspsed.Start();
}
private void DownloadTimer(object sender, DownloadProgressChangedEventArgs e)
{
progressBar2.Value = e.ProgressPercentage;
if (e.ProgressPercentage > 0)
{
double totalTime = (100d / (double)e.ProgressPercentage) * workerTimeElaspsed.Elapsed.TotalSeconds;
double remaining = totalTime - workerTimeElaspsed.Elapsed.TotalSeconds;
label5.Text = Math.Round(remaining).ToString();
}
}
void wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
lblInfo1.Visible = true;
lblInfo1.ForeColor = Color.Red;
lblInfo1.Text = "Error Downloading ";
//throw e.Error;
}
else if (e.Cancelled)
{
lblInfo1.Visible = true;
lblInfo1.ForeColor = Color.Red;
lblInfo1.Text = "Download Cancelled " + e.UserState + e.Error;
}
else
{
lblInfo1.Visible = true;
lblInfo1.ForeColor = Color.Red;
lblInfo1.Text = e.UserState + " Download Complete!! ";
}
//throw new NotImplementedException();
}
void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
double bytesIn = double.Parse(e.BytesReceived.ToString());
double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
double percentage = bytesIn / totalBytes * 100;
progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void progressBar1_Click(object sender, EventArgs e)
{
}
}
}
You call InitiateDownload to start the download, then wait for a fixed period of time (which appears to be very short), then you call InitiateDownload again to start another download *regardless of whether or not the first download has finished.
What you want to do is re-write InitiateDownload so that it returns a Task that indicates when the download is complete. You can then await that task and start the next download when it is done.
The easiest way to do that is to simply use the DownloadDataTaskAsync method instead of DownloadFileAsync.
You need to use your DownloadFileCompleted event to trigger the next InitiateDownload if you want them to happen serially.

Spell checker in a WPF RichTextBox

I have created a RichTextBox text editor as a c# Form application solution. I have added a WPF application to this solution to act as a spellchecker. Basically, when I run the spell checker the contents of the RichTextBox are copied to a WPF RichTextBox in the WPF application which I have configured to look like a spell check dialog. I have included a ListBox which displays the spelling error suggestions and buttons that allow the user to ignore errors or change if one of the suggestions are clicked.
The main routine loops through the text in the RichTextBox until it finds a spelling error. The user then has the option to ignore by clicking a button that calls EditingCommands.IgnoreSpellingError or change by clicking a button that calls EditingCommands.CorrectSpellingError. Currently I am using another button to then re-cycle through the RichTextBox to find the next spelling error.
Ideally what I would like to happen is, for example, when the ignore button is clicked, the EditingCommands.IgnoreSpellingError is called and then the main routine is run immediately after, like so:
private void button3_Click(object sender, RoutedEventArgs e)
{
button3.Command = EditingCommands.IgnoreSpellingError;
button3.CommandTarget = richTextBox1;
spellCheckWords();
}
However, this does not work as the RichTextBox seems to go out of synchronisation with the spell checker. It's as if the form or the RichTextBox are not refreshing properly. Just to re-iterate, if I use a separate button to re-run the main routine then it works ok.
The full code is below.
Any help would be appreciated.
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
richTextBox1.SpellCheck.IsEnabled = true;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
richTextBox1.Paste();
richTextBox1.ContextMenu = GetContextMenu();
spellCheckWords();
}
private static int chrPos = 0;
private void spellCheckWords()
{
SpellingError spellingError;
TextRange spellErrorRange;
TextPointer start_pointer, end_pointer;
richTextBox1.ContextMenu = GetContextMenu();
richTextBox1.SelectAll();
int txtLen = richTextBox1.Selection.Text.Length;
bool noSpellingErrors = true; ;
for (int i = chrPos; i < txtLen; i++)
{
start_pointer = richTextBox1.Document.ContentStart.GetNextInsertionPosition
(LogicalDirection.Forward).GetPositionAtOffset(i, LogicalDirection.Forward);
spellingError = richTextBox1.GetSpellingError(start_pointer);
if (spellingError != null)
{
spellErrorRange = richTextBox1.GetSpellingErrorRange(start_pointer);
int errRange = spellErrorRange.Text.Length;
textBox1.Text = spellErrorRange.Text;
noSpellingErrors = true;
string textRun = start_pointer.GetTextInRun(LogicalDirection.Forward);
string trimmedString = string.Empty;
end_pointer = richTextBox1.Document.ContentStart.GetNextInsertionPosition
(LogicalDirection.Forward).GetPositionAtOffset(i + errRange, LogicalDirection.Forward);
richTextBox1.Selection.Select(start_pointer, start_pointer);
richTextBox1.Focus();
Rect screenPos = richTextBox1.Selection.Start.GetCharacterRect(LogicalDirection.Forward);
double offset = screenPos.Top + richTextBox1.VerticalOffset;
richTextBox1.ScrollToVerticalOffset(offset - richTextBox1.ActualHeight / 2);
listBox1.Items.Clear();
foreach (string str in spellingError.Suggestions)
{
listBox1.Items.Add(str);
}
//chrPos = i + errRange + 1;
return;
}
}
if (noSpellingErrors == true)
{
textBox1.Text = "No spelling Errors";
listBox1.Items.Clear();
button2.IsEnabled = false;
button3.IsEnabled = false;
button4.IsEnabled = false;
button6.IsEnabled = false;
}
}
private void button1_Click(object sender, RoutedEventArgs e)
{
richTextBox1.SelectAll();
richTextBox1.Copy();
richTextBox1.Document.Blocks.Clear();
richTextBox1.SpellCheck.IsEnabled = false;
this.Close();
}
private ContextMenu GetContextMenu()
{
return null;
}
private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (listBox1.SelectedItem != null)
textBox1.Text = listBox1.SelectedItem.ToString();
}
private void button2_Click(object sender, RoutedEventArgs e)
{
button2.Command = EditingCommands.CorrectSpellingError;
button2.CommandParameter = textBox1.Text;
button2.CommandTarget = richTextBox1;
spellCheckWords();
}
private void button3_Click(object sender, RoutedEventArgs e)
{
button3.Command = EditingCommands.IgnoreSpellingError;
button3.CommandTarget = richTextBox1;
spellCheckWords();
}
private void button4_Click(object sender, RoutedEventArgs e)
{
spellCheckWords();
}
}
}
After a while i got your problem^^ (i think) and it is easy to fix.
In you Button-Click-events you set the command of the button and the parameters and then execute spellCheckWords. But the actual command is executed AFTER the code has leaved the method.
Which means, spellCheckWords() is executed BEFORE IgnoreSpellingError.
Change your code to this:
private void button2_Click(object sender, RoutedEventArgs e)
{
EditingCommands.CorrectSpellingError.Execute(textBox1.Text, richTextBox1);
spellCheckWords();
}
private void button3_Click(object sender, RoutedEventArgs e)
{
EditingCommands.IgnoreSpellingError.Execute(null, richTextBox1);
spellCheckWords();
}
This will execute the command first and then the spellcheck again.

C# xmldataprovider insert node

I am trying to write an editor for an xml config file. I have the xml file bound to a listview and selecting an element and then clicking edit allows you to edit an element. When the user clicks save, a delegate is called which updates the element within the xml document. I have tried to do the updating in various ways including appendnode, prependnode, insertafter, and replace child. I have gotten them all working without any compile time or runtime errors, yet they don't update the xmldataprovider or the listview. It's probably just something simple that I am missing but I am having trouble figuring out what it is. Can someone please help?
Thanks,
Brian
If it helps, here's the source code of my main form (the one with the listview)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Xml;
using System.IO;
namespace g2config
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public XmlDataProvider dp;
string cfgfile;
public managecmpnts manage_components;
public delegate void managecmpnts(int id, XmlElement component);
public MainWindow()
{
InitializeComponent();
dp = this.FindResource("configfile") as XmlDataProvider;
cfgfile = "C:\\Users\\briansvgs\\Desktop\\g2config\\g2config\\bin\\Debug\\g2config.pxml";
if(Environment.GetCommandLineArgs().Count() > 1)
{
string path = Environment.GetCommandLineArgs().ElementAt(1);
//MessageBox.Show("Path: " + path);
cfgfile = path;
}
if (File.Exists(cfgfile))
{
dp.Source = new Uri(cfgfile, UriKind.RelativeOrAbsolute);
}
else
{
MessageBox.Show("config file not found");
}
this.Title = this.Title + " (" + cfgfile + ") ";
}
public void browsedir( object sender, EventArgs e)
{
System.Windows.Forms.OpenFileDialog ofd = new System.Windows.Forms.OpenFileDialog();
ofd.Filter = "PXML Files (*.pxml)|*.pxml"; ;
//http://www.kirupa.com/net/using_open_file_dialog_pg4.htm
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) //how to browse dirs instead of files?????
{
startpath.Text = ofd.FileName;
}
}
public void newcomponent(object sender, RoutedEventArgs e)
{
componentsettings new_cmpnt = new componentsettings();
new_cmpnt.Title = "Add Component";
new_cmpnt.ShowDialog();
}
public void editcomponent(object sender, RoutedEventArgs e)
{
int selected = components.SelectedIndex;
XmlElement current = (XmlElement)components.Items.CurrentItem;
componentsettings edit_cmpnt = new componentsettings(current);
edit_cmpnt.cmpnt_mgr.manage_components += new manager.mngcmpnts(test);
edit_cmpnt.Title = "Edit Component";
edit_cmpnt.ShowDialog();
}
private void browsedir(object sender, RoutedEventArgs e)
{
System.Windows.Forms.FolderBrowserDialog directory;
directory = new System.Windows.Forms.FolderBrowserDialog();
directory.Description = "Please select the folder containing your g2 configuration (pxml) files. Default is C:\\Program Files\\Exacom, Inc.";
directory.ShowDialog();
startpath.Text = directory.SelectedPath;
}
private void save(object sender, RoutedEventArgs e)
{
MessageBox.Show(dp.Source.ToString());
if(File.Exists(cfgfile + ".old"))
{
File.Delete(cfgfile + ".old");
}
File.Move(cfgfile, cfgfile + ".old");
dp.Document.Save(cfgfile);
}
private void remove(object sender, RoutedEventArgs e)
{
XmlNode component = dp.Document.DocumentElement["Components"].ChildNodes[components.SelectedIndex];
dp.Document.DocumentElement["Components"].RemoveChild(component);
}
private void temp(object sender, RoutedEventArgs e)
{
MessageBox.Show(dp.Document.DocumentElement["Components"].ChildNodes[components.SelectedIndex].InnerText.ToString()); //will this give me trouble????? Probably not since it's my config, but...
}
private void test(int id, XmlElement testelement)
{
//dp.Document.DocumentElement["Components"].ChildNodes[id] = testelement;
//XmlNode testnode = dp.Document.DocumentElement["Components"].ChildNodes[id + 1];
//XmlNode testnode = testelement;
MessageBox.Show(testelement.OuterXml.ToString());
//dp.Document.DocumentElement["Components"].InsertAfter(dp.Document.DocumentElement["Components"].ChildNodes[0], testelement);
//dp.Document.DocumentElement["Components"].RemoveChild(testelement);
//dp.Document.DocumentElement["Components"].ReplaceChild(testnode, dp.Document.DocumentElement["Components"].ChildNodes[id]);
dp.Document.DocumentElement["Components"].PrependChild(testelement);
//dp.Document.NodeChanged();
//dp.Document.DocumentElement["Components"].CloneNode(true);
dp.Document.DocumentElement["Components"].AppendChild(testelement);
//dp.Document.
//MessageBox.Show(dp.Document.OuterXml.ToString());
//MessageBox.Show(dp.Document.DocumentElement["Components"].FirstChild.AppendChild(testelement).OuterXml.ToString());
}
}
}
JMSA. Thank you for the suggestion. I was playing around with it today and I found a solution. It's a little messy, but it works. I copy one of the present nodes and then clear all of the values. Here is the code if anyone is interested.
private void add(object sender, RoutedEventArgs e)
{
System.Xml.XmlNode test = configfile.Document.DocumentElement["Components"].FirstChild;
System.Xml.XmlNode testclone = test.Clone();
for (int i = 0; i < testclone.ChildNodes.Count; i++)
{
testclone.ChildNodes[i].RemoveAll();
}
configfile.Document.DocumentElement["Components"].AppendChild(testclone);
components.SelectedItem = components.Items.Count + 1;
}
My suggestion would be to create a class for each element by using a composite pattern, lazy loading the XML doc in the class and perform operations as you need. Then again save it. Quite a handful though.

Categories