How to save changes to a variable? [duplicate] - c#

This question already has answers here:
How can I save application settings in a Windows Forms application?
(14 answers)
Closed 7 years ago.
I would like to ask you if anyone of you knows how can I save changes in the progrem so when It will be restarted, the changes would remain?
for example, I have a boolean variable which his defualt value is "false".
I want after the inital start, change the value to "true" so when I'll close and start the program, the boolean variable value would be true.

That's what we have databases for...
or config files
or file systems
You need to retain data, which cant be in memory it has to be on disk
Read about various data persisting strategies try out things and let us know if you are stuck

Choose format you want to use for persisting you data. On Application starts - read file and deserialize it to your model. On Close - serialize it and save to file. Take a look at next question: Best practice to save application settings in a Windows Forms Application
Next sample was taken from the above link:
using System;
using System.IO;
using System.Web.Script.Serialization;
namespace MiscConsole
{
class Program
{
static void Main(string[] args)
{
MySettings settings = MySettings.Load();
Console.WriteLine("Current value of 'myInteger': " + settings.myInteger);
Console.WriteLine("Incrementing 'myInteger'...");
settings.myInteger++;
Console.WriteLine("Saving settings...");
settings.Save();
Console.WriteLine("Done.");
Console.ReadKey();
}
class MySettings : AppSettings<MySettings>
{
public string myString = "Hello World";
public int myInteger = 1;
}
}
public class AppSettings<T> where T : new()
{
private const string DEFAULT_FILENAME = "settings.jsn";
public void Save(string fileName = DEFAULT_FILENAME)
{
File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(this));
}
public static void Save(T pSettings, string fileName = DEFAULT_FILENAME)
{
File.WriteAllText(fileName, (new JavaScriptSerializer()).Serialize(pSettings));
}
public static T Load(string fileName = DEFAULT_FILENAME)
{
T t = new T();
if(File.Exists(fileName))
t = (new JavaScriptSerializer()).Deserialize<T>(File.ReadAllText(fileName));
return t;
}
}
}

Related

C# Path not recognized or utilized when invoking File.AppendAllText

I’m trying to use the File class to work with a text file in a console and winforms desktop app and getting the following exception:
The type initializer for '_Library.Logging' threw an exception
From what I’ve read here this error is typically caused by a problem in App.config for Winfoms apps but the Exception details seem to point elsewhere:
System.ArgumentNullException: Value cannot be null. Parameter name: path
at System.IO.File.AppendAllText(String path, String contents)
The MSDN examples for file manipulation all hard code the path parameter without any reference to using an App.confiig file so my presumption was it is possible to do this without involving ConfigurationManager.
This is the code I'm trying to use
// in calling method
class Program_Console
{
private static StringBuilder SB4log = new StringBuilder();
public static void Main(string[] tsArgs)
{
// Conditionals dealing with argumentts from Task Scheduler
Save2Log("Calling _UI.exe via \"Process.Start()\"", true);
// try-catch-finally for Process.Start
}
private static void Save2Log(string msgTxt, bool noTS)
{
SB4log.AppendLine($"{msgTxt}");
if (noTS) Logging.SaveLog(SB4log);
else Logging.SaveLog_TimeStamp(SB4log);
SB4log.Clear();
}
}
// saving app progression messages to a single log txt file
public static class Logging
{
private static String filePath = Connections.LogPath();
private static StringBuilder SB4log = new StringBuilder();
public static void SaveLog(StringBuilder logTxt)
{
File.AppendAllText(filePath, logTxt.ToString());
logTxt.Clear();
}
}
// class for DB connection and file paths
public static class Connections
{
private static StringBuilder SB4log = new StringBuilder();
public static string AppPath()
{
string appRoot;
try
{
string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
SaveLog($"->App Path: {appDir}", true); // culprit
int loc = appDir.IndexOf("BURS");
appRoot = appDir.Substring(0, loc + 5);
}
catch (Exception ex)
{
// Exception response and MessageBox
}
return appRoot;
}
public static string ConsolePath() {}
public static string UIPath() {}
public static string LogPath()
{
string appRoot = AppPath();
string wrkDir = #"_Library\Data\BURS_Log.Txt";
string fullDir = $"{appRoot}{wrkDir}";
SaveLog($"->Log Path: {fullDir}\n", true); // culprit
return fullDir;
}
}
In stepping through the code the code the variable containing the path -- filePath -- has the expected value: "D:\BURS_Library\Data\BURS_Log.Txt" (quotes used to show there re no unintended spaces needing to be trimmed). Acordinng to MSDN if it's a malformed path an exception will be thrown but the path looks valid to me.
Why isn’t the Path variable utilized?
Any help will be appreciated.
Edit: expanded code to show start-to-finish flow since the original abridged version seemed to be confusing. Have added the text "//culprit" to the two lines which caused the error as pointed out by the responders.
It's not clear what Connections is, but given Connections.LogPath(); it seems that you're calling LogPath(); to set the value for filePath which is a problem because that calls AppPath which has the following statement SaveLog($"->App Path: {appDir}", true);.
You haven't included a version of SaveLog that has 2 parameters, but assuming it's similar to the one you've posted, you're attempting to use filePath when the value hasn't been set yet - which causes an issue.

general error handling message for different projects template

I have the following class which writes new line to a text file.
using System;
using System.Text;
using System.IO;
namespace TextStreamer
{
class TextWriter
{
private string _FilePath;
private string _TextToAdd;
// Constructor will assign file Path variable and check if file is valid
public TextWriter(string filePath)
{
this._FilePath = filePath;
ValidateFile();
}
// Validate if the text file exist
private void ValidateFile()
{
// If file does not exist show error message
// and create new text file
if(!File.Exists(_FilePath))
{
Console.WriteLine("File not found");
File.Create(_FilePath);
Console.WriteLine("Created new file {0}", _FilePath);
}
}
// Write new line to the text file
public void WriteNewLine(string text)
{
this._TextToAdd = text + Environment.NewLine;
File.AppendAllText(_FilePath, _TextToAdd);
}
}
}
Right now if the file does not exist it will write a message to the console and then it will create the text file, but what if i used say WPF application, in this case i prefer showing a message box with the same message, how can i achieve that.
I tried throwing exception FileNotFoundException but that just crashes the program and exit.
A simple way to achieve this is by using a public variable to change the option to console log or show message.
Add the namespace for the generic message box:
using System.Windows;
And add a public variable in your class that will let you programmatically change the logging method, such as:
public bool UseMsgBox = false;
You could improve this by using things like using an int or enum to have more logging methods, though a bool is fine for only 2 options.
Add a logging method such as:
private void LogMsg(string msg)
{
if(UseNsgBox) MessageBox.Show(msg);
else Console.WriteLine(msg);
}
And replace your Console.WriteLine's with LogMsg instead.
Make sure to change the option when you create your class:
TextWriter textWriter = new TextWriter("SomeFile.txt");
textWriter.UseMsgBox = true; // or false
Actually, this might not work as you instantly call the LogMsg when your class is created, so perhaps add it as an initialization parameter as well:
public TextWriter(string filePath, bool useMsgBox = false)
{
UseMsgBox = useMsgBox;
// ...
}

Error: A namespace cannot directly contain members such as fields or methods [duplicate]

This question already has an answer here:
"A namespace cannot directly contain members such as fields or methods" [closed]
(1 answer)
Closed 2 years ago.
I'm new to C# and I'm having trouble solving this error can anyone help me please? This script is to delete an un-needed shortcut then install a new program if it hasn't been installed already.
using System;
using WindowsInstaller;
string startMenuDir = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
string shortcutold = Path.Combine(startMenuDir, #"Ohal\TV AMP (Windows XP Mode).lnk");
if (File.Exists(shortcutold))
File.Delete(shortcutold);
string startMenuDir = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
string shortcut = Path.Combine(startMenuDir, #"Ohal\TV AMP.lnk");
if (File.Exists(shortcut))
{
Console.WriteLine("Already installed...");
}
else
{
Type type = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Installer installer = (Installer)Activator.CreateInstance(type);
installer.InstallProduct(#"Y:\LibSetup\TVAMP313\TVAmp v3.13.msi");
}
Your code should be in a class and then a method. You can't have code under namespace. Something like following.
using System;
using WindowsInstaller;
class MyClass //Notice the class
{
//You can have fields and properties here
public void MyMethod() // then the code in a method
{
string startMenuDir = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
string shortcutold = Path.Combine(startMenuDir, #"Ohal\TV AMP (Windows XP Mode).lnk");
if (File.Exists(shortcutold))
File.Delete(shortcutold);
// your remaining code .........
}
}
As Habib says, you need to put code in methods, constructors etc. In this case if the code you want is just what you want as the entry point, you just need:
using System;
using WindowsInstaller;
class Program
{
// Or static void Main(string[] args) to use command line arguments
static void Main()
{
string startMenuDir = ...;
string shortcutold = ...;
// Rest of your code
}
}
Basically the Main method is the entry point for a stand-alone C# program.
Of course, if your code is meant to be a plug-in for something else, you may need to just implement an interface or something similar. Either way, you'll have to have your code in members rather than just "bare".
Most likely this is what you intended to do:
using System;
using WindowsInstaller;
namespace DataImporter
{
class Program
{
static void Main(string[] args)
{
string startMenuDir = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
string shortcutold = Path.Combine(startMenuDir, #"Ohal\TV AMP (Windows XP Mode).lnk");
if (File.Exists(shortcutold))
File.Delete(shortcutold);
string startMenuDir = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
string shortcut = Path.Combine(startMenuDir, #"Ohal\TV AMP.lnk");
if (File.Exists(shortcut))
{
Console.WriteLine("Already installed...");
}
else
{
Type type = Type.GetTypeFromProgID("WindowsInstaller.Installer");
Installer installer = (Installer)Activator.CreateInstance(type);
installer.InstallProduct(#"Y:\LibSetup\TVAMP313\TVAmp v3.13.msi");
}
}
}
}
Your Method must be in a Class Now This is in a namespace , You must declare a class in this Namespace then declare method in this class

How to save state of checkbox, textbox and ... after closing a program in c#

In my C# program that is made with Visual Studio 2010 and uses WinForms, I would like the program to save state of some checkboxes and textboxes so the next time program will be loaded they are checked or unchecked as theire last run's state. Also same with strings inside textboxes and etc...
What will be the proper way to achieve this? Is there a built in stuff in .NET? Any tips and code snippets would be appriciated!
Thanks
You'd probably want to look at reading the relevant values from your UI during the FormClosing event, and then saving them into User Settings.
Have a look at: http://codehill.com/2009/01/saving-user-and-application-settings-in-winforms/
I would bind the value to user settings, and saving the configuration OnClose event.
One way to do this is using an XML configuration file and serializing it:
ConfigManager.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
namespace MyApplication
{ [ Serializable() ]
public class ConfigManager
{
private int removeDays = 7;
public ConfigManager() { }
public int RemoveDays
{
get
{
return removeDays;
}
set
{
removeDays = value;
}
}
}
somewhere in your application
private ConfigManager cm;
private XmlSerializer ser;
...
Then you have to load the configuration:
private void LoadConfig()
{
try
{
cm = new ConfigManager();
ser = new XmlSerializer(typeof(ConfigManager));
filepath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + cm.filepath;
if (File.Exists(filepath))
{
FileStream fs = new FileStream(filepath, FileMode.Open);
cm = (ConfigManager)ser.Deserialize(fs);
// do something
}
} catch (Exception ex) { }
}
To save it:
XmlSerializer ser;
ConfigManager cm;
...
private void saveConfig()
{
try {
cm.RemoveDays = 6;
TextWriter tw = new StreamWriter(filepath, false);
ser.Serialize(tw, cm);
tw.Close();
} catch (Exception ex)
}
You asked very broad question. there are two ways to look at it.
1) If you have a need to persist application level configuration, your best bet is to use Application Settings. One can serialize program settings the user has done using your app, and restore them after the program has restarted. This works with WinForms and WPF:
2) If you need user level persistence, you need user settings.
Also, you can create custom class that implements that stores all of the configuration properties that you need.
Implement ISerializable and mark it [Serializable]. You could just mark it [Serializable], but if you add new properties in the future, you'll run into deserialization problems.
Add a Version property.
Add two static methods: Load and Save. These methods use IsolatedStorage to deserialize/serialize your configuration class to disk. You can use any kind of serialization you want - I use binary. Why not XML? Because binary is faster and users never need to get into these files. I used to do this for .net 2.0.

Writing Logs to an XML File with .NET

I am storing logs in an xml file...
In a traditional straight text format approach, you would typically just have a openFile... then writeLine method...
How is it possible to add a new entry into the xml document structure, like you would just with the text file approach?
use an XmlWriter.
example code:
public class Quote
{
public string symbol;
public double price;
public double change;
public int volume;
}
public void Run()
{
Quote q = new Quote
{
symbol = "fff",
price = 19.86,
change = 1.23,
volume = 190393,
};
WriteDocument(q);
}
public void WriteDocument(Quote q)
{
var settings = new System.Xml.XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent= true
};
using (XmlWriter writer = XmlWriter.Create(Console.Out, settings))
{
writer.WriteStartElement("Stock");
writer.WriteAttributeString("Symbol", q.symbol);
writer.WriteElementString("Price", XmlConvert.ToString(q.price));
writer.WriteElementString("Change", XmlConvert.ToString(q.change));
writer.WriteElementString("Volume", XmlConvert.ToString(q.volume));
writer.WriteEndElement();
}
}
example output:
<Stock Symbol="fff">
<Price>19.86</Price>
<Change>1.23</Change>
<Volume>190393</Volume>
</Stock>
see
Writing with an XmlWriter
for more info.
One of the problems with writing a log file in XML format is that you can't just append lines to the end of the file, because the last line has to have a closing root element (for the XML to be valid)
This blog post by Filip De Vos demonstrates quite a good solution to this:
High Performance writing to XML Log files (edit: link now dead so removed)
Basically, you have two XML files linked together using an XML-include thing:
Header file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log [
<!ENTITY loglines SYSTEM "loglines.xml">
]>
<log>
&loglines;
</log>
Lines file (in this example, named loglines.xml):
<logline date="2007-07-01 13:56:04.313" text="start process" />
<logline date="2007-07-01 13:56:25.837" text="do something" />
<logline date="2007-07-01 13:56:25.853" text="the end" />
You can then append new lines to the 'lines file', but (most) XML parsers will be able to open the header file and read the lines correctly.
Filip notes that: This XML will not be parsed correctly by every XML parser on the planet. But all the parsers I have used do it correctly.
The big difference is the way you are thinking about your log data. In plain text files you are indeed just adding new lines. XML is a tree structure however, and you need to think about like such. What you are adding is probably another NODE, i.e.:
<log>
<time>12:30:03 PST</time>
<user>joe</user>
<action>login</action>
<log>
Because it is a tree what you need to ask is what parent are you adding this new node to. This is usually all defined in your DTD (Aka, how you are defining the structure of your data). Hopefully this is more helpful then just what library to use as once you understand this principle the interface of the library should make more sense.
Why reinvent the wheel? Use TraceSource Class (System.Diagnostics) with the XmlWriterTraceListener.
Sorry to post a answer for old thread. i developed the same long time ago. here i like to share my full code for logger saved log data in xml file date wise.
logger class code
using System.IO;
using System.Xml;
using System.Threading;
public class BBALogger
{
public enum MsgType
{
Error ,
Info
}
public static BBALogger Instance
{
get
{
if (_Instance == null)
{
lock (_SyncRoot)
{
if (_Instance == null)
_Instance = new BBALogger();
}
}
return _Instance;
}
}
private static BBALogger _Instance;
private static object _SyncRoot = new Object();
private static ReaderWriterLockSlim _readWriteLock = new ReaderWriterLockSlim();
private BBALogger()
{
LogFileName = DateTime.Now.ToString("dd-MM-yyyy");
LogFileExtension = ".xml";
LogPath= Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Log";
}
public StreamWriter Writer { get; set; }
public string LogPath { get; set; }
public string LogFileName { get; set; }
public string LogFileExtension { get; set; }
public string LogFile { get { return LogFileName + LogFileExtension; } }
public string LogFullPath { get { return Path.Combine(LogPath, LogFile); } }
public bool LogExists { get { return File.Exists(LogFullPath); } }
public void WriteToLog(String inLogMessage, MsgType msgtype)
{
_readWriteLock.EnterWriteLock();
try
{
LogFileName = DateTime.Now.ToString("dd-MM-yyyy");
if (!Directory.Exists(LogPath))
{
Directory.CreateDirectory(LogPath);
}
var settings = new System.Xml.XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true
};
StringBuilder sbuilder = new StringBuilder();
using (StringWriter sw = new StringWriter(sbuilder))
{
using (XmlWriter w = XmlWriter.Create(sw, settings))
{
w.WriteStartElement("LogInfo");
w.WriteElementString("Time", DateTime.Now.ToString());
if (msgtype == MsgType.Error)
w.WriteElementString("Error", inLogMessage);
else if (msgtype == MsgType.Info)
w.WriteElementString("Info", inLogMessage);
w.WriteEndElement();
}
}
using (StreamWriter Writer = new StreamWriter(LogFullPath, true, Encoding.UTF8))
{
Writer.WriteLine(sbuilder.ToString());
}
}
catch (Exception ex)
{
}
finally
{
_readWriteLock.ExitWriteLock();
}
}
public static void Write(String inLogMessage, MsgType msgtype)
{
Instance.WriteToLog(inLogMessage, msgtype);
}
}
Calling or using this way
BBALogger.Write("pp1", BBALogger.MsgType.Error);
BBALogger.Write("pp2", BBALogger.MsgType.Error);
BBALogger.Write("pp3", BBALogger.MsgType.Info);
MessageBox.Show("done");
may my code help you and other :)
Without more information on what you are doing I can only offer some basic advice to try.
There is a method on most of the XML objects called "AppendChild". You can use this method to add the new node you create with the log comment in it. This node will appear at the end of the item list. You would use the parent element of where all the log nodes are as the object to call on.
Hope that helps.
XML needs a document element (Basically top level tag starting and ending the document).
This means a well formed XML document need have a beginning and end, which does not sound very suitable for logs, where the current "end" of the log is continously extended.
Unless you are writing batches of self contained logs where you write everything to be logged to one file in a short period of time, I'd consider something else than XML.
If you are writing a log of a work-unit done, or a log that doesn't need to be inspected until the whole thing has finished, you could use your approach though - simply openfile, write the log lines, close the file when the work unit is done.
For editing an xml file, you could also use LINQ. You can take a look on how here:
http://www.linqhelp.com/linq-tutorials/adding-to-xml-file-using-linq-and-c/

Categories