I have a VS2012 in C# solution with 4 projects in 4 layer structure(Presentation, BusinessLogic, DomainModel, DataAccess) and wanted to give the user the option to select the database's file's path at the Login form, which is in the Presentation layer (it is then used when a creating connection to the Database in a static method in the DataAccess layer). And the path would be saved and used the next time the application runs.
A bit more workflow example would be:
User starts the application and the login form appears;
User chooses the Database's file path with a FolderBrowserDialog or
OpenDirectoryDialog;
User works on the application then ends it;
User starts the application and the Database's file path is the one
he picked before;
User works on the application then ends it;
User starts the application and picks another file;
User works on the application then ends it;
User starts the application and the Database's file path is the one
he picked before.
Codewise I don't want to go passing along that string (path) all around my code for each method that needs to create a connection and such.
So any ideas on how to directly save it in the method that's directly using it? If not only when the user wants to change it then just forcing the user to pick the file when the starts the application.
Currently what I'm doing is forcing the user to put the file he wants in the solution's directory with a specific name before starting the application when he wants to use another Database file. For that I'm using:
public static string path;
public static OleDbConnection createConnection()
{
path = DirProject() + "\\Calibrações Equipamentos ULSM.accdb";
OleDbConnectionStringBuilder builder = new OleDbConnectionStringBuilder();
builder["Provider"] = "Microsoft.ACE.OLEDB.12.0";
builder["Data Source"] = path;
return new OleDbConnection(builder.ConnectionString);
}
private static string DirProject()
{
string DirDebug = System.IO.Directory.GetCurrentDirectory();
string DirProject = DirDebug;
for (int counter_slash = 0; counter_slash < 3; counter_slash++)
{
DirProject = DirProject.Substring(0, DirProject.LastIndexOf(#"\"));
}
return DirProject;
}
Configuration (=data) is not saved into a method (=code). It's usually saved into a configuration file. You can leverage .NET's application and user settings mechanism to achieve what you want.
Use User Settings
How To: Write User Settings at Run Time with C#
How To: Read Settings at Run Time With C#
Related
I'm using visual studio to do my first full release of an application, which is a simple WPF GUI to configure a locally saved JSON object, and a console application to run in the background, using data from that object to monitor a website. I published each project to a test folder (we'll say Publish/), where each application manifest now resides, along with an 'Application Files' folder containing another sub-folder for each project.
I got the applications to run just fine - the problem is the configuration file saves to one folder a few more levels into C:\Users\username\AppData\Local\Apps\2.0\ and the console application is attempting to read from a completely separate folder. Both projects write and read using GetCurrentDirectory() for the file path. Is there a way I'm missing to remedy this?
Code for retrieving JSON object
try
{
//Confirms file exists
if (File.Exists(Directory.GetCurrentDirectory().ToString() + filePath))
{
//Initialize json variable to a string, then deserialize it into an instance of RCDetails
var json = File.ReadAllText(Directory.GetCurrentDirectory().ToString() + filePath);
RCDetails parsedDetails = JsonConvert.DeserializeObject<RCDetails>(json);
//Return deserialized JSON
return parsedDetails;
}
else return null;
And code for writing json object
public void WriteToFile(RCDetails rcd)
{
//Serialize input instance of RCDetails class
string JSONresult = JsonConvert.SerializeObject(rcd, Formatting.Indented);
//Remove old JSON object
if (File.Exists(Directory.GetCurrentDirectory().ToString() + jsonFilePath))
File.Delete(Directory.GetCurrentDirectory().ToString() + jsonFilePath);
//Create new JSON object with updated details
File.WriteAllText(Directory.GetCurrentDirectory().ToString() + jsonFilePath, JSONresult);
}
Exact steps for publishing project:
With console app selected, click Build > Publish (console app name)
Browse to C:\Users\username\Downloads\Publish\
For installation, choose "From a CD-ROM or DVD-ROM"
Select "the application will not check for updates
Select finish, and repeat all steps with same file path for config WPF
Solution I wound up finding (feel free to let me know if this is a bad plan). I just plopped this into both apps.
string s = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\IntendedFolder";
if (!Directory.Exists(s))
Directory.CreateDirectory(s);
Directory.SetCurrentDirectory(s);
I am still new to the WPF/XAML coding and while learning I encountered another problem. I decided I want to add buttons on the UserControl, that I would like to make them do a few different things. On one of them, I want to open the local default browser and open a webpage link, and in another button, I want to start a local exe/rpm file from a directory in my project called "tools".
For opening the link I tried - WebBrowserTask which is an unrecognized event/task
For the running of the application - Process.Start("thelocation/thefile.exe/rdp"). After that, I tried guiding it to the proper path, but my project doesn't recognize the folder and files inside.
Both tries were unsuccessful.
Try this:
public void DoSomething
{
const string webpageUrl = "https://stackoverflow.com/questions/55778625/";
const string localFile = #"C:\Windows\notepad.exe";
var localTools = $#"{AppDomain.CurrentDomain.BaseDirectory}Tools\SomeTools.exe";
Process.Start(webpageUrl);
Process.Start(localFile);
Process.Start(localTools);
}
to opening an web page your address must be start with http://...
First, some background on our app: When deployed, our application will be installed on a clients machine in the Program Files directory. We use connection strings which are stored in the App.config file that comes with the program. Under most circumstances, these connection strings will never change. However, at some point, it may be possible that the connection string info will go out of date. To that end, we have included a feature in our program that allows the user to enter new database information if the database can't be reached. The information is parsed into a connection string, tested for validity, and written back into the config file if valid. (Yes, this change is intended to affect all users on the computer, since they will be unable to run the application without a valid connection string anyway - if one user knows the DB info, then other users on the same computer will benefit from that knowledge. Writing to App.config instead of a per-user settings file is preferred.)
The problem I'm running into is that the end user will not have admin permissions, and thus will not be able to run our app at a level that allows it to make any changes to its own config file (since it is located in the C:\Program Files directory). I'm looking at a couple of different options, but I'm having problems implementing each of them:
Move config file to a different location. Not possible in this case because we are deploying an executable, and from what I understand, the App.config file must reside in the same directory.
Separate the connection string info into an external config file. I know about the configSource property that can be added to the connection string section of App.config. However, I'm having trouble specifying a concrete target. I can't simply put the file alongside the executable (otherwise I'd get the same permissions issues as the regular App.config). However, it appears environment variables (such as %AppData%) are not supported as valid config sources. I have tried to implement the solution from this question, but the program crashes before I can change the config source to a valid string because the ConfigurationManager apparently attempts to read the config source folder immediately and thus crashes with a FileNotFoundException.
Create our own config file implementation. I could always just create a custom class that is dedicated to reading a config file that is located in a specific location, but I would prefer using the built-in ConfigurationManager if possible.
My main question is this: How can I allow the end user (who only has read permissions in the application folder) to modify config settings dynamically when the config file must stay in the application folder?
Since windows xp, the OS prevents programs with out admin privileges from writing to the Program Files folder. The "Program Data" folder was created to allow programs to store persistent information. I always use a custom config file to store program data that an end User needs to config to connect to a database. I also encrypt at least the password of the connection string, but that is a another discussion. I provided a sample class and usage that stores a config file in Program Data Folder.
Config Class
using System;
using System.IO;
using System.Xml.Serialization;
namespace MyNamespace
{
public class Config
{
public string SomeStringProperty { get; set; }
public int SomeIntProperty { get; set; }
private static string _filePath = null;
static Config()
{
_filePath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.CommonApplicationData) + #"\My Program Name";
}
public static Config LoadConfig()
{
if (File.Exists(_filePath))
{
try
{
XmlSerializer reader = new XmlSerializer(typeof(Config));
StreamReader file = new StreamReader(_filePath);
Config config = (reader.Deserialize(file) as Config);
return config;
}
catch (Exception ex)
{
//Deal With no file file read error here.
}
}
// IF we get here no valid config file was loaded so make a new one.
return new Config();
}
public Exception SaveConfig()
{
try
{
XmlSerializer writer = new XmlSerializer(typeof(Config));
StreamWriter file = new StreamWriter(_filePath);
writer.Serialize(file, this);
file.Close();
return null;
}
catch (Exception ex)
{
return ex;
}
}
}
}
Basic Usage
Config config = Config.LoadConfig();
Debug.WriteLine("SomeIntProperty=" + config.SomeIntProperty.ToString());
Debug.WriteLine("SomeStringProperty=" + config.SomeStringProperty);
config.SomeIntProperty = 10;
config.SomeStringProperty = "Hello";
config.SaveConfig();
var configuration = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
var section = (ConnectionStringsSection)configuration.GetSection("connectionStrings");
section.ConnectionStrings["YourNameConnectionString"].ConnectionString = "yourstring";
My speech recognition project include 2 forms form1 & form2. Form2 is my main form but before loading form2 my program take a variable value from user through form1 and pass it to form2. It means at start my program opens form1 takes value & pass it to form2 then form2 is shown.
Now my question is how to
make form1 load only at programs 1st launch after installation and after 1st launch directly form2 is loaded thereafter?
means form1 should not be loaded after that..
I suggest to use a simple textfile where you could store the input value recorded the first time your app starts, then, check if the file with the value exists and read it back.
For Example
string customValue = string.Empty;
string appData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
appData = Path.Combine(appData, "MyAppName");
if(!Directory.Exists(appData))
Directory.CreateDirectory(appData);
string appDataFile = Path.Combine(appData, "MyAppNameSettings.txt");
if(File.Exists(appDataFile))
customValue = File.ReadAllText(appDataFile);
else
{
customValue = AskUserForTheFirstTimeValue();
File.WriteAllText(appDataFile, customValue);
}
The file is stored in a subfolder of the common application data (C:\programdata) created to store your data files. You check if the file exists at first launch. If the file exists you read its content (assumed to be a simple string here), if the file doesn't exist then you ask the input and store the value for the successive runs of your app.
You can create a Registry Key in Windows Registry (regedit), and when you start your program verify if its exists and the value.
Link about Registry Keys:
http://msdn.microsoft.com/en-us/library/microsoft.win32.registrykey(v=vs.110).aspx
You should have a settings file that keeps a variable like IsFirstRun = true; This application should be distributed and compiled with this file, at start up you should read this file and if you encounter the true state you should load the appropriate forms. You should also ensure that the value is immediately set to false for the second launch condition.
have a look at .net's setting class.
There are two ways I can suggest:
First one
Use the application configuration file:
Creating such file in c# is pretty easy, per usual if you start with a standard Windows Forms Project Template you will most likely already have an app.config in that project, if not, follow instructions from here (under Application Configuration Files).
Add a simple boolean value to the file like so (that appSettings-node is created under the root configuration-node:
<appSettings>
<add key="FirstRun" value="true" />
</appSettings>
In your first form, where you do your application configuration you add code to manipulate your app.config programmatically using the ConfigurationManager-Class for the event that configuration has been finished and can be saved (probably some button click, note that you will need a Reference to System.Configuration):
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
bool myVal = bool.Parse(config.AppSettings.Settings["FirstRun"].Value);
if (myVal)
{
// MessageBox.Show("yep, its true");
config.AppSettings.Settings["FirstRun"].Value = false.ToString();
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("appSettings");
}
else
{
// MessageBox.Show("na, its not true anymore.");
}
Second one
Use a property grid for your application configuration:
Since from your description it seems you only use the first form to enter some application configuration values, I would recommend using the PropertyGrid-Control for that.
That control can be easily bound to an object that exposes some properties (what a surprise) that are then used to render a standardized control containing captions and value selection controls dependend on the property's types, for example:
public class Settings
{
public int MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
}
Then you check at your program start, whether the configuration file (you define the path) exists, and if so, try to deserialize an object from its xml (sample below works for primitive types, for more complex ones you might have to extend the serializer, but you get the idea).
That serialized object represents your saved application settings, so you do not need to open up the propertygrid-form anymore.
If no file could be found the grid gets initialized with a simple new constructed object and you show that form:
this goes into your initialization code:
FileInfo file = new FileInfo("SaveHere.xml");
if (file.Exists)
{
using(StreamReader reader = new StreamReader("SaveHere.xml"))
{
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
Settings mySettings = (Settings)serializer.Deserialize(reader);
reader.Close();
}
}
else
{
this.propertyGrid1.SelectedObject = new Settings();
// show form code
}
this code goes into the event code where you want to save your configuration
using(StreamWriter writer = new StreamWriter("SaveHere.xml",false))
{
XmlSerializer serializer = new XmlSerializer(this.propertyGrid1.SelectedObject.GetType());
serializer.Serialize(writer, this.propertyGrid1.SelectedObject);
writer.Close();
}
I used following code that mention on this post
Do not overwrite settings.settings file with clickonce
but it is not working for my connection string that stored in setting with application scope, I am in doubt that this method work for user scope only or both application and user scope ?
if (Settings.Default.upgradeRequired)
{
Settings.Default.Upgrade();
Settings.Default.upgradeRequired = false;
Settings.Default.Save();
}
I come up with that by adding one more setting named "userconnection" Scope:User then
every time that user must set its connection string then this "userconnection" updated, Then in main point of my application check if userconnection(User Scope)!=ConnectionString(Applicaiton Scope) then try to change application ConnectionString to user customized one which was stored in userconnection properties.