22144


Settings.settingsでは名前や値を設定出来ます。  
そこで設定した値を取得・格納は出来ているのですが、  
新規項目をコードから追加する方法が不明です。

なぜSettings.settingsで予め項目を用意せずに  
コードから追加したいのかと言うと、  
理由は単純で大量の項目を作成する必要があるからです。  
それならユーザーが入力したデータを元に項目追加した方がスマートだと思いました。

どなたかご存知の方がいればご教授願います。


CC BY-SA 3.0
2016-02-14T11:19:37.437
22068
2016-02-16T03:48:18.347
2016-02-14T13:28:32.480
76
13969
question
c# visual-studio
ユーザー設定の新規項目をコード中から追加したい


一般的には無理です。  
実行時に`app.config`相当のファイルを入出力する方法がないわけではありませんが、`ApplicationSettings`および`UserSettings`は`Settings.settings`のデザイナーで生成されたクラスから使用することが前提になっています。アプリケーションスコープの設定変更は管理者権限も絡むため、他のストレージに保存することを推奨します。

単に項目が膨大であるというだけなのであれば、`app.config`または`Settings.setting`にExcel等でソースコードを生成して貼り付け、`Settings.designer.cs`に反映させるという方法があります。また`.settings`ファイルを複数に分割することもできます。

また項目自体を規則的に自動生成したいのであれば、T4テキストテンプレートでパーシャルクラスを作成するという方法があります。例えばSettings.ttというファイルをプロジェクトに追加し、カスタムツールに`TextTemplatingFileGenerator`と指定して以下のコードを記述すると、`Settings.setting`にプロパティを追加した場合と同様に動作します。

[code]

    <#@ template language="C#" debug="True" #>
    <#@ output extension="cs" #> 
    
    <#
    
        var props = new []
        {
            new { Name = "Setting1", Type = "string", Default = "hoge", IsUserScoped = false },
            new { Name = "Setting2", Type = "int", Default = "123", IsUserScoped = false }
        };
    
    #>
    
    namespace /* TODO:名前空間の設定 */Properties
    {
        partial class Settings
        {
    <#
    
    foreach (var p in props)
    {
    #>
    
            [global::System.Configuration.<#= p.IsUserScoped ? "User" : "Application" #>ScopedSettingAttribute()]
            [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
            [global::System.Configuration.DefaultSettingValueAttribute("<#= p.Default #>")]
            public <#= p.Type #> <#= p.Name #> {
                get {
                    return ((<#= p.Type #>)(this["<#= p.Name #>"]));
                }
                set {
                    this["<#= p.Name #>"] = value;
                }
            }
    
    <#
    }
    
    #>
        }
    }
    
[/code]


CC BY-SA 3.0
2016-02-14T11:47:32.737
22069
2016-02-14T12:10:59.230
2016-02-14T12:10:59.230
5750
5750
22068
answer


大量の項目を管理する場合には、無理にSettingsを利用するよりも素直にDBを使ったり[クラスをシリアル化](https://msdn.microsoft.com/ja-
jp/library/4abbf6k0%28v=vs.120%29.aspx)して設定ファイルのように扱う方が柔軟に対応できると思われます。  
類似の質問への[回答](https://stackoverflow.com/a/7608985)が本家stackoverflowに載っていましたが、回答者自身もあまり推奨できない荒業だというニュアンスで記述しています。

手軽に任意件数のデータをSettingsで扱いたいのならば、Settingsにカンマ区切りの文字列を設定する方法も使えます。(カンマ入力を制限したり置換したりする必要はありますが…)

以下はSettingsに`Locality`を`string`型で定義し、`FruitsCollection`を`System.Collections.Specialized.StringCollection`型で定義して使用するWPFアプリケーションのサンプルコードです。  
コンボボックスに任意個の産地と果物を設定し、連結して表示します。  
また、コンボボックスの下に配置されたテキストボックスに「Aomori」などを入力してフォーカスを離すと、産地の表示を変更できます。

※あらかじめSettingsに`Locality`を`string`型で定義し、`FruitsCollection`を`System.Collections.Specialized.StringCollection`型で定義してください。

MainWindow.xaml.cs

[code]

    using System;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Windows;
    using System.Windows.Controls;
    using WpfApplication1.Properties;
    
    namespace WpfApplication1
    {
        public partial class MainWindow : Window, INotifyPropertyChanged
        {
            public string ItemName
            {
                get
                {
                    var locality = (KeyValuePair<string, string>)(CmbLocality.SelectedItem ?? new KeyValuePair<string, string>());
                    var fruits = (KeyValuePair<string, string>)(CmbFruits.SelectedItem ?? new KeyValuePair<string, string>());
                    return string.Concat(locality.Value, fruits.Value);
                }
            }
    
            public string Locality
            {
                get { return (CmbLocality.SelectedValue ?? "").ToString(); }
                set
                {
                    CmbLocality.SelectedValue = value;
                    OnPropertyChanged("ItemName");
                }
            }
    
            public string Fruits
            {
                get { return (CmbFruits.SelectedValue ?? "").ToString(); }
                set
                {
                    CmbFruits.SelectedValue = value;
                    OnPropertyChanged("ItemName");
                }
            }
    
            public MainWindow()
            {
                InitializeComponent();
    
                //サンプルなのでコードで追加
                Settings.Default.Locality = @"Aomori,青森
    Iwate,岩手
    Akita,秋田";
                var c = new StringCollection();
                c.AddRange(new string[] { "Apple,りんご", "Orange,みかん", "Peach,もも" });
                Settings.Default.FruitsCollection = c;
    
                Loaded += MainWindow_Loaded;
                CmbLocality.SelectionChanged += CmbLocality_SelectionChanged;
                CmbFruits.SelectionChanged += CmbFruits_SelectionChanged;
            }
    
            private void MainWindow_Loaded(object sender, RoutedEventArgs e)
            {
                PnlRoot.DataContext = this;
    
                //産地をコンボに格納
                var localities = Settings.Default.Locality.Split(new string[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries);
                CmbLocality.ItemsSource = GetDictionaryFromCsv(localities);
                //果物をコンボに格納
                var fruits = new string[Settings.Default.FruitsCollection.Count];
                Settings.Default.FruitsCollection.CopyTo(fruits, 0);
                CmbFruits.ItemsSource = GetDictionaryFromCsv(fruits);
            }
    
            private Dictionary<string, string> GetDictionaryFromCsv(IEnumerable<string> lines)
            {
                var dic = new Dictionary<string, string>();
                foreach (var line in lines)
                {
                    if (string.IsNullOrWhiteSpace(line)) continue;
                    var ss = line.Split(',');
                    dic.Add(ss[0].Trim(), ss[1].Trim());
                }
                return dic;
            }
    
            private void CmbLocality_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                OnPropertyChanged("Locality");
                OnPropertyChanged("ItemName");
            }
    
            private void CmbFruits_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                OnPropertyChanged("Fruits");
                OnPropertyChanged("ItemName");
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
            protected virtual void OnPropertyChanged(string propertyName)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    
[/code]

MainWindow.xaml

[code]

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApplication1"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <Grid Name="PnlRoot">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" MinWidth="50"/>
                <ColumnDefinition Width="Auto" MinWidth="50"/>
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <ComboBox Grid.Row="0" Grid.Column="0" Name="CmbLocality" SelectedValuePath="Key" DisplayMemberPath="Value"/>
            <ComboBox Grid.Row="0" Grid.Column="1" Name="CmbFruits" SelectedValuePath="Key" DisplayMemberPath="Value"/>
            <TextBox Grid.Row="0" Grid.Column="2" Text="{Binding ItemName, Mode=OneWay}"/>
            <TextBox Grid.Row="1" Grid.Column="0" Text="{Binding Locality, Mode=TwoWay}"/>
            <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Fruits, Mode=TwoWay}"/>
        </Grid>
    </Window>
    
[/code]


CC BY-SA 3.0
2016-02-16T03:09:07.083
22144
2016-02-16T03:48:18.347
2017-05-23T12:38:56.083
-1
9820
22068
answer