如何从 WPF 中的 app.config 获取 List<string> 值集合?

Posted

技术标签:

【中文标题】如何从 WPF 中的 app.config 获取 List<string> 值集合?【英文标题】:How to get a List<string> collection of values from app.config in WPF? 【发布时间】:2010-12-19 05:51:45 【问题描述】:

以下示例使用我从代码中获得的 BackupDirectories 列表填充 ItemsControl

如何更改此设置以便从 app.config 文件中获得相同的信息?

XAML:

<Window x:Class="TestReadMultipler2343.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Grid Margin="10">
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="120"/>
            <ColumnDefinition Width="160"/>
        </Grid.ColumnDefinitions>
        <TextBlock 
            Grid.Row="0"
            Grid.Column="0"
            Text="Title:"/>
        <TextBlock 
            Grid.Row="0"
            Grid.Column="1" 
            Text="Binding Title"/>
        <TextBlock 
            Grid.Row="1"
            Grid.Column="0"
            Text="Backup Directories:"/>
        <ItemsControl 
            Grid.Row="1"
            Grid.Column="1"
            ItemsSource="Binding BackupDirectories"/>
    </Grid>
</Window>

代码隐藏:

using System.Collections.Generic;
using System.Windows;
using System.Configuration;
using System.ComponentModel;

namespace TestReadMultipler2343

    public partial class Window1 : Window, INotifyPropertyChanged
    

        #region ViewModelProperty: Title
        private string _title;
        public string Title
        
            get
            
                return _title;
            

            set
            
                _title = value;
                OnPropertyChanged("Title");
            
        
        #endregion

        #region ViewModelProperty: BackupDirectories
        private List<string> _backupDirectories = new List<string>();
        public List<string> BackupDirectories
        
            get
            
                return _backupDirectories;
            

            set
            
                _backupDirectories = value;
                OnPropertyChanged("BackupDirectories");
            
        
        #endregion

        public Window1()
        
            InitializeComponent();
            DataContext = this;

            Title = ConfigurationManager.AppSettings.Get("title");

            GetBackupDirectoriesInternal();
        

        void GetBackupDirectoriesInternal()
        
            BackupDirectories.Add(@"C:\test1");
            BackupDirectories.Add(@"C:\test2");
            BackupDirectories.Add(@"C:\test3");
            BackupDirectories.Add(@"C:\test4");
        

        void GetBackupDirectoriesFromConfig()
        
            //BackupDirectories = ConfigurationManager.AppSettings.GetValues("backupDirectories");
        


        #region INotifiedProperty Block
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        
            PropertyChangedEventHandler handler = PropertyChanged;

            if (handler != null)
            
                handler(this, new PropertyChangedEventArgs(propertyName));
            
        
        #endregion

    

app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="title" value="Backup Tool" />
    <!--<add key="backupDirectories">
      <add value="C:\test1"/>
      <add value="C:\test2"/>
      <add value="C:\test3"/>
      <add value="C:\test4"/>
    </add>-->
  </appSettings>
</configuration>

【问题讨论】:

最简单的解决方案是使用System.Collections.Specialized.StringCollection:回答问题:Store String Array In appSettings? 看看我的新答案***.com/a/29487138/105445 How to implement a ConfigurationSection with a ConfigurationElementCollection的可能重复 似乎在***.com/a/32637544/69663也得到了回答 【参考方案1】:

您可以将它们用分号分隔在单个值中,例如

App.config

<add key="paths" value="C:\test1;C:\test2;C:\test3" />

C#

var paths = new List<string>(ConfigurationManager.AppSettings["paths"].Split(new char[]  ';' ));

【讨论】:

如果您不需要自定义配置部分的开销,这是一种快速的方法。这已经足够 IMO 了。 这是我的想法...自定义配置部分非常好用且功能强大,但对于简单的字符串数组来说可能有点过分了。 这是我长期以来一直这样做的方式......今天我正在转换为配置部分,因为管理我的列表(它是要加载的插件类列表,可以更改取决于环境)已经被 30 多个字符串弄乱了。 这个解决方案真是个好主意。当我想编辑配置时不太好 - 删除一些路径。添加不是问题。 呃。很好的解决方案,但只需要提及您必须添加 System.Configuration 作为参考(不能只使用“使用”)才能访问 ConfigurationManager。【参考方案2】:

您可以在 app.config 文件中创建自己的自定义配置部分。有很多 few tutorials around 可以帮助您入门。最终,您可能会得到这样的结果:

<configSections>
    <section name="backupDirectories" type="TestReadMultipler2343.BackupDirectoriesSection, TestReadMultipler2343" />
  </configSections>

<backupDirectories>
   <directory location="C:\test1" />
   <directory location="C:\test2" />
   <directory location="C:\test3" />
</backupDirectories>

为了补充 Richard 的回答,这是您可以与他的示例配置一起使用的 C#:

using System.Collections.Generic;
using System.Configuration;
using System.Xml;

namespace TestReadMultipler2343

    public class BackupDirectoriesSection : IConfigurationSectionHandler
    
        public object Create(object parent, object configContext, XmlNode section)
        
            List<directory> myConfigObject = new List<directory>();

            foreach (XmlNode childNode in section.ChildNodes)
            
                foreach (XmlAttribute attrib in childNode.Attributes)
                
                    myConfigObject.Add(new directory()  location = attrib.Value );
                
            
            return myConfigObject;
        
    

    public class directory
    
        public string location  get; set; 
    

然后您可以访问backupDirectories配置部分,如下所示:

List<directory> dirs = ConfigurationManager.GetSection("backupDirectories") as List<directory>;

【讨论】:

是我遗漏了什么,还是这三个教程都没有真正向您展示如何拥有一个元素列表? @Chuu 查看此页面上的示例:msdn.microsoft.com/en-us/library/… @Demodave 你可以随时查看我的答案:***.com/a/33544322/1955317 这里我提供了执行 Richard 所说的所需的 C# 代码 :) @Demodave,C# 代码在我回答的教程链接中。 当我使用上面相同的代码时,我收到以下错误:无法加载文件或程序集“命名空间”或其依赖项之一。系统找不到指定的文件【参考方案3】:

实际上,BCL 中有一个鲜为人知的类用于此目的:CommaDelimitedStringCollectionConverter。它充当拥有ConfigurationElementCollection(如理查德的回答)和自己解析字符串(如亚当的回答)之间的某种中间立场。

例如,您可以编写以下配置部分:

public class MySection : ConfigurationSection

    [ConfigurationProperty("MyStrings")]
    [TypeConverter(typeof(CommaDelimitedStringCollectionConverter))]
    public CommaDelimitedStringCollection MyStrings
    
        get  return (CommaDelimitedStringCollection)base["MyStrings"]; 
    

然后你可以有一个如下所示的 app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="foo" type="ConsoleApplication1.MySection, ConsoleApplication1"/>
  </configSections>
  <foo MyStrings="a,b,c,hello,world"/>
</configuration>

最后,您的代码将如下所示:

var section = (MySection)ConfigurationManager.GetSection("foo");
foreach (var s in section.MyStrings)
    Console.WriteLine(s); //for example

【讨论】:

我不知道你为什么要创建一个自定义部分,然后将其限制为一个分隔字符串;然而,这是我从未见过或不知道的。这是一个有趣的想法并且有据可查,感谢提供!【参考方案4】:

有同样的问题,但以不同的方式解决了。它可能不是最好的解决方案,但它是一个解决方案。

在 app.config 中:

<add key="errorMailFirst" value="test@test.no"/>
<add key="errorMailSeond" value="krister@tets.no"/>

然后在我的配置包装类中,我添加了一个搜索键的方法。

        public List<string> SearchKeys(string searchTerm)
        
            var keys = ConfigurationManager.AppSettings.Keys;
            return keys.Cast<object>()
                       .Where(key => key.ToString().ToLower()
                       .Contains(searchTerm.ToLower()))
                       .Select(key => ConfigurationManager.AppSettings.Get(key.ToString())).ToList();
        

对于阅读本文的任何人,我同意创建自己的自定义配置部分更清洁、更安全,但对于需要快速操作的小型项目,这可能会解决问题。

【讨论】:

...除了您可以直接查询AppSettings.Keys 以获得字符串相等性之外,为什么要转换为object,然后再转换回string 考虑到这是我 4 年前写的,我不记得了。当我现在看到它时,演员看起来没有必要。【参考方案5】:

我喜欢 Richard Nienaber 的回答,但正如 Chuu 指出的那样,它确实没有告诉 如何 完成 Richard 所说的解决方案。 因此,我选择向您提供我最终这样做的方式,以理查德所说的结果结束。

解决方案

在这种情况下,我正在创建一个问候小部件,它需要知道它必须在哪些选项中打招呼。这可能是针对 OP 问题的过度设计的解决方案,因为我还在为可能的未来小部件创建一个容器。

首先我设置我的集合来处理不同的问候

public class GreetingWidgetCollection : System.Configuration.ConfigurationElementCollection

    public List<IGreeting> All  get  return this.Cast<IGreeting>().ToList();  

    public GreetingElement this[int index]
    
        get
        
            return base.BaseGet(index) as GreetingElement;
        
        set
        
            if (base.BaseGet(index) != null)
            
                base.BaseRemoveAt(index);
            
            this.BaseAdd(index, value);
        
    

    protected override ConfigurationElement CreateNewElement()
    
        return new GreetingElement();
    

    protected override object GetElementKey(ConfigurationElement element)
    
        return ((GreetingElement)element).Greeting;
    

然后我们创建实际的问候元素和它的界面

(界面可以省略,我一直都是这样。)

public interface IGreeting

    string Greeting  get; set; 


public class GreetingElement : System.Configuration.ConfigurationElement, IGreeting

    [ConfigurationProperty("greeting", IsRequired = true)]
    public string Greeting
    
        get  return (string)this["greeting"]; 
        set  this["greeting"] = value; 
    

greetingWidget 属性,以便我们的配置理解集合

我们将集合 GreetingWidgetCollection 定义为 ConfigurationProperty greetingWidget,以便我们可以在生成的 XML 中使用“greetingWidget”作为我们的容器。

public class Widgets : System.Configuration.ConfigurationSection

    public static Widgets Widget => ConfigurationManager.GetSection("widgets") as Widgets;

    [ConfigurationProperty("greetingWidget", IsRequired = true)]
    public GreetingWidgetCollection GreetingWidget
    
        get  return (GreetingWidgetCollection) this["greetingWidget"]; 
        set  this["greetingWidget"] = value; 
    

生成的 XML

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <widgets>
       <greetingWidget>
           <add greeting="Hej" />
           <add greeting="Goddag" />
           <add greeting="Hello" />
           ...
           <add greeting="Konnichiwa" />
           <add greeting="Namaskaar" />
       </greetingWidget>
    </widgets>
</configuration>

你会这样称呼它

List<GreetingElement> greetings = Widgets.GreetingWidget.All;

【讨论】:

GreetingWidget 属性在哪里? @Rhyous 我更新了我的答案以澄清这一点。但这也很大程度上取决于您为您的案例修改了多少代码:) @Squazz 我想他可能一直在问应该在哪里定义 C# GreetingWidget 属性。我假设打算将其添加到 GreetingWidgetCollection 类中? @Squazz 我之所以提到这一点,是因为我对此也感到困惑。不过,我仍然无法让它为我工作。每次我尝试从集合中读取数据时,都会出现堆栈溢出异常。我假设它的用法只是:List&lt;IGreeting&gt; greetingWidgets = new GreetingWidgetCollection().GreetingWidget.All;?另外,section 需要如何在 app.config configSections 节点中定义?像这样:&lt;section name="greetingWidget" type="WidgetApp.GreetingWidgetCollection, GreetingWidget"/&gt;? @Squazz 是的,这就是我要问的。在你回复之前我就想通了。我得到了它的工作。【参考方案6】:

在 App.config 中:

<add key="YOURKEY" value="a,b,c"/>

在 C# 中:

string[] InFormOfStringArray = ConfigurationManager.AppSettings["YOURKEY"].Split(',').Select(s => s.Trim()).ToArray();
List<string> list = new List<string>(InFormOfStringArray);

【讨论】:

非常好,但我有一个问题,我对将这些值放入数组然后将它们放入列表的原因感到有些困惑,而您可以简单地使用 .ToList() 代替? 【参考方案7】:

感谢您的提问。但我已经找到了解决这个问题的方法。 一开始,我创建了一个方法

    public T GetSettingsWithDictionary<T>() where T:new()
    
        IConfigurationRoot _configurationRoot = new ConfigurationBuilder()
        .AddXmlFile($"Assembly.GetExecutingAssembly().Location.config", false, true).Build();

        var instance = new T();
        foreach (var property in typeof(T).GetProperties())
        
            if (property.PropertyType == typeof(Dictionary<string, string>))
            
                property.SetValue(instance, _configurationRoot.GetSection(typeof(T).Name).Get<Dictionary<string, string>>());
                break;
            

        
        return instance;
    

然后我用这个方法生成了一个类的实例

var connStrs = GetSettingsWithDictionary<AuthMongoConnectionStrings>();

我有下一个类的声明

public class AuthMongoConnectionStrings

    public Dictionary<string, string> ConnectionStrings  get; set; 

我将我的设置存储在 App.config 中

<configuration>    
  <AuthMongoConnectionStrings
  First="first"
  Second="second"
  Third="33" />
</configuration> 

【讨论】:

【参考方案8】:

在搜索如何从appsettings.json获取列表时发现此线程。


  "foo": 
    "bar": [
      "1",
      "2",
      "3"
    ]
  

你可以这样做:

Configuration.GetSection("foo:bar").Get<List<string>>()

来源:

https://***.com/a/42296371/3850405

【讨论】:

你不能真正将它用于 app.config,因为应用程序配置使用 xml 而不是 json。而且创建自定义json文件效率不高

以上是关于如何从 WPF 中的 app.config 获取 List<string> 值集合?的主要内容,如果未能解决你的问题,请参考以下文章

在WPF程序运行时动态修改app.config文件后如何立即生效

WPF Core - App.config 中的连接字符串不起作用

从 c# 中的 app.config 获取 ConnectionString [重复]

WPF程序中App.Config文件的读与写

如何在 WPF 应用程序中使用 App.config 进行 log4net 配置

从 VB.Net 中的 app.config 获取动态更新的连接字符串