为什么我的数据绑定用户设置没有保存?

时间:2016-08-19 20:08:26

标签: c# wpf data-binding observablecollection settings

我有以下内容:

主窗口:

<Window x:Class="TestApp.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:TestApp"
        xmlns:settings="clr-namespace:TestApp.Settings"
        mc:Ignorable="d" Title="MainWindow" Height="350" Width="525">
    <Grid>
        <ItemsControl>
            <ItemsControl.ItemsSource>
                <Binding>
                    <Binding.Source>
                        <CollectionViewSource Source="{Binding Source={x:Static settings:CustomSettings.Default}, Path=coll}" />
                    </Binding.Source>
                </Binding>
            </ItemsControl.ItemsSource>

            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="100" />
                            <ColumnDefinition Width="100" />
                        </Grid.ColumnDefinitions>

                        <TextBox Text="{Binding Name}" />
                        <Button Grid.Column="1" Click="Button_Click" />
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>

代码隐藏:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;

namespace TestApp
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            if (Settings.CustomSettings.Default.coll == null)
            {
                Settings.CustomSettings.Default.coll = new ObservableCollection<BasicClass>();
                Settings.CustomSettings.Default.coll.Add(new BasicClass("String1"));
                Settings.CustomSettings.Default.coll.Add(new BasicClass("String2"));
                Settings.CustomSettings.Default.coll.Add(new BasicClass("String3"));
            }

            Settings.CustomSettings.Default.Save();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Settings.CustomSettings.Default.Save();

            foreach (BasicClass item in Settings.CustomSettings.Default.coll)
            {
                MessageBox.Show(item.Name);
            }
        }
    }

    public class BasicClass : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        private string name;
        public string Name
        {
            get { return name; }
            set
            {
                if (value != name)
                {
                    name = value;
                    NotifyPropertyChanged("Name");
                }
            }
        }

        public BasicClass() { }

        public BasicClass(string Name)
        {
            this.Name = Name;
        }
    }
}

设定:

using System.Collections.ObjectModel;
using System.Configuration;
using System.Diagnostics;

namespace TestApp.Settings
{
    internal sealed partial class CustomSettings : ApplicationSettingsBase
    {
        private static CustomSettings defaultInstance = ((CustomSettings)(Synchronized(new CustomSettings())));

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

        [UserScopedSetting()]
        [DebuggerNonUserCode()]
        public ObservableCollection<BasicClass> coll
        {
            get
            {
                return ((ObservableCollection<BasicClass>)(this["coll"]));
            }
            set
            {
                this["coll"] = value;
            }
        }
    }
}

工作原理:

该应用提供了三个控件,包括TextBoxButton。这些是ItemsControl的一部分,其来源绑定到ObservableCollection<BasicClass>类型的用户设置'coll'。 BasicClass有一个属性“名称”,它通过数据绑定显示在TextBox中。

预期行为:

我更改TextBox中的文字,然后点击相应的Button。然后,这将在“coll”中保存新值,然后呈现MessageBox序列,证明这确实已经改变。我重启了应用程序,我的值显示了新保存的值。

实际行为:

我更改了文字,单击ButtonMessageBox序列显示该值现在存储在用户设置中(因此应该已保存)。但是,当我重新启动应用程序时,我会看到原始值,而不是已保存的值。

异常(?):

如果我单击按钮两次而不是一次(通过MessageBox序列两次),当我重新启动时,该值现已成功保存。

1 个答案:

答案 0 :(得分:1)

编辑(以下原始答案):

虽然我怀疑在ObservableCollection 的子类上实现IBindableComponent,但我不推荐它。如果您只是存储字符串,System.Collections.Specialized.StringCollection可能会帮助您。

但总的来说,每次发生变化时,我都认为更新应用程序设置没有多大意义。而是在应用程序启动期间将它们加载到视图模型(例如ObservableCollection)中,并在关闭时将它们移回应用程序设置。这样,只需要对值进行反序列化和序列化一次。

您在评论中提到的关于将值设置为自身的效果(看起来很有效),因为在设置列表时会重新序列化列表。看来,ApplicationSettingsBase正在存储您提供的每个值的序列化副本,因此无法对原始对象的更改做出反应。再次提供该值时,它将使用对象的新状态的序列化版本覆盖其副本。但是,如果每次用户进行更改时序列化列表,一旦列表变长,它将影响应用程序的性能。

这对你来说也很有趣: How to store int[] array in application Settings

似乎没有必要自己继承ApplicationSettingsBase,请参阅https://social.msdn.microsoft.com/Forums/en-US/4e299ed8-8e3a-408e-b900-eb6738fe0775/persist-and-restore-application-state?forum=wpf

ORIGINAL:

我不熟悉ApplicationSettingsBase,但这可能有助于https://msdn.microsoft.com/en-in/library/8eyb2ct1(en-us).aspx

  

您只能将应用程序设置绑定到支持IBindableComponent接口的组件。此外,组件必须为特定绑定属性实现更改事件,或通过INotifyPropertyChanged接口通知属性已更改的应用程序设置。 如果组件未实现IBindableComponent并且您通过Visual Studio进行绑定,则绑定属性将首次设置,但不会更新。如果组件实现了IBindableComponent但不支持属性更改通知,则在更改属性时,绑定将不会在设置文件中更新。

这似乎与仅存储设置的序列化版本这一事实有关,因此无法识别列表中的更改(因为引用本身不会更改)。请注意,单击按钮完全替换列表后,将存储新列表的值。