How to read values from 'WPF' project App.config file from within class library - app-config

I have a WpfApplication project (Visual Studio 2008) with an app.config generated from entering 'Name' and 'Value' pairs in the Settings.settings file (and therefore generating 'applicationSettings' elements rather than 'appSettings' elements).
I have added a class1 class library project in the same Visual Studio solution.
** I have added the WpfApplication app.config file to the class1 project using "add existing item and then add as link".** since found out this is unneccassary (i.e. string a = ConfigurationManager.AppSettings.Get("key1"); below works without this)
I want to read the values within app.config from class1 and have explored the following:
string s1 = Settings.Default.appsetting1;
But I do not want to reference the WpfApplication10 project from Class 1 project and so cannot get a reference to the Settings class. So this syntax only works within the WPF project.
string a = ConfigurationManager.AppSettings.Get("key1").ToString();
This does work, but only if I add the following to app.config:
<appSettings>
<add key="key1" value="1"/>
</appSettings>
Are there other ways to achieve what I want which may be better (e.g. offering type safety or being able to read the Settings class properties?

You can do the following thing:
if you need to get value from key="key1" just write this(supposing your app.config is in the correct directory).
string str=System.Configuration.ConfigurationSettings.AppSettings["key1"];

Related

Creating classical Properties.Settings in .Net 6.0 (Core) "Class Library" projects

Created a new "WPF Application" .NET 6.0 project
There creating classical Application Settings was easy in project->properties->Settings->"Create or open application settings"
Observed: the project gets a new folder "Properties" which has a yellow Folder icon with an additional black wrench symbol, okay
It contains a new item Settings.settings that can get edited via classical Settings Designer looking like it used to look in .Net 4.8, and a new App.config XML file is getting created automatically in the project's root folder which also looks like it used to in .Net 4.8, okay
Now the same procedure can apparently only be done manually in
a new "Class Library" project being added in the same solution where I would want to use that Properties.Settings / app.config feature pack for storing a DB connection string configurably:
the new sub-project does not seem to have a "Settings" option in the project Properties dialog (as opposed to a .Net4.x would have had)
the new Properties folder and new Settings file can be created successfully there too manually as described in Equivalent to UserSettings / ApplicationSettings in WPF .NET 5, .NET 6 or .Net Core
but doing a "Rebuild solution" gives an
Error CS1069 The type name 'ApplicationSettingsBase' could not be found in the namespace 'System.Configuration'. This type has been forwarded to assembly 'System.Configuration.ConfigurationManager, Version=0.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' Consider adding a reference to that assembly. ClassLibrary1 C:\Users\Stefan\source\repos\WpfCorePropertiesSettings\ClassLibrary1\Properties\Settings.Designer.cs 16 Active
as a next step adding NuGet package "System.Configuration.Abstractions" to the Class Library project cures the symptom, "rebuild solution" makes the error disappear.
TLDNR, actual question: is that sequence an acceptable solution or a kludge to avoid?
To me the NuGet package description does not sound as if the package was made for that purpose, and I have not heard the maintainers' names before (which might or might not matter?)
https://github.com/davidwhitney/System.Configuration.Abstractions
TIA
PS:
Maybe I don't understand something...
Why create "Equivalent to UserSettings"?
My configuration is Win10+VS2022. I am creating a WPF .Net6 project. I go to the "Project Properties" menu. In the menu of the project properties tab (column on the left) there is an item Options. When selected, if the settings have not yet been created, there will be a small comment and a link to "Open or create application settings".
Unfortunately, I have Russian localization, so the screenshots are with Russian names.
Addition
But an additional "Class Library" sub-project does not seem to have that Project Properties option in my En/US localization. Does it in yours?
These are the APP settings.
Therefore, they do not make much sense in the library.
But if you need to, you can just copy the class to the library and then set up the links you need.
To do this, type in the application code the line Properties.Settings.Default.Save();. Move the cursor to Settings and press the F12 key.
You will be taken to the source code for the Settings class declaration. This code is generated by a code generator.
After moving to, copy all the source code into a class in another project. After the migration, you may need to add references in the project, fix the namespace and add usings.
As for the parameters in the «Class Library» project, it probably depends on what type this library is.
I have such settings in the «Class Library for WPF».
But in Libraries for Standard - no.
In the meantime I'm happy with a custom "AppSettings.json" approach.
After removing the previously described "classical app.config" approach, and after adding two NuGet packages:
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
... I created a custom Json file on "Class Library" (sub)project level in Visual Studio manually, and set its CopyToOutputDirectory property
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
And added an 'IConfigurationBuilder` part:
using Microsoft.Extensions.Configuration;
namespace Xyz.Data
{
internal class AppSettingsConfig
{
public AppSettingsConfig()
{
IConfigurationBuilder builder = new ConfigurationBuilder();
_ = builder.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "AppSettings.Movies3Data.json"));
var root = builder.Build();
AttachedDb = root.GetConnectionString("AttachedDb")!;
}
public string AttachedDb { get; init; }
}
}
And then made it a "Jon Skeet singleton"
/// <summary>
/// Singleton as described by Jon Skeet
/// </summary>
/// https://csharpindepth.com/Articles/Singleton
internal sealed class AppSettingsConfigSingleton
{
private static readonly Logger log = LogManager.GetCurrentClassLogger();
private AppSettingsConfigSingleton()
{
log.Trace($"{nameof(AppSettingsConfigSingleton)} ctor is running");
IConfigurationBuilder builder = new ConfigurationBuilder();
_ = builder.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "AppSettings.Movies3Data.json"));
var root = builder.Build();
AttachedDb = root.GetConnectionString("AttachedDb")!;
}
static AppSettingsConfigSingleton() { }
public string? AttachedDb { get; init; }
public static AppSettingsConfigSingleton Instance { get { return Nested.instance; } }
private class Nested
{
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static Nested()
{
}
internal static readonly AppSettingsConfigSingleton instance = new();
}
}
And it "works well" by also reading JSON content just having been modified by admins at run-time. (Which would be the Entity Framework Core "localdb" location for the unit-of-work pattern in a multi-UI solution). Thanks again to you too, #EldHasp

.Net 6 WinForms - Unable to get configuration section value

I have a .Net 6 WinForms application and am trying to access some sections in the App.config (myapp.dll.config after build).
I have a class defined with the properties MyProp { public String runDate {get;set;} and app looking for a section called in the file.
I have used ConfigurationBuild().SetBasePath(AppContext.BasePath().AddXMLFile("myapp.dll.config").Build() returning an IConfiguration object called config.
The issue is that when I call either config.GetSection("MyProp").Bind(props) or config.GetSection("MyProp").Get(); It returns the entity but with the value null.
Note: if I change the settings section to bob then both values work. However, my config section is in the old web.config format and uses key/value pairs.
I recognize that when using an XML Provider, the key and value become attributes of the XElement, so its [the xelement] will have a value set to nothing. I was wondering if AddXmlFile was the correct provider to use for this?

MC3074 - type does not exist in "clr-namespace..."

Im having trouble referencing classes in xaml from other assemblies.
In the same solution, i have two projects. One called Controls (to hold user controls) and one called DataBinding (holding converters / validation rules). In a control, im attempting reference a validation rule in xaml:
<Binding.ValidationRules>
<databind:Validators.FileExistsRule />
</Binding.ValidationRules>
My project references the project containing my classes. Ive added this declaration at the top of my Control.xaml:
xmlns:databind="clr-namespace:GuiParts.DataBinding;assembly=DataBinding"
However, when i compile, i get an error:
The tag 'Validators.FileExistsRule' does not exist in XML namespace 'clr-namespace:GuiParts.DataBinding;assembly=DataBinding'.
The class definitely exists, i can call it in the code behind with no problems, but not via xaml. If i move the class to the same project, again, i have no problems. Ive seen other questions on here, and have tried the following:
Cleaning and rebuilding all relevant projects
Ensuring all projects are targeting the same version of .Net (4.0, Full Profile)
Removing the 'assembly' definition from the end of the namespace definition.
None of the above has worked. Any suggestions as to where im going wrong?
EDIT
My FileExists Validator:
namespace GuiParts.DataBinding.Validators
{
/// <summary>
/// Validates that the file with the specified name exists
/// </summary>
public class FileExistsRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
ValidationResult res = null;
res = ( ! File.Exists((string)value))
? new ValidationResult(false, "File does not exist")
: new ValidationResult(true, null);
return res;
}
}
}
I can call the following in the code behind without any errors:
new GuiParts.DataBinding.Validators.FileExistsRule();
So ive got my namespaces etc. correct.
Try this:
xmlns:databind="clr-namespace:GuiParts.DataBinding.Validators;assembly=DataBinding"
<Binding.ValidationRules>
<databind:FileExistsRule />
</Binding.ValidationRules>
Is your class in your target assembly public?
Is the field in Validators, public?
Is your namespace GuiParts.DataBinding correct?
While I'm not sure what the issue you're having is, you can alternatively create a friendlier namespace definition for your assembly and CLR namespaces. In fact, I use this technique to group various namespaces into one XML namespace... You do this using the XmlnsPrefixAttribute and the XmlnsDefinitionAttributes.
For example:
[assembly: XmlnsPrefix("http://my.xml.namespace.com/", "databind")]
[assembly: XmlnsDefinition("http://my.xml.namespace.com/",
"GuiParts.DataBinding")]
[assembly: XmlnsDefinition("http://my.xml.namespace.com/",
"GuiParts.DataBinding.Validators")]
Then, when you want to reference the xmlnamespace in your xaml you simply do:
xmlns:databind="http://my.xml.namespace.com/"
Note, I use ReSharper, but I'm sure this also works naturally in Visual Studio. If you don't type in the xmlns import and attempt to use an object in the namespace, then when you go to resolve it it will automatically use the friendlier named namespace with the specified prefix. Also, it's really nice and helps you avoid having a 1:1 relationship between xml namespaces and clr namespaces because you can map multiple clr namespaces into a single xml namespace.
Again, I'm not sure what the issue you're having specifically is, but this will probably fix it and be better than using clr-namespaces and assembly information. Just make sure to come up with something unique so you don't run into xmlns collisions or you'll have to go back to clr/assembly namespacing.
Oh, and one last thing... if you wish to utilize versioning in your xml namespace naming scheme (which you should), don't worry about locking yourself in for backwards compatibility. You can always utilize the XmlnsCompatibleWithAttribute to ensure that code that utilized the old friendly namespace doesn't break if you ever update your external assemblies to map to a newer xml namespace.
For example, if you originally had you're assembly pointing to a 2012 namespace, then switched it to a 2013 namespace because you updated the assembly...
// Previous Assembly version
//[assembly: XmlnsDefinition("http://schemas.xyzcorp.com/wpf/2012",
// "Xyz.Databinding")]
[assembly: XmlnsCompatibleWith("http://schemas.xyzcorp.com/wpf/2012",
"http://schemas.xyzcorp.com/wpf/2013")]
[assembly: XmlnsDefinition("http://schemas.xyzcorp.com/wpf/2013",
"Xyz.Databinding")]

Prism / MEF: How to RegisterViewWithRegion Without Hard-Coding the Region Name

We are building a WPF Prism application. We have different developers working on different module projects, and multiple modules are injected into the main Application Shell. The main application is also a separate project. We also want to be able to use the modules in different applications. We do not want to have to name the Regions with the same names in every application.
For instance, say we have a module to be used in two different applications. In one application, its developer may name the module's region "DetailsRegion," and in the other, its developer may name it "ResultsRegion."
Every example I can find registers the View with the Region by hard-coding the region name in the module's class definition:
myRegionManager.RegisterViewWithRegion("RegionNameHere", GetType(ModuleViewType))
What I want to do is put the Region name in the main application's app.config file, and pass this name to the module. Something like this:
In the main Shell Application's app.config:
<Modules>
<SearchModule>
<add key="RegionName" value="SearchRegion" />
</SearchModule>
</Modules>
And in the module's class file:
Dim settings As NameValueCollection = CType(ConfigurationManager.GetSection("Modules/SearchModule"), NameValueCollection)
Dim regionName as string = settings("RegionName")
myRegionManager.RegisterViewWithRegion(regionName, GetType(SearchModuleType)
In a way, this would be the last step to completely decouple the modules from the shell and from each other.
This works perfectly in the views of the module. But I cannot do it in the module's class definition file, as ConfigurationManager is not available at that level.
I can do this by putting the region name in the ApplicatonSettings section of the module's app.config. But this defeats the purpose of being able to store the module in one location to be loaded by multiple applications. It really needs to be in the main application's app.config.
Is there a way to register a module's View with a Region, without hard-coding the name of the Region in the code? We try so hard NOT to hard-code anything. Is it truly necessary here?
As Meleak already mentioned in his comment: Use a static class
namespace Infrastructure
{
public static class RegionNames
{
public const string MainRegion = "MainRegion";
}
}
In your xaml code you can use the region name as follows:
<UserControl
xmlns:Inf="clr-namespace:Infrastructure;assembly=Infrastructure"
xmlns:Regions="clr-namespace:Microsoft.Practices.Prism.Regions;assembly=Microsoft.Practices.Prism">
<ContentControl Regions:RegionManager.RegionName="{x:Static Inf:RegionNames.MainRegion}"/>
</UserControl>
I got it. Turns out I was wrong on one point, and I apologize for that. The parent application's .config settings ARE available at the Module class definition level. One must add the correct references and make the correct Imports (or using) entries. I must have been napping at the keyboard.
In the host application's app.config, add configSection definitions. Here I define sections for two modules:
<configSections>
<sectionGroup name="Modules">
<section name="SearchModule" type="System.Configuration.NameValueSectionHandler" />
<section name="HeaderModule" type="System.Configuration.NameValueSectionHandler"/>
</sectionGroup>
...
</configSections>
In the host application's app.config, add a Modules section, and a subsection for each module:
<Modules>
<SearchModule>
<add key="Region" value="SearchRegion"/>
</SearchModule>
<HeaderModule>
<add key="Region" value="HeaderRegion"/>
</HeaderModule>
</Modules>
In the Module project, add a reference to System.Configuration.dll. Add "Imports" (VB) or "using" (C#) for System.Collections.Specialized and System.Configuration:
VB:
Imports System.Collections.Specialized
Imports System.Configuration
C#:
using System.Collections.Specialized;
using System.Configuration;
In the Initialize method of the Module's Class definition file:
VB:
Public Sub Initialize() Implements Microsoft.Practices.Prism.Modularity.IModule.Initialize
Dim settings As NameValueCollection = CType(ConfigurationManager.GetSection("Modules/SearchModule"), NameValueCollection)
MyRegionManager.RegisterViewWithRegion(settings("Region"), GetType(SearchModuleView))
End Sub
C#:
public void Initialize() : Microsoft.Practices.Prism.Modularity.IModule.Initialize
{
(NameValueCollection)settings = (NameValueCollection)ConfigurationManager.GetSection("Modules/SearchModule");
MyRegionManager.RegisterViewWithRegion(settings["Region"], typeof(SearchModuleView));
}
This, then, registers the View with a Region from entries made in the Host application's app.config. This means one module can be built for multiple host applications, and it can be inserted in a Region of any name in the Host. No need to make changes in compiled code, or to make a separate RegionNames class for each application.
Our application is also built using MVVM architecture. We define the View-Models in the Host application, and expose them to the modules by names defined the app.config using RegionContext or EventAggregator. This now completely decouples the modules from the application, and makes the modules totally reusable in different applications without modification.
Thanks for the input, and I hope this helps someone else in the future.

Silverlight 2 ArgumentException

I have a silverlight 2 app that has an ObservableCollection of a class from a separate assem/lib. When I set my ListBox.ItemsSource on that collection, and run it, I get the error code:
4004 "System.ArgumentException: Value does not fall within the expected range."
Here is part of the code:
public partial class Page : UserControl
{
ObservableCollection<Some.Lib.Owner> ooc;
public Page()
{
ooc = new ObservableCollection<Some.Lib.Owner>();
Some.Lib.Owner o1 = new Some.Lib.Owner() { FirstName = "test1" };
Some.Lib.Owner o2 = new Some.Lib.Owner() { FirstName = "test2" };
Some.Lib.Owner o3 = new Some.Lib.Owner() { FirstName = "test3" };
ooc.Add(o1);
ooc.Add(o2);
ooc.Add(o3);
InitializeComponent();
lb1.ItemsSource = ooc;
}
}
But when I create the Owner class within this same project, everything works fine.
Is there some security things going on behind the scenes? Also, I'm using the generate a html page option and not the aspx option, when I created this Silverlight 2 app.
Are you trying to use a standard class library or a "Silverlight Class Library"?
Because Silverlight 2 uses a subset of the CLR it cannot access standard class libraries that were compiled using the full CLR. To use an external assembly you must create it as a "Silverlight Class Library". This will create a project that only includes the namespaces available to Silverlight and will allow you to reference the assembly within your Silverlight project.
Check out the MSDN article ".NET Framework Class Library for Silverlight" for more info.
It may be because you're not handling a failure in SubmittedChanges(). See http://www.scottleckie.com/2010/04/code-4004-unhandled-error-in-silverlight-application/ for more info
Everything is in one project now.
Yes, but not like you just did it, instead, share, link to the file(s).
For this an old jedi mind trick of Silverlight when there is a need to share common entity code between the app and the service. This is done when the library could not be brought in due to the differences in .Net/CLR.
The trick is to include the file as a link into the other project. Here is how
In the target (Silverlight project) folder which needs the code file, right click and select Add then Existing Item... or shift alt A.
Browse to the location of the origins file(s) found and select the/those file(s).
Once the item(s) have been selected, then on the Add button select the drop down arrow.
Select Add as link to add the file(s) as a link into the folder.
Once done, there is only one copy, but built in two different places.
That will give access to the file as if the file was actually within the project's folder, but the file physically resides elsewhere...and avoids CLR issues.

Resources