質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.77%

WPF Settings.settings コレクション保存

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 742

score 9

前提

C# WPF 初心者です。
用語の誤用/的外れな質問ありましたらご容赦ください。

表題/Memoを入力して、追加すると、下のリストボックスに内容が追加されるアプリを作成しています。
左側リストボックスを選択すると、右側のリストボックスに内容が表示される。
[イメージ説明]

実現したいこと

アプリ内で追加したリスト内容を保存し、アプリを再起動しても追加した内容が出てくるようにしたいです。
Settings.settingsを使用するのが一番楽かと思いますが、
リストボックスは、ソースコード内ではコレクションをバインドしています。
Settings.settingsにコレクションを追加することはできるのでしょうか?

該当のソースコード

using Prism.Mvvm;
using System;
using System.ComponentModel;
using Reactive.Bindings;
using System.Linq;
using System.Reactive.Linq;
using System.Collections.ObjectModel;

namespace ListBoxデータ保存.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        public ReactiveProperty<string> Txt_Title { get; set; } = new ReactiveProperty<string>();
        public ReactiveProperty<string> Txt_Memo { get; set; } = new ReactiveProperty<string>();
        public ObservableCollection<Task> TaskList { get; set; }


        //コマンド
        public ReactiveCommand AddListCommand { get; private set; }

        public MainWindowViewModel()
        {
            TaskList  = new ObservableCollection<Task>();

            //コマンド生成
            AddListCommand = Txt_Title
                .Select(x => !string.IsNullOrEmpty(x))
                .ToReactiveCommand();

            //コマンド動作定義
            AddListCommand.Subscribe(_ =>
            {
                TaskList.Add(new Task { Title = Txt_Title.Value, Memo = Txt_Memo.Value });

                Txt_Title.Value = "";
                Txt_Memo.Value = "";
            });
        }
    }


    public class Task : BindableBase
    {
        public string Title { get; set; }
        public string Memo { get; set; }

        public ReactiveProperty<bool> IsSelected { get; set; } = new ReactiveProperty<bool>();
    }

}
<Window x:Class="ListBoxデータ保存.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True"
        Title="{Binding Title}" Height="350" Width="525" >

    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
    </Window.Resources>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="2*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Label
                Margin="5"
                VerticalAlignment="Center"
                Content="表題" HorizontalAlignment="Right" Width="34" />
            <TextBox
                Grid.Column="1"
                VerticalAlignment="Center"
                Text="{Binding Txt_Title.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                TextWrapping="Wrap" Margin="10,0,10,0" />
        </Grid>
        <Grid Grid.Column="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <Label
                Margin="5"
                VerticalAlignment="Center"
                Content="Memo" HorizontalAlignment="Right" Width="44" />
            <TextBox
                Grid.Column="1"
                Margin="5"
                VerticalAlignment="Center"
                Text="{Binding Txt_Memo.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                TextWrapping="Wrap" />
            <Button
                Grid.Column="2"
                MinWidth="56"
                Margin="5"
                Command="{Binding AddListCommand}"
                Content="追加" HorizontalAlignment="Left" Width="56" />
        </Grid>

        <Grid Grid.Row="1">
            <ListBox
                x:Name="lst_Title"
                Margin="5"
                ItemsSource="{Binding TaskList}"
                DisplayMemberPath="Title"
                IsSynchronizedWithCurrentItem="True"
                SelectionMode="Extended">

                <ListBox.ItemContainerStyle>
                    <Style TargetType="ListBoxItem">
                        <Setter Property="IsSelected" Value="{Binding IsSelected.Value}" />
                    </Style>
                </ListBox.ItemContainerStyle>
            </ListBox>

        </Grid>

        <Grid Grid.Row="1" Grid.Column="1" >
            <ListBox

                Margin="5"
                ItemsSource="{Binding TaskList}">
                <ListBox.ItemContainerStyle>
                    <Style TargetType="ListBoxItem">
                        <Setter Property="Visibility" Value="{Binding IsSelected.Value, Converter={StaticResource BooleanToVisibilityConverter}}" />
                    </Style>
                </ListBox.ItemContainerStyle>
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock FontWeight="Bold" Text="{Binding Title}" />
                            <TextBlock Text="{Binding Memo}" />
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </Grid>

    </Grid>
</Window>

補足情報(FW/ツールのバージョンなど)

OS:win10
.NET Framework:4.7.2
Visual Studio 2019

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • SurferOnWww

    2020/08/29 10:27 編集

    開発環境(OS, .NET Framework, Visual Studio のバージョンなど)は書けませんか?

    「Settingファイル」というのは Settings.settings のことですか? どのような形で格納したいのか具体的に書けませんか(コレクションとだけ言われても・・・ 前回の質問を読むというのも負担が大きすぎますし・・・)?

    キャンセル

  • tomiieee

    2020/08/29 11:44

    情報不足申し訳ありません。
    内容を修正いたしましたので、ご確認ください。

    キャンセル

  • SurferOnWww

    2020/08/29 11:49 編集

    「どのような形で格納したいのか具体的に書けませんか」に対する回答がどこに書かれているのか分からないのですが・・・

    例えばですが、もしシリアライズして JSON 文字列にできるなら、それを Settings.settings に文字列として保存し、使うときは JSON 文字列 ⇒ オブジェクト(コレクション)にデシリアライスするというようなことは考えられないのでしょうか?

    キャンセル

回答 1

checkベストアンサー

+1

Settings.Designer.csを編集すれば可能です。
が、Settings.settingsのデザイナをいじったりすると消えやすいので、別に書いたほうが安全です(partialなので)

TaskListMainWindowViewModel経由になっている深い意味はありません。

<Window
  x:Class="Questions288214.Views.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
  xmlns:prism="http://prismlibrary.com/"
  xmlns:properties="clr-namespace:Questions288214.Properties"
  Width="{Binding Width, Mode=TwoWay, Source={x:Static properties:Settings.Default}}"
  Height="{Binding Height, Mode=TwoWay, Source={x:Static properties:Settings.Default}}"
  prism:ViewModelLocator.AutoWireViewModel="True"
  Left="{Binding Left, Mode=TwoWay, Source={x:Static properties:Settings.Default}}"
  Top="{Binding Top, Mode=TwoWay, Source={x:Static properties:Settings.Default}}">
  <Window.Resources>
    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
    <Style TargetType="Label">
      <Setter Property="Margin" Value="5" />
    </Style>
    <Style TargetType="TextBox">
      <Setter Property="Margin" Value="5" />
      <Setter Property="VerticalAlignment" Value="Center" />
      <Setter Property="TextWrapping" Value="Wrap" />
    </Style>
  </Window.Resources>
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="Closing">
      <prism:InvokeCommandAction Command="{Binding WindowClosingCommand}" />
    </i:EventTrigger>
  </i:Interaction.Triggers>
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition />
      <ColumnDefinition Width="2*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition />
    </Grid.RowDefinitions>

    <Grid>
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
      </Grid.ColumnDefinitions>
      <Label VerticalAlignment="Center" Content="表題" />
      <TextBox Grid.Column="1" Text="{Binding Txt_Title.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    </Grid>

    <Grid Grid.Column="1">
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition />
        <ColumnDefinition Width="Auto" />
      </Grid.ColumnDefinitions>
      <Label VerticalAlignment="Center" Content="Memo" />
      <TextBox Grid.Column="1" Text="{Binding Txt_Memo.Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
      <Button
        Grid.Column="2"
        MinWidth="56"
        Margin="5"
        Command="{Binding AddListCommand}"
        Content="追加" />
    </Grid>

    <ListBox
      x:Name="lst_Title"
      Grid.Row="1"
      Margin="5"
      DisplayMemberPath="Title"
      IsSynchronizedWithCurrentItem="True"
      ItemsSource="{Binding TaskList}"
      SelectionMode="Multiple">
      <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
          <Setter Property="IsSelected" Value="{Binding IsSelected.Value}" />
        </Style>
      </ListBox.ItemContainerStyle>
    </ListBox>

    <ListBox
      Grid.Row="1"
      Grid.Column="1"
      Margin="5"
      ItemsSource="{Binding TaskList}">
      <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
          <Setter Property="Visibility" Value="{Binding IsSelected.Value, Converter={StaticResource BooleanToVisibilityConverter}}" />
        </Style>
      </ListBox.ItemContainerStyle>
      <ListBox.ItemTemplate>
        <DataTemplate>
          <StackPanel>
            <TextBlock FontWeight="Bold" Text="{Binding Title}" />
            <TextBlock Text="{Binding Memo}" />
          </StackPanel>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
  </Grid>
</Window>
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using Prism.Mvvm;
using Reactive.Bindings;

namespace Questions288214.ViewModels
{
    public class MainWindowViewModel : BindableBase
    {
        public ReactiveProperty<string> Txt_Title { get; set; } = new ReactiveProperty<string>();
        public ReactiveProperty<string> Txt_Memo { get; set; } = new ReactiveProperty<string>();

        public ObservableCollection<Task> TaskList
        {
            get => Properties.Settings.Default.TaskList;
            private set => Properties.Settings.Default.TaskList = value;
        }

        public ReactiveCommand AddListCommand { get; }
        public ReactiveCommand WindowClosingCommand { get; } = new ReactiveCommand();


        public MainWindowViewModel()
        {
            if(TaskList == null) TaskList = new ObservableCollection<Task>();

            AddListCommand = Txt_Title
                .Select(x => !string.IsNullOrEmpty(x))
                .ToReactiveCommand();

            AddListCommand.Subscribe(() =>
            {
                TaskList.Add(new Task { Title = Txt_Title.Value, Memo = Txt_Memo.Value, });
                Txt_Title.Value = "";
                Txt_Memo.Value = "";
            });

            WindowClosingCommand.Subscribe(() => Properties.Settings.Default.Save());
        }
    }

    public class Task
    {
        public string Title { get; set; }
        public string Memo { get; set; }
        public ReactiveProperty<bool> IsSelected { get; set; } = new ReactiveProperty<bool>();
    }
}

// 別ファイルに書けば安心
namespace Questions288214.Properties
{
    using Questions288214.ViewModels;

    internal sealed partial class Settings
    {
        [System.Configuration.UserScopedSetting()]
        [System.Diagnostics.DebuggerNonUserCode()]
        public ObservableCollection<Task> TaskList
        {
            get => (ObservableCollection<Task>)this[nameof(TaskList)];
            set => this[nameof(TaskList)] = value;
        }
    }
}

ウィンドウ位置保存用は普通にデザイナで入れたとする

//------------------------------------------------------------------------------
// <auto-generated>
//     このコードはツールによって生成されました。
//     ランタイム バージョン:4.0.30319.42000
//
//     このファイルへの変更は、以下の状況下で不正な動作の原因になったり、
//     コードが再生成されるときに損失したりします。
// </auto-generated>
//------------------------------------------------------------------------------

namespace Questions288214.Properties {


    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {

        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));

        public static Settings Default {
            get {
                return defaultInstance;
            }
        }

        [global::System.Configuration.UserScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("NaN")]
        public double Top {
            get {
                return ((double)(this["Top"]));
            }
            set {
                this["Top"] = value;
            }
        }

        [global::System.Configuration.UserScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("NaN")]
        public double Left {
            get {
                return ((double)(this["Left"]));
            }
            set {
                this["Left"] = value;
            }
        }

        [global::System.Configuration.UserScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("525")]
        public double Width {
            get {
                return ((double)(this["Width"]));
            }
            set {
                this["Width"] = value;
            }
        }

        [global::System.Configuration.UserScopedSettingAttribute()]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Configuration.DefaultSettingValueAttribute("350")]
        public double Height {
            get {
                return ((double)(this["Height"]));
            }
            set {
                this["Height"] = value;
            }
        }
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/08/30 08:17

    解決できました。
    ご回答ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.77%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る