I'll try my best to explain my situation.
I'm developing a software using C#, which allows multiple users to edit the same file under a common directory at the same time and see the changes others make too.
So I used FileSystemWatcher to monitor the changes in the file (to update others' changes) and textchanged of the textbox (to save changes to the file so others' screen would be updated too).
It's working if I input characters (both events are fired once)
It's not working if I try to delete characters in any form (backspace, delete, etc) It won't delete any character and the cursor always gets reset to position 0. I used box.SelectionStart to move the cursor and it's working when I input characters.
I put a counter kinda thing to check and I found that when I tried to delete characters, both events are fired twice.
I tried to search but I got mixed answers...
Thanks in advance
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;`enter code here`
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.Windows.Forms;
using System.IO;
using System.Windows.Threading;
namespace SharedFileEditor
{
public partial class EditorView : Window
{
private EditorModel model;
private FileSystemWatcher watcher;
private string path;
private int count = 0;
private int count2 = 0;
public EditorView()
{
InitializeComponent();
model = new EditorModel();
this.DataContext = model;
}
private void OpenClicked(object sender, RoutedEventArgs e)
{
using (OpenFileDialog dialog = new OpenFileDialog())
{
dialog.Filter = "Text files (*.txt)|*.txt";
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
watcher = new FileSystemWatcher(System.IO.Path.GetDirectoryName(dialog.FileName), "*.txt");
Console.WriteLine(System.IO.Path.GetDirectoryName(dialog.FileName));
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEve`enter code here`nts = true;
path = dialog.FileName;
HandleOpen(dialog.FileName);
}
}
}
internal void HandleOpen(string path)
{
FileStream f = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
StreamReader reader = new StreamReader(f);
model.Content = reader.ReadToEnd();
reader.Close();
}
private void OnChanged(object source, FileSystemEventArgs e)
{
if (this.Box.Dispatcher.CheckAccess())
{
try
{
FileStream f = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
StreamReader reader = new StreamReader(f);
model.Content = reader.ReadToEnd();
this.Box.CaretIndex = model.Cursor;
reader.Close();
Console.WriteLine("read:" + count2++);
}
catch (IOException x)
{
Console.WriteLine(x.Message);
}
}
else
{
this.Box.Dispatcher.Invoke(
new updateContent(OnChanged), source, e);
}
}
private void ContentChanged(object sender, TextChangedEventArgs e)
{
FileStream f = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
StreamWriter writer = new StreamWriter(f);
writer.Write(this.Box.Text);
model.Cursor = this.Box.SelectionStart;
model.Content = this.Box.Text;
writer.Close();
Console.WriteLine("write:"+count++);
}
public delegate void updateContent(object source, FileSystemEventArgs e);
}
}
I figured it out... it has something to do with how my application works. Solved by setting the EnableRaisingEvents flag to false and back to true later.
I think the actual answer to your question is contained in this post:
FileSystemWatcher Changed event is raised twice
To summarize, this is a bug in the FileSystemWatcher class. The save process often occurs in batches. Your FileSystemWatcher class picks them all up.
Related
I made a application with iTextSharp to put numbers on a PDF file.
As you will see in the following code, my application just can do this, only if the file is in a specific directory.
So I made an "Other" button where the user can select a file.
What I want to do now is, that the chosen file will download the converted PDF.
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace NummerierePDF
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
byte[] bytes = File.ReadAllBytes(#"L:\Users\user\Documents\PDFnummerieren\PDF.pdf");
iTextSharp.text.Font blackFont = FontFactory.GetFont("Arial", 12, iTextSharp.text.Font.NORMAL, BaseColor.BLACK);
using (MemoryStream stream = new MemoryStream())
{
PdfReader reader = new PdfReader(bytes);
using (PdfStamper stamper = new PdfStamper(reader, stream))
{
int pages = reader.NumberOfPages;
for (int i = 1; i <= pages; i++)
{
ColumnText.ShowTextAligned(stamper.GetOverContent(i), Element.ALIGN_RIGHT, new Phrase(i.ToString(), blackFont), 568f, 15f, 0);
}
}
bytes = stream.ToArray();
}
File.WriteAllBytes(#"L:\Users\user\Documents\PDFnummerieren\PDF1.pdf", bytes);
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button2_Click(object sender, EventArgs e)
{
this.Close();
}
private void button3_Click(object sender, EventArgs e)
{
var FD = new System.Windows.Forms.OpenFileDialog();
if (FD.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string fileToOpen = FD.FileName;
System.IO.FileInfo File = new System.IO.FileInfo(FD.FileName);
System.IO.StreamReader reader = new System.IO.StreamReader(fileToOpen);
}
}
}
}
So File.WriteAllBytes(#"L:\Users\user\Documents\PDFnummerieren\PDF1.pdf", bytes);
can stay because it doesn't matter where the file will be downloaded after it's converted.
But File.ReadAllBytes(#"L:\Users\user\Documents\PDFnummerieren\PDF.pdf");
shouldn't be a specific directory, it should get the chosen file from button3.
As you probably noticed I'm new into programming so I thought maybe I could do this: File.ReadAllBytes(fileToOpen);
to get the string. Though that doesn't do his job.
Thanks for your time.
If you want to use a variable between methods of the same class then you need to declare a private and non-static class level variable (called also Instance Field). This is visible to every method of the class.
public partial class Form1 : Form
{
// Here declare a variable visible to all methods inside the class but
// not outside the class. Init it with an empty string
private string theFile = "";
private void button1_Click(object sender, EventArgs e)
{
// When you click the button1 check if the variable
// has been set to something in the button3 click event handler
if(string.IsNullOrEmpty(theFile) || !File.Exists(theFile))
return;
// Now you can use it to load the file and continue with the code
// you have already written
byte[] bytes = File.ReadAllBytes(theFile);
......
......
}
private void button3_Click(object sender, EventArgs e)
{
// The only job of button3 is to select the file to work on
// as you can see we can use the variable also here
var FD = new System.Windows.Forms.OpenFileDialog();
if (FD.ShowDialog() == System.Windows.Forms.DialogResult.OK)
theFile = FD.FileName;
}
}
But do you really need a separate button for this? I mean you could put the three lines of code in the button3 directly in at the start of the code in button1 and remove the superfluos (at this point) button3
I suggest you to read some docs on variable's Scope and Lifetime
Try this
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;
namespace NummerierePDF
{
public partial class Form1 : Form
{
string fileToOpen = string.Empty;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if(string.IsNullOrWhiteSpace(fileToOpen)) return;
byte[] bytes = File.ReadAllBytes(fileToOpen);
iTextSharp.text.Font blackFont = FontFactory.GetFont("Arial", 12, iTextSharp.text.Font.NORMAL, BaseColor.BLACK);
using (MemoryStream stream = new MemoryStream())
{
PdfReader reader = new PdfReader(bytes);
using (PdfStamper stamper = new PdfStamper(reader, stream))
{
int pages = reader.NumberOfPages;
for (int i = 1; i <= pages; i++)
{
ColumnText.ShowTextAligned(stamper.GetOverContent(i), Element.ALIGN_RIGHT, new Phrase(i.ToString(), blackFont), 568f, 15f, 0);
}
}
bytes = stream.ToArray();
}
File.WriteAllBytes(#"L:\Users\user\Documents\PDFnummerieren\PDF1.pdf", bytes);
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button2_Click(object sender, EventArgs e)
{
this.Close();
}
private void button3_Click(object sender, EventArgs e)
{
var FD = new System.Windows.Forms.OpenFileDialog();
if (FD.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
fileToOpen = FD.FileName;
System.IO.FileInfo File = new System.IO.FileInfo(FD.FileName);
System.IO.StreamReader reader = new System.IO.StreamReader(fileToOpen);
}
}
}
}
I have put your variable to class scope and now you can access it from anywhere inside your class
I am trying to adapt the following code so that the functionality is on a click event ...
Here is the code as is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string IN_FILENAME = #"c:\temp\testin.csv";
const string OUT_FILENAME = #"c:\temp\testout.csv";
static void Main(string[] args)
{
StreamReader reader = new StreamReader(IN_FILENAME);
StreamWriter writer = new StreamWriter(OUT_FILENAME);
string inputLine = "";
while ((inputLine = reader.ReadLine()) != null)
{
List<string> inputArray = inputLine.Split(new char[] { ',' }).ToList();
inputArray.Add(inputArray[3]);
writer.WriteLine(string.Join(",", inputArray));
}
reader.Close();
writer.Flush();
writer.Close();
}
}
}
Then I need to add the functionality to a click even so this is where I am:
private void button1_Click(object sender, EventArgs e)
{
const string IN_FILENAME = #"c:\temp\testin.csv";
const string OUT_FILENAME = #"c:\temp\testout.csv";
StreamReader reader = new StreamReader(IN_FILENAME);
StreamWriter writer = new StreamWriter(OUT_FILENAME);
}
and I can't do anymore because it's telling me StreamReader cannot be found.
Can anyone help me adapt this code to the click event?
The StreamReader is a class define in the System.IO namespace. Using this namespace at the start of your file, you would resolve it.
using System.IO;
I'm having some trouble, somehow I get a "not allowed exception" and I can't seem to figure out what is wrong I know the answer is simple but I really can't spot what's wrong with my code...
it happens when I try to write something to the newly created file and I could imagine that it also will when it tries to read the file... any information would be acknowledged...
thx in advance
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; // ny
using System.Net;
using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Tasks;
using Microsoft.Phone.Shell;
using System.IO; // ny
using System.IO.IsolatedStorage; // ny
namespace SmartSence
{
public partial class MainPage : PhoneApplicationPage
{
private static UTF8Encoding enc = new UTF8Encoding();
// Constructor
public MainPage()
{
InitializeComponent();
if (CreateStore().FileExists("Userdata\\Userdata.txt"))
{
mail.Text = ReadFile(CreateStore());
}
}
private void minknap_Click(object sender, RoutedEventArgs e)
{
string newusername;
newusername = Username.Text;
mytext1.Text = newusername;
string igen;
igen = "&password=";
string newpassword;
newpassword = mypassword.Password;
mypasswordblock.Text = newpassword;
WebBrowserTask webBrowserTask = new WebBrowserTask();
webBrowserTask.Uri = new Uri("http://www.smartsence.dk/winindexcheck.php?id=" + mytext1.Text + igen + mypasswordblock.Text, UriKind.Absolute);
webBrowserTask.Show();
}
private void signin_Click(object sender, RoutedEventArgs e)
{
string signin;
signin = mail.Text;
signintext.Text = signin;
string igen;
igen = "&password=";
string newpassword;
newpassword = mail.Text;
signintext.Text = newpassword;
if (!CreateStore().DirectoryExists("Userdata"))
{
CreateStore().CreateDirectory("Userdata");
if (!CreateStore().FileExists("Userdata\\data.txt"))
{
CreateStore().CreateFile("Userdata\\data.txt");
}
IsolatedStorageFileStream stream = new IsolatedStorageFileStream("Userdata\\Userdata.txt", FileMode.Create, CreateStore());
WriteToFile(CreateStore(), signin);
}
WebBrowserTask webBrowserTask = new WebBrowserTask();
webBrowserTask.Uri = new Uri("http://www.smartsence.dk/winindexcheck.php?id=" + signintext.Text + igen + signintext.Text, UriKind.Absolute);
webBrowserTask.Show();
}
private void minknap2_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/Info.xaml", UriKind.Relative));
}
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
{
}
private void info_Click(object sender, EventArgs e)
{
MessageBox.Show("SmartSence App 4.0 - Is free to use, it´s has never been easier to navigate around the web. Buy the app and get free VIP status forever - Please support us :) ");
}
private void VIP_Click(object sender, EventArgs e)
{
WebBrowserTask webBrowserTask = new WebBrowserTask();
webBrowserTask.Uri = new Uri("http://www.smartsence.dk/winindexappvipcheck2.php", UriKind.Absolute);
webBrowserTask.Show();
}
private void image1_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
}
#region NewMethods
private IsolatedStorageFile CreateStore()
{
IsolatedStorageFile lager = IsolatedStorageFile.GetUserStoreForApplication();
return lager;
}
private void WriteToFile(IsolatedStorageFile storeFile, string content)
{
IsolatedStorageFileStream fileStream = storeFile.OpenFile("Userdata\\Userdata.txt", FileMode.Open, FileAccess.Write); // Exception: not allowed on IsolatedStorageFile
using (StreamWriter writer = new StreamWriter(fileStream))
{
writer.WriteLine(content);
}
}
private string ReadFile(IsolatedStorageFile storeFile)
{
IsolatedStorageFileStream fileStream = storeFile.OpenFile("Userdata\\Userdata.txt", FileMode.Open, FileAccess.Read);
using (StreamReader reader = new StreamReader(fileStream))
{
return reader.ReadLine().Trim();
}
}
private void mail_Tap(object sender, GestureEventArgs e)
{
mail.Text = string.Empty;
}
#endregion
}
}
I think the issue is that you're not closing your IsolatedStorageFileStream, so the file is still open, and hence cannot be written, therefore throwing this exception. You should make sure to wrap your reads & writes in a using, or calling Dispose yourself.
public MainPage()
{
InitializeComponent();
if (CreateStore().FileExists("Userdata\\Userdata.txt"))
{
mail.Text = ReadFile(CreateStore()); //opens file and never closes it.
}
}
To fix this, you should use using statements.
private void WriteToFile(IsolatedStorageFile storeFile, string content)
{
using (IsolatedStorageFileStream fileStream = storeFile.OpenFile("Userdata\\Userdata.txt", FileMode.Open, FileAccess.Write){ // Exception: not allowed on IsolatedStorageFile
using (StreamWriter writer = new StreamWriter(fileStream))
{
writer.WriteLine(content);
}
}
}
private string ReadFile(IsolatedStorageFile storeFile)
{
using(IsolatedStorageFileStream fileStream = storeFile.OpenFile("Userdata\\Userdata.txt", FileMode.Open, FileAccess.Read){
using (StreamReader reader = new StreamReader(fileStream))
{
return reader.ReadLine().Trim();
}
}
}
I've been trying to get this program to work, but when I try to run it, nothing happens. I attempted debugging, and it told me that I had a TypeInitializationException, so I looked online for a fix, but I wasn't able to find anything that could help me. Here's my code; the program is a GUI, where the computer randomly selects one of three letters (A, B, or C) ten times, and the user tries to guess which letter was selected. The high schore is saved to a file, and is read from that file and displayed.
Any help at all would be greatly appreciated.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace HighScore
{
public partial class Form1 : Form
{
const string FILENAME = #"C:\\Guess\\Data.txt";
static FileStream outFile = new FileStream(FILENAME, FileMode.Create, FileAccess.Write);
StreamWriter writer = new StreamWriter(outFile);
static FileStream file = new FileStream(FILENAME, FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(file);
string answer;
string input;
int writenum;
string num;
public Form1()
{
InitializeComponent();
}
private void label1_Click(object sender, EventArgs e)
{
}
private void label2_Click(object sender, EventArgs e)
{
}
private void label3_Click(object sender, EventArgs e)
{
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
Random ranNumberGenerator = new Random();
int randomNumber;
int x;
num = reader.ReadLine();
writenum = Convert.ToInt32(num);
label1.Text = "Score: " + writenum;
label1.Visible = true;
randomNumber = ranNumberGenerator.Next(1, 3);
if (randomNumber == 1)
{
answer = "a";
}
if (randomNumber == 2)
{
answer = "b";
}
if (randomNumber == 3)
{
answer = "c";
}
if (textBox1.Text == "a")
{
input = "a";
}
if (textBox1.Text == "b")
{
input = "b";
}
if (textBox1.Text == "c")
{
input = "c";
}
if (answer == input)
{
label2.Text = "Correct! Computer guessed " + answer + " and you guessed " + input;
label2.Visible = true;
num = reader.ReadLine();
writenum = Convert.ToInt32(num);
writenum = writenum + 1;
num = writenum.ToString();
}
for (x = 0; x < 10; ++x)
{
button1.Enabled = false;
}
}
}
}
Given you are getting a TypeInitializationException, it implies the error is occurring either inside a static constructor (which this class doesn't have), or during the initialization of static fields (which it does).
In your code above, the two static fields are both FileStream, so the exception must be happening during initialization of these; before any of your other code runs.
I can't help noticing you are creating a stream for reading and a stream for writing, both pointing to the same file - is this intentional?
The reason for the exception is that you open a file for writing - by default this won't share with another file stream. You then open a second file stream for reading, on the same file - which is throwing an exception. If it really is your intention to read and write from/to the same file, you'll need to use other constructor overloads that let you specify the sharing semantics:
FileStream outFile = new FileStream(FILENAME, FileMode.Create, FileAccess.Write, FileShare.Read);
FileStream file = new FileStream(FILENAME, FileMode.Open, FileAccess.Read, FileShare.Write);
Generally speaking though - I'd avoid having these as static fields. Make them instance properties, initialize them in the constructor. It'll be far easier to do some barrier checking, and to debug when it goes wrong.
As a note: TypeInitializationException is "special"; once you've had this exception once and the CLR has failed to initialize the type, you will always get it every time you try and access the type - it is a "fail once, fail always" situation.
This is caused by a static constructor. Look at static field initialization too.
The code you posted does not indicate any such construct. Look elsewhere in your project.
I'm a real noob to C# trying to write a small XML replacer program based on a short code a friend of mine used in one of his apps..
I'm having trouble with this line:
StreamReader sr = new StreamReader(textBox1.Text);
I get an error: "Empty path name is not legal."
why doesn't this work?
the code is:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace ReplaceMe
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
StreamReader sr = new StreamReader(textBox1.Text);
StreamWriter sw = new StreamWriter(textBox1.Text.Replace(".", "_new."));
string cur = "";
do
{
cur = sr.ReadLine();
cur = cur.Replace(textBox2.Text, textBox3.Text);
sw.WriteLine(cur);
}
while (!sr.EndOfStream);
sw.Close();
sr.Close();
MessageBox.Show("Finished, the new file is in the same directory as the old one");
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
this.textBox1.Text = openFileDialog1.FileName;
}
}
}
}
Thanks in advance :)
That is because your textBox1 doesn't contain text at the moment you create StreamReader. You set textBox1 text in button1_Click, so you have to create StreamReader in that method.
You should make sure the file exists before you try to access it, it seems you deliver an empty string as a filename.
try accessing the file only when:
if (File.Exists(textBox1.Text))
{
//Your Code...
}
the value of textbox1 is null or empty. also, if you want to manipulate xml look into the objects of the System.Xml namespace. These objects are designed specifically for working with XML.
it's because you're setting an empty string in StreamReader constructor.
I recommend you do a simple validation before read file.
as this:
string fileName = textBox1.Text;
if(String.IsNullOrEmpty(fileName)) {
//textbox is empty
} else if(File.Exists(fileName)) {
//file not exists
} else {
// read it
StreamReader sr = new StreamReader(fileName);
//..
}
Note: it is not right way to manipulate xml files.
check out the XML documentation for more info.