关于C#:如何从WPF中的app.config获取List 值集合?

How to get a List<string> collection of values from app.config in WPF?

下面的示例用我从代码中获得的backupdirectory列表填充itemscontrol。

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

XAML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<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>

代码落后:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
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:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 
   
    <!--
     
     
     
     
    </add>-->
  </appSettings>
</configuration>


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

App.CONFIG

1
 

C.*

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


您可以在app.config文件中创建自己的自定义配置节。有很多教程可以让你开始学习。最终,您可能会得到这样的结果:

1
2
3
4
5
6
7
8
9
<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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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配置部分,如下所示:

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


我喜欢理查德·尼纳伯的回答,但正如邱指出的那样,这并不能说明如何完成理查德所说的解决方案。因此,我选择了提供给你我最后这样做的方式,以理查德所说的结果结束。

解决方案

在本例中,我创建了一个问候语小部件,需要知道它必须问候哪些选项。这可能是一个过度工程化的解决方案,因为我也在为未来可能的小部件创建一个容器。

首先,我建立了我的收集处理不同的问候

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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问候元素和它的接口

(您可以省略接口,这正是我一直在做的方式。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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定义为ConfigurationPropertygreetingWidget,以便在生成的XML中使用"greetingwidget"作为容器。

1
2
3
4
5
6
7
8
9
10
11
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

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
   <widgets>
       <greetingWidget>
           
           
           
           ...
           
           
       </greetingWidget>
    </widgets>
</configuration>

你可以这样称呼它

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


实际上,在BCL中有一个非常鲜为人知的类用于此目的:commadelimitedStringCollectionConverter。它是一个中间地带,介于拥有一个ConfigurationElementCollection(如理查德的答案)和自己解析字符串(如亚当的答案)之间。

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

1
2
3
4
5
6
7
8
9
public class MySection : ConfigurationSection
{
    [ConfigurationProperty("MyStrings")]
    [TypeConverter(typeof(CommaDelimitedStringCollectionConverter))]
    public CommaDelimitedStringCollection MyStrings
    {
        get { return (CommaDelimitedStringCollection)base["MyStrings"]; }
    }
}

然后您可以有一个app.config,如下所示:

1
2
3
4
5
6
7
<?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>

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

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


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

在App.CONFIG中:

1
 

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

1
2
3
4
5
6
7
8
        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();
        }

对于任何阅读本文的人,我都同意创建您自己的自定义配置节是更干净、更安全的,但是对于小项目,如果您需要快速的东西,这可能会解决它。


在App.config:

1
 

C中:

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