How to make designer generated .Net application settings portable - c#

I've been looking at modifying the source of the Doppler podcast aggregator with the goal of being able to run the program directly from my mp3 player.
Doppler stores application settings using a Visual Studio designer generated Settings class, which by default serializes user settings to the user's home directory. I'd like to change this so that all settings would be stored in the same directory as the exe.
It seems that this would be possible by creating a custom provider class which inherits the SettingsProvider class. Has anyone created such a provider and would like to share code?
Update: I was able to get a custom settings provider nearly working by using this MSDN sample, i.e. with simple inheritance. I was initially confused as Windows Forms designer stopped working until I did this trick suggested at Codeproject:
internal sealed partial class Settings
{
private MySettingsProvider settingsprovider = new MySettingsProvider();
public Settings()
{
foreach (SettingsProperty property in this.Properties)
{
property.Provider = settingsprovider;
}
...
The program still starts with window size 0;0 though.
Anyone with any insight to this?
Why the need to assing the provider in runtime---instead of using attributes as suggested by MSDN?
Why the changes in how the default settings are passed to the application with the default settings provider vs. the custom one?

Why not use the CodeProject PortableSettingsProvider solution as is (with a few minor changes) ?
I have done so in my project (StreamRecorder.NET) with success.
Some comments on the project's page were useful:
http://www.codeproject.com/Messages/2934144/Fixed-csharp-version.aspx
http://www.codeproject.com/Messages/3285411/Re-Win-Form-Designer-breaking-with-custom-Settings.aspx
And the code I ended up with:
static void Main(string[] args)
{
if (args.Contains("-p") || args.Contains("--portable"))
{
MakePortable(Properties.Settings.Default);
MakePortable(Properties.LastUsedSettings.Default);
MakePortable(Properties.DefaultSettings.Default);
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm(args));
}
private static void MakePortable(ApplicationSettingsBase settings)
{
var portableSettingsProvider =
new PortableSettingsProvider(settings.GetType().Name + ".settings");
settings.Providers.Add(portableSettingsProvider);
foreach (System.Configuration.SettingsProperty prop in settings.Properties)
prop.Provider = portableSettingsProvider;
settings.Reload();
}
Lastly I made these changes to the CP project:
string _fileName;
public PortableSettingsProvider(string fileName)
{
_fileName = fileName;
}
public virtual string GetAppSettingsFilename()
{
//Used to determine the filename to store the settings
//return ApplicationName + ".settings";
return _fileName;
}

I know this question is quite old already. I just want to share my own version of a portable settings provider which I published as nuget package here.
The usage is pretty simple:
// make the default settings class portable
PortableSettingsProvider.ApplyProvider(Properties.Settings.Default);
I also explained the basic strategy of this implementation at https://www.codeproject.com/Articles/1238550/Making-Application-Settings-Portable.

Just to 'close' the question: The somewhat unsatisfactory solution I ended up with was
Create a custom settings provider, which inherits from SettingsProvider and stores the settings in a XML file
Set the Provider property of each of the setting (by selecting the entire grid in the designer) to the custom settings provider using the designer
Drawbacks: The forms designer breaks and gives an exception which basically says that the custom provider class cannot be found. The built exe however works OK. Setting the provider in the code as described in the question makes the designer work, but then for some reason, which I haven't looked closely at, the settings won't serialize.
It seems that making settings portable was all that was needed to make Doppler portable. Whether I'll start using Doppler as my main podcast aggregator or stick with my homebrew command line aggregator, I'll see.

Related

Read Settings from Dependent Project in C#

I just want to start by saying that I've done a lot of research but couldn't find an answer, hence the post.
I'm adding user settings functionality to my app which works as a plugin inside a common off the shelf program for architecture (called Autodesk Revit). The main project (let's call it MainProj) has several dependencies including a project that handles logging and usage (let's call it Loggers). I created a Settings file inside the Loggers project with the goal to have users change the logging level from Error to Debug when there are issues so I can ask them to make the change and send me the log.
The issue I'm facing is that when I change the log level directly inside the config file and re-run the command from within Revit, the change doesn't get translated into the log, as if the log level is somehow compiled during design and is never changed.
I actually tried to reproduce the problem in a simpler way and created a little console program and I'm facing the same issue. Below is the code from the Loggers project.
namespace Loggers
{
public static class Logger
{
public static string RunMe()
{
if (Properties.Settings.Default.LogMode == "Debug") { return "DEBUG"; }
else return "NOTHING";
}
}
}
I then changed the LogMode property from Debug to anything else in the config file but the console kept on returning DEBUG.
static void Main(string[] args)
{
Console.WriteLine(Logger.RunMe());
Console.Read();
}
I also tried changing the setting from user to application and editing its value in the config file and re-running the command but the outcome was the same.
Any help would be very much appreciated. I've been stuck on this for a while. Thank you.
Thanks to #BurnsBA, the link you shared had comments saying that the user.config lives in a different folder and it's not created until the user changes a setting. This made me understand that there wasn't a point in manually editing the app.config and expect the settings to work.
I then did some testing by creating a simple form with a checkbox linked to the Property I wanted to change and the user.config file gets created straight after I call the Save() method on the Properties.
private void btnOK_Click(object sender, EventArgs e)
{
if (chkDebugMode.Checked == true)
Loggers.Properties.Settings.Default.LogMode = "Debug";
else Loggers.Properties.Settings.Default.LogMode = "Error";
Loggers.Properties.Settings.Default.Save();
Close();
}

Determine if library loaded into Console, Desktop, or UWP app

I have a libary which needs to behave differently for console applications, desktop application (e.g. WPF), and for UWP apps.
How can I determine at run-time into which application type my libary is loaded?
Determining if it is a console application seems easy: How to tell if there is a console
For UWP, I can probably determine if WinRT is loaded. But how?
What distinguishing attributes do desktop applications have?
I ended up defining following enum:
public enum ExecutionMode
{
Console,
Desktop,
UniversalWindowsPlatform
}
which is passed to the constructor of the main class of my libary. Not a new idea, but very reliable (if used correctly).
Create a CustomAttribute in an assembly that is available to all of the applications like so
using System;
namespace MyNamespace.Reflection {
[System.AttributeUsage(AttributeTargets.Assembly)]
public class ApplicationTypeAttribute : Attribute {
public enum ApplicationTypes {
Console,
Desktop,
UWP,
ClassLibrary
}
public ApplicationTypeAttribute(ApplicationTypes appType) {
ApplicationType = appType;
}
public ApplicationTypes ApplicationType { get; private set; } = ApplicationTypes.Console;
}
}
Then add this attribute to your AssemblyInfo.cs file for a console application
using MyNamespace.Reflection;
[assembly: ApplicationType(ApplicationTypeAttribute.ApplicationTypes.Console)]
or a Desktop application
[assembly: ApplicationType(ApplicationTypeAttribute.ApplicationTypes.Desktop)]
etc.
Then wherever you want to check the calling type of the application that was started, use this
using MyNamespace.Reflection;
var assy = System.Relection.Assembly.GetEntryAssembly();
var typeAttribute = assy.GetCustomAttribute(typeof(ApplicationTypeAttribute));
if (typeAttribute != null) {
var appType = ((ApplicationTypeAttribute)typeAttribute).ApplicationType;
}
There is one caveat to this method. .NET Core apps have a different project structure and the AssemblyInfo.cs file is auto-generated at build time by default. You can override this behavior by specifying the following in the .csproj file in the Project node.
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
To match the old project file structure, you can create a Properties directory in the project directtory and then you can add an AssemblyInfo.cs file to that directory. Otherwise you can place the Custom Attribute definition in any file (after the usings and before the namespace declaration).

Roslyn AdditionalFiles

I want to develop a Roslyn Code Analyzer which has access to some static configuration in the form of text files. Since an analyzer cannot access the local file system I guess the only way to read such external configuration is via Context Option and AdditionalFiles.
I am aware of this example dealing with this problem:
https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Using%20Additional%20Files.md
What the example does not say is: Is the analyzer reading the AdditionalFiles shipped with the analyzer assembly or the target being analyzed? The latter does not solve my problem because the configuration is analyzer and not target specific.
Update:
I cannot use the standard "Add New Text File" resource mechanism either. The according context menu entry is disabled:
This seems to be related to the TargetFrameworkProfile which is set to Profile7 when creating a new "Analyzer with Code Fix (NuGet + VSIX)" project.
You should be able to use this overload of the ResourceManager class and just pass in a type defined in your assembly.
class MyResourceManager
{
private readonly ResourceManager _manager;
public MyResourceManager()
{
_manager = new ResourceManager(typeof(MyResourceManager))
}
public string GetStringResouce(string name)
{
return _manager.GetString(name);
}
}

Best practice for reading/writing and accessing settings (not using windows settings)

First thing to say is that I can't use built in Properties.Settings, as these settings are stored in an XML file which are used by several people (not at the same time but via version control).
I am developing a code generation tool which allows the users to specificy the output locations of all the generated files, along with other project specific settings.
Currently, in the application I have a static class called ProjectSettings which has a public static parameter for each setting using the in program:
public static string Settings_ScreenDefinitions_C;
public static string Settings_ScreenDefinitions_H;
// Etc currently there are about 20 of these.
Is there a better way of storing and accessing these thoughout the project?
Secondly these settings are stored in an XML using a read/write call for each setting in the above ProjectSettings class:
xmlWriter.WriteElementString("ScreenDefinitionsFileC", ProjectSettings.Settings_ScreenDefinitions_C);
xmlWriter.WriteElementString("ScreenDefinitionsFileH", ProjectSettings.Settings_ScreenDefinitions_H);
// Again there is around 20 of these, one for each property and the same for reading them back out.
As I add more properties I can't help but feel that there must be a more elegant way of constructing the class (maybe a dictionary?) and a better way of using that within the XML for saving and reading?
Any suggestions/pointers would be great.
Here is how I would do it with an enum and a dictionary. I've used console output to show how you can write the key and value of the dictionary.
using System;
using System.Collections.Generic;
namespace ConsoleApplication
{
class Program
{
private enum Settings
{
ScreenDefinitionsFileC,
ScreenDefinitionsFileH
};
static void Main(string[] args)
{
var settings= new Dictionary<Settings, string>()
{
{Settings.ScreenDefinitionsFileC, "Setting 1"},
{Settings.ScreenDefinitionsFileH, "Setting 2"}
};
foreach (var setting in settings)
{
Console.WriteLine("{0} {1}", setting.Key, setting.Value);
}
Console.ReadKey(true);
}
}
}
This way you can enumerate your settings when writing to XML, but can also look-up specific settings during the life cycle of your program.
I don't know the whole list of the configurations, but I hope most of them may be controlled by adding different configuration in configurationManger, that is if you add "MyConfiguration" you can open afterwards project settings (especially pre-build and post build event)and the configurations there are all only for "myConfiguration" so switching between different configurations is only by selecting different configuration.

Adding a user Settings property at runtime (Winforms)

I am using C# ,win forms and .net 2.0
I am trying to add a property inside user settings file at run time but i am not able to view that added property in settings.settings file in certain location i.e. file exists but property is not added
I am not getting error when i call this property it works
Using this below code
MessageBox.Show(***********.Properties.Settings.Default.Properties["NewProperty"].DefaultValue);
I have written this following code
Calling the function
clCommonFuncation cl = new clCommonFuncation();
if (***********.Properties.Settings.Default.Properties["NewProperty"] == null)
{
cl.addPropertyinSettingsFile("NewProperty",
***********.Properties.Settings.Default.Providers,
***********.Properties.Settings.Default.Providers["LocalFileSettingsProvider"],
***********.Properties.Settings.Default.Properties,
typeof(string),"ASD",null);
***********.Properties.Settings.Default.Save();
***********.Properties.Settings.Default.Reload();
}
And this is calling funaction
public void addPropertyinSettingsFile(string settingName,
SettingsProviderCollection settingsProviderCollection,
SettingsProvider settingsProvider,
SettingsPropertyCollection settingPrpertyCollection,
Type dataType,
object defaultValue,
object settingDefault)
{
SettingsProperty lvSettingProperty = new SettingsProperty(settingName);
lvSettingProperty.DefaultValue = defaultValue;
lvSettingProperty.IsReadOnly = false;
lvSettingProperty.PropertyType = dataType;
lvSettingProperty.Provider = settingsProvider;
lvSettingProperty.SerializeAs = SettingsSerializeAs.String;
lvSettingProperty.Name = settingName;
lvSettingProperty.Attributes.Add(typeof(System.Configuration.UserScopedSettingAttribute),
new System.Configuration.UserScopedSettingAttribute());
settingPrpertyCollection.Add(lvSettingProperty);
}
What is it i am doing wrong?
Any suggestion will be appreciated
Thank you
I think you'd better write a custom struct or class with your application settings and use serialization for loading and saving it - that is much more clear and relevant in your case.
You can not add or remove properties in .Net Setting files at runtime. There are some tricks round the web but none of them is applicable and a solution to what you want.
Settings files have not been designed for such a purpose. These files have been designed to be populated at design time and only be "read" or "modified" during runtime.
The reason is that when you create and edit settings file in designer (by double clicking on a settings file in solution explorer or choosing settings tab at project properties menu item) Visual studio creates an ApplicationSettings class (Derived from ApplicationSettingsBase class) which has data members for any setting field you created plus some additional attributes (like [ApplicationScopedSetting] or [UserScopedSetings] ). At run time the .Net runtime interacts with seetings files using this class. Therefore when you try to add properties at run time, you have no backing fileds and attributes in the ApplicationSettings class and CLR does not know what to do with them.
Conclusion: The Settings file have specific usage and are not suitable for any arbitrary configuration persistence in you application. Try using XML files which fully support what you want from reading, writing, adding, removing, updating basic types properties (string, char, int and so on) to supporting complex objects using XML.Seriliazation.

Categories