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


71

下面的示例使用从代码中获取的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>




Answers:


127

您可以在app.config文件中创建自己的自定义配置部分。有相当一些 教程 身边让你开始。最终,您可能会遇到以下情况:

<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>;

17
我是否缺少某些内容,或者这三个教程中没有一个实际上没有向您展示如何获得元素列表?
Chuu 2014年

@Chuu看看这个页面上的例子:msdn.microsoft.com/en-us/library/...
bonh

1
@Demodave,您可以随时检查我的答案:stackoverflow.com/a/33544322/1955317在这里,我提供了执行Richard所谈论的内容所需的C#代码:)
Squazz

@ Demodave,C#代码在我的答案的教程链接中。
理查德·尼纳伯

当我使用上面的相同代码时,我收到以下错误:无法加载文件或程序集的“命名空间”或其依赖项之一。系统找不到指定的文件
Muni

145

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

App.config

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

C#

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

19
如果您不需要自定义配置部分的开销,那么这是一种快速的方法。这已经足够IMO了。
彼得·凯利

6
那是我的想法...自定义配置部分非常好而且功能非常强大,但是对于简单的字符串数组来说可能会过分杀伤。
亚当·拉尔夫

2
这是我很长一段时间以来一直采用的方式...今天,我将转换为config部分,因为管理列表(这是要加载的插入类的列表,可以根据环境而变化)变得混乱其中有30多个字符串。
驼鹿

1
这个解决方案确实是个好主意。当我要编辑配置时,效果不是很好-删除一些路径。添加不是问题。
Nobody女士

6
啊。很棒的解决方案,但是只需提及一下,您必须添加System.Configuration作为参考(不能仅使用“ using”)才能访问ConfigurationManager。
Jiminion 2014年

33

我喜欢Richard Nienaber的回答,但是正如Chuu指出的那样,它的确并没有告诉我们如何完成Richard所指的解决方案。因此,我选择为您提供我最终完成此操作的方式,最后以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;
    }
}

然后我们创建了acutal 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

@Rhyous我更新了答案以澄清这一点。但这也很大程度上取决于您为案例修改的代码量:)
Squazz

1
@Squazz我想他可能一直在问GreetingWidget应该在哪里定义C#属性。我以为是要添加到GreetingWidgetCollection该类中的?
PseudoPsyche '16

1
@Squazz我提到的是因为我对此也感到困惑。我仍然很难让它为我工作。每当我尝试从集合中读取内容时,都会收到堆栈溢出异常。我假设这个用法,简直是:List<IGreeting> greetingWidgets = new GreetingWidgetCollection().GreetingWidget.All;?另外,将如何sectionapp.config configSections节点中定义需求?是这样的:<section name="greetingWidget" type="WidgetApp.GreetingWidgetCollection, GreetingWidget"/>
伪心理

1
@Squazz是的,那是我要的。在您回应之前,我已经弄清楚了。我知道了
罗尤斯

32

实际上,在BCL中确实有一个鲜为人知的类:CommaDelimitedStringCollectionConverter。它在拥有ConfigurationElementCollection(如Richard的答案)和自己解析字符串(如Adam的答案)之间充当中间立场。

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

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

5
我不确定为什么要创建一个自定义节,然后将其限制为带分隔符的字符串。但是这是我从未见过或不知道的。这是一个有趣的主意,并且有据可查,感谢您提供!
Matt Klinker 2012年

9

遇到了相同的问题,但是以不同的方式解决了它。它可能不是最佳解决方案,但它是一个解决方案。

在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
brichins

考虑到我是4年前写的,我记不起来了。现在,当我看到它时,演员表看起来就不必要了。
ruffen

8

在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()呢?
EasyE

0

谢谢你的问题。但是我已经找到了解决这个问题的方法。首先,我创建了一个方法

    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> 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.