How to track a specific file changes using MVVM pattern in .net? - c#

I am working on a .NET WPF project using Visual Studio 2022 and I added tray icon functionality to my app. I am managing tray icon part in mainwindow.xaml.cs but i need to track file changes in my mainviewmodel class as well. How can i pass this event to mvvm or maybe pass data to mainviewmodel ? Any help would be appreciated.
public void CreateFileWatcher(string path)
{
watcher = new FileSystemWatcher();
watcher.Path = path;
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = nameof(AppStateModel);
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
Thread.Sleep(1000);
var appState = CheckAppState();
AppState = appState;
string workingDirectory = Environment.CurrentDirectory;
string projectDirectory = Directory.GetParent(workingDirectory).Parent.FullName;
var path = Path.Combine(projectDirectory, "Assets");
switch (appState)
{
case 1:
m_notifyIcon.Icon = new System.Drawing.Icon(Path.Combine(path, "48x48_active.ico"));
break;
case 2:
m_notifyIcon.Icon = new System.Drawing.Icon(Path.Combine(path, "48x48_inactive.ico"));
break;
case 4:
m_notifyIcon.Icon = new System.Drawing.Icon(Path.Combine(path, "48x48_running.ico"));
break;
default:
break;
}
}
private int CheckAppState()
{
try
{
var tempFile = Path.Combine(AppWizard.Default.FolderPath, nameof(AppStateModel));
if (!File.Exists(tempFile))
return -1;
var _path = tempFile;
using (var stream = new FileStream
(_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
var appStateModel = JsonConvert.DeserializeObject<AppStateModel>(File.ReadAllText(tempFile));
if (appStateModel != null)
{
if (appStateModel.CurrentState == AppStates.Ready)
return 1;
else if (appStateModel.CurrentState == AppStates.Dead || appStateModel.CurrentState == AppStates.Expired)
return 2;
else if (appStateModel.CurrentState == AppStates.Busy)
return 4;
}
}
}
catch (Exception ex)
{
FileLogger.Default.WriteExceptionLog(ex);
}
return -1;
}

A watcher service may look as following:
public class FileWatcherService
{
FileSystemWatcher watcher;
public FileWatcherService()
{
CreateFileWatcher();
}
public event EventHandler<FileSystemEventArgs> FileChanged;
public void CreateFileWatcher()
{
watcher = new FileSystemWatcher(#"C:\Temp");
watcher.NotifyFilter = NotifyFilters.Attributes
| NotifyFilters.CreationTime
| NotifyFilters.DirectoryName
| NotifyFilters.FileName
| NotifyFilters.LastAccess
| NotifyFilters.LastWrite
| NotifyFilters.Security
| NotifyFilters.Size;
watcher.Changed += MakeANote;
watcher.Created += MakeANote;
watcher.Deleted += MakeANote;
watcher.Renamed += MakeANote;
watcher.Filter = "*.*";
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
}
private void MakeANote(object sender, FileSystemEventArgs e)
{
FileChanged?.Invoke(this, e);
}
}
A simple way is to put the "watcher" in the View Model :
public class MainViewModel :Binding
{
FileWatcherService _fileWatcherService = new FileWatcherService();
public MainViewModel()
{
_fileWatcherService.FileChanged += OnFileChanged;
}
private void OnFileChanged(object sender, FileSystemEventArgs e)
{
LastAction = e.ChangeType.ToString();
File = e.Name;
}
string _lastAction;
public string LastAction
{
get { return _lastAction; }
set { _lastAction = value;NotifyPropertyChanged(nameof(LastAction)); }
}
string _file;
public string File
{
get { return _file; }
set { _file = value; NotifyPropertyChanged(nameof(File)); }
}
}
And the sample XAML code will be as following:
<Grid>
<Grid.DataContext>
<local:MainViewModel/>
</Grid.DataContext>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding File}"/>
<TextBlock Text=" has "/>
<TextBlock Text="{Binding LastAction}"/>
</StackPanel>
</Grid>

Related

FileSystemWatcher not detecting folder creation

I am creating an application that will watch for file changes in a directory although while files with an extension are detected, folders are not.
using System;
using System.IO;
using System.Diagnostics;
namespace fileSystemWatcherTest
{
class myClass
{
static void Main()
{
using var watcher = new FileSystemWatcher(#"C:\Users\Username\Desktop\Watching folder");
watcher.NotifyFilter = NotifyFilters.Attributes
| NotifyFilters.CreationTime
| NotifyFilters.FileName
| NotifyFilters.LastWrite;
watcher.Changed += onChanged;
watcher.Created += onCreated;
watcher.Error += onError;
watcher.Filter = "";
watcher.IncludeSubdirectories = true;
watcher.EnableRaisingEvents = true;
Console.WriteLine("Press enter to exit.");
Console.ReadLine();
}
private static void onChanged(object sender, FileSystemEventArgs e)
{
if (e.ChangeType != WatcherChangeTypes.Changed)
{
return;
}
Console.WriteLine($"Changed: {e.FullPath}");
string fileExtension = Path.GetExtension(e.FullPath);
if (fileExtension != string.Empty)
{
Console.WriteLine(fileExtension);
}
}
private static void onCreated(object sender, FileSystemEventArgs e)
{
Console.WriteLine($"Created: {e.FullPath}");
}
private static void onError(object sender, ErrorEventArgs e)
{
printException(e.GetException());
}
private static void printException(Exception ex)
{
if (ex != null)
{
Console.WriteLine("ERROR");
Console.WriteLine($"Message: {ex.Message}");
Console.WriteLine($"StackTrace:");
Console.WriteLine(ex.StackTrace);
Console.WriteLine("");
printException(ex.InnerException);
}
}
}
}
According to the documentation and the intellisense directories should be detected.
I believe that the problem is with the Filter property but i can't get it to work so i would appreciate some help.
To detect directories as well, you need to add NotifyFilters.DirectoryName to watcher.NotifyFilter:
watcher.NotifyFilter = NotifyFilters.Attributes
| NotifyFilters.CreationTime
| NotifyFilters.DirectoryName
| NotifyFilters.FileName
| NotifyFilters.LastWrite;
Checkout the documentation of NotifyFilters.

How can get value of "FileSystemWatcher" in other loop function

I have class "SystemWatchFile" used to detect a file change . Every a file changed so i can capture this change in function "OnChanged".
In other loop function i call to SystemWatchFile to monitor the change of file. Like below :
while (true)
{
SystemWatchFile watchfile = new SystemWatchFile(#" f:\Tutorial\C#\FileSytemWatcher\readme.txt");
watchfile.Run();
///at here , how can i get value from "OnChanged" , when a file is changed
}
I try use to delegate to implement callback but not work.My class :
public class SystemWatchFile
{
string FileNeedWatching = "";
bool let = false;
public SystemWatchFile(string file)
{
FileNeedWatching = file;
}
public void Run()
{
using (FileSystemWatcher watcher = new FileSystemWatcher())
{
string filePath1 = FileNeedWatching;
watcher.Path = Path.GetDirectoryName(filePath1);
// Watch for changes in LastAccess and LastWrite times, and
// the renaming of files or directories.
watcher.NotifyFilter = NotifyFilters.LastAccess
| NotifyFilters.FileName
| NotifyFilters.DirectoryName;
// Only watch text files.
watcher.Filter = Path.GetFileName(filePath1);
// Add event handlers.
watcher.Changed += OnChanged;
// Begin watching.
watcher.EnableRaisingEvents = true;
}
}
private void OnChanged(object source, FileSystemEventArgs e)
{
if (let == false)
{
Console.WriteLine($"File: {e.FullPath} {e.ChangeType}");
let = true;
}
else
{
let = false;
}
}
}
How can i get value when file change ?

Read file when updated. Error The process cannot access the file because it is being used by another process

I'm want to read the last line of a CSV file when updated. When i save the updates the app brakes with this error "The process cannot access the file '' because it is being used by another process." My question is, the "another process" is the watcher? If it's the watcher how can i read the file when updated?
public partial class Auto_Window : Form
{
FileSystemWatcher watcher = new FileSystemWatcher();
private int i = 0;
public Auto_Window()
{
InitializeComponent();
watcher.Path = ConfigurationManager.AppSettings["CSVFolder"];
watcher.NotifyFilter = NotifyFilters.LastWrite;
watcher.Filter = ConfigurationManager.AppSettings["CSVFilter"];
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.EnableRaisingEvents = true;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
i++;
var data = File.ReadAllLines(e.FullPath);
string last = data[data.Length - 1];
if (i == 1)
{
tb_art1.Invoke(new Action(() => tb_art1.Text = last));
}
if (i == 2)
{
tb_art2.Invoke(new Action(() => tb_art2.Text = "ART2"));
}
if (i == 3)
{
tb_art3.Invoke(new Action(() => tb_art3.Text = "ARTO3"));
MessageBox.Show("com?");
tb_art1.Invoke(new Action(() => tb_art1.Text = ""));
tb_art2.Invoke(new Action(() => tb_art2.Text = ""));
tb_art3.Invoke(new Action(() => tb_art3.Text = ""));
i = 0;
}
}
private void btn_auto_Click(object sender, EventArgs e)
{
this.Close();
}
private void btn_conf_Click(object sender, EventArgs e)
{
if ((Control.ModifierKeys & Keys.Shift) != 0 && (Control.ModifierKeys & Keys.Control) != 0)
{
Config cfgform = new Config();
cfgform.ShowDialog();
watcher.Path = ConfigurationManager.AppSettings["CSVFolder"];
watcher.Filter = ConfigurationManager.AppSettings["CSVFilter"];
}
}
private void btn_clear_Click(object sender, EventArgs e)
{
tb_art1.Text = "";
tb_art2.Text = "";
tb_art3.Text = "";
i = 0;
}
}
Update: Code updated
I solved the problem adding watcher.EnableRaisingEvents = false; at the start of the event and watcher.EnableRaisingEvents = true; at the end to prevent duplicated modifications ins seconds.
The other problem was the way i was reading the file. I solved in this way:
var fs = new FileStream(e.FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var reader = new StreamReader(fs);
await Task.Delay(500);
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
searchList.Add(line);
}

How to send custom commands to a service c#

I'm using a windows service and a windows form. I have been trying to send custom commands to a service in order to create an isolated storage file. However when I click my "btnSubmit" no file is created. For some reason it doesn't seem to execute the command
Code in Form
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Project2Service;
namespace Project2
{
public partial class Form1 : Form
{
public Service1 s = new Service1();
public ServiceInstaller si = new ServiceInstaller();
public ProjectInstaller pi = new ProjectInstaller();
public ServiceController sc = new ServiceController("Project2Service");
private string[] isoType;
string machineName = System.Windows.Forms.SystemInformation.ComputerName;
public Form1()
{
InitializeComponent();
isoType = new string[] { "User", "Assembly And Domain"};
cboIsoType.Items.AddRange(isoType);
cboIsoType.SelectedIndex = 0;
btnContinue.Enabled = false;
btnPause.Enabled = false;
btnStop.Enabled = false;
}
public void Labels()
{
lblMachine.Text = machineName;
lblSName.Text = s.ServiceName;
lblSType.Text = si.StartType.ToString();
lblSStatus.Text = sc.Status.ToString();
lblPause.Text = sc.CanPauseAndContinue.ToString();
lblShutdown.Text = sc.CanShutdown.ToString();
lblStop.Text = sc.CanStop.ToString();
}
private void btnStart_Click(object sender, EventArgs e)
{
//Controller.Refresh(); //Gets the current status of service
//if (Controller.Status == ServiceControllerStatus.Stopped)
//{
// Controller.Start();
//}
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running);
Labels();
btnStart.Enabled = false;
btnContinue.Enabled = false;
btnStop.Enabled = true;
btnPause.Enabled = true;
}
private void btnStop_Click(object sender, EventArgs e)
{
sc.Stop();
sc.WaitForStatus(ServiceControllerStatus.Stopped);
Labels();
btnStart.Enabled = true;
btnContinue.Enabled = false;
btnPause.Enabled = false;
btnStop.Enabled = false;
}
private void btnPause_Click(object sender, EventArgs e)
{
sc.Pause();
sc.WaitForStatus(ServiceControllerStatus.Paused);
Labels();
btnPause.Enabled = false;
btnContinue.Enabled = true;
btnStart.Enabled = false;
btnStop.Enabled = true;
}
private void btnContinue_Click(object sender, EventArgs e)
{
sc.Continue();
sc.WaitForStatus(ServiceControllerStatus.Running);
Labels();
btnStop.Enabled = true;
btnStart.Enabled = false;
btnPause.Enabled = true;
btnContinue.Enabled = false;
}
private void btnSubmit_Click(object sender, EventArgs e)
{
if (cboIsoType.SelectedItem.ToString() == "User")
{
sc.ExecuteCommand(128);
}
}
}
}
My Service Controller
public enum ServiceCustomCommands { Command1 = 128, Command2 = 129 };
//private IsolatedStorageScope iso;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
//iso = IsolatedStorageScope.User | IsolatedStorageScope.Domain;
FileSystemWatcher Watcher = new FileSystemWatcher(#"C:\Users\Martin\Desktop\Project2\ServiceTest");
Watcher.EnableRaisingEvents = true;
Watcher.NotifyFilter = NotifyFilters.LastAccess
| NotifyFilters.LastWrite
| NotifyFilters.FileName
| NotifyFilters.DirectoryName;
Watcher.Changed += new FileSystemEventHandler(Watcher_Changed);
Watcher.Created += new FileSystemEventHandler(Watcher_Created);
Watcher.Deleted += new FileSystemEventHandler(Watcher_Deleted);
Watcher.Renamed += new RenamedEventHandler(Watcher_Renamed);
WriteServiceInfo("Service Started!");
}
// This event is raised when a file is changed
private void Watcher_Changed(object sender, FileSystemEventArgs e)
{
WriteServiceInfo("File Changed!");
DirectoryInfo d = new DirectoryInfo(#"C:\Users\Martin\Desktop\Project2\ServiceTest");//Assuming Watch is your Folder
FileInfo[] Files = d.GetFiles("*.txt"); //Getting Text files
string str = "";
foreach (FileInfo file in Files)
{
str = str + ", " + file.Name;
str = str + ", " + file.LastWriteTime;
str = str + ", " + file.CreationTime;
str = str + ", " + file.Length;
WriteServiceInfo(file.Name);
WriteServiceInfo(file.LastWriteTime.ToString());
WriteServiceInfo(file.CreationTime.ToString());
WriteServiceInfo(file.Length.ToString());
}
}
private void Watcher_Created(object sender, FileSystemEventArgs e)
{
WriteServiceInfo("File Created!");
}
private void Watcher_Deleted(object sender, FileSystemEventArgs e)
{
WriteServiceInfo("File Deleted!");
}
private void Watcher_Renamed(object sender, FileSystemEventArgs e)
{
WriteServiceInfo("File Renamed!");
}
private void WriteServiceInfo(string info)
{
FileStream fs = new FileStream(#"C:\Users\Martin\Desktop\Project2\WindowsServiceLog.txt",
FileMode.OpenOrCreate, FileAccess.Write);
StreamWriter m_streamWriter = new StreamWriter(fs);
m_streamWriter.BaseStream.Seek(0, SeekOrigin.End);
m_streamWriter.WriteLine(info + "\n");
m_streamWriter.Flush();
m_streamWriter.Close();
}
protected override void OnCustomCommand(int command)
{
switch ((ServiceCustomCommands)command)
{
case ServiceCustomCommands.Command1:
//Command1 Implementation
IsolatedStorageFile isoFile = IsolatedStorageFile.GetStore(IsolatedStorageScope.User | IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly, null, null);
IsolatedStorageFileStream isoStream = new IsolatedStorageFileStream("LaptopInfo.txt", FileMode.Append, FileAccess.Write, isoFile);
using (StreamWriter writer = new StreamWriter(isoStream))
{
writer.WriteLine("Data");
}
//iso = IsolatedStorageScope.User;
break;
case ServiceCustomCommands.Command2:
//iso = IsolatedStorageScope.User | IsolatedStorageScope.Assembly;
break;
default:
break;
}
}
I think you need to add ServieControllerPermission:
ServiceController sc = new ServiceController("YOURServiceName", Environment.MachineName);
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "YOURServiceName");//this will grant permission to access the Service
scp.Assert();
sc.Refresh();
sc.ExecuteCommand((int)YourMethods.methodX);

FileSystemWatcher: multiple type

I want to send a file which has been found when the System Watcher has discoverd a created file ending with ".txt", ".doc" or ".docx". My Problem is that the System Watcher discover just files ending with ".txt".
Here is my Code:
private String Attachmenttosend
{
get { return attachmentfile; }
set { attachmentfile = value; }
}
private void NewFileSystemWatcher()
{
String filter = "*.txt,*.doc,*.docx";
string[] FileExtension = filter.Split(',');
for (int i = 0; i < FileExtension.GetLength(0); i++)
{
watcher = new FileSystemWatcher(folder); // on its own Thread
watcher.Created += new FileSystemEventHandler(NewEMail);
attachmenttosend.Add(Attachmenttosend);
watcher.Filter = FileExtension[i];
watcher.EnableRaisingEvents = true;
watchlist.Add(watcher);
}
Send(Attachmenttosend);
}
private void NewEMail(Object source, FileSystemEventArgs e)
{
while (Locked(e.FullPath)) // check if the file is used
{
Thread.Sleep(10);
}
Attachmenttosend = e.FullPath; // store the filename
}
I think this will help you,just create a console app on the fly and paste this in and try it out:
private static string[] filters = new string[] { ".txt", ".doc", ".docx" };
static void Main(string[] args)
{
FileSystemWatcher watcher = new FileSystemWatcher();
watcher.Path = #"C:\...\...\...";//your directory here
/* Watch for changes in LastAccess and LastWrite times, and
the renaming of files or directories. */
watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
| NotifyFilters.FileName | NotifyFilters.DirectoryName;
//dont set this
//watcher.Filter = "*.txt";
watcher.Changed += new FileSystemEventHandler(OnChanged);
watcher.Created += new FileSystemEventHandler(OnChanged);
watcher.Deleted += new FileSystemEventHandler(OnChanged);
watcher.Renamed += new RenamedEventHandler(OnRenamed);
watcher.EnableRaisingEvents = true;
Console.ReadKey();
}
private static void OnChanged(object source, FileSystemEventArgs e)
{
if(filters.Contains(Path.GetExtension(e.FullPath)))
{
Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
//Attachmenttosend = e.FullPath;
}
}
private static void OnRenamed(object source, RenamedEventArgs e)
{
if (filters.Contains(Path.GetExtension(e.FullPath)))
Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath);
}
Also as Kunal pointed out
attachmenttosend.Add(Attachmenttosend);
I guess from the uppercase and lower case that you are trying to add to the backing field of the property its own property,dont,also...you dont add to a string only += (concat).
Unless attachmenttosend is a for example a list of strings.

Categories