【问题标题】:Why aren't my data-bound user settings saving?为什么我的数据绑定用户设置没有保存?
【发布时间】:2016-12-27 01:58:34
【问题描述】:

我有以下几点:

主窗口:

<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&lt;BasicClass&gt; 类型的用户设置“coll”。 BasicClass 有一个属性“名称”,它通过数据绑定出现在 TextBox 中。

预期行为:

我更改了TextBox中的文字,然后点击对应的Button。这会将新值保存在 'coll' 中,然后呈现一个 MessageBox 序列,证明这确实已被更改。我重新启动应用程序,我的值显示新保存的值。

实际行为:

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

异常(?):

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

【问题讨论】:

  • 请返回阅读minimal reproducible example页面。与您之前的问题一样,这个问题似乎包含的代码远远多于重现您的问题所需的代码。例如,当您为您的ItemsControl 使用WrapPanel 时,此问题是否真的 发生?我严重怀疑它是。您的帖子中还有许多其他此类无关代码的示例。如果您希望有人帮助您调试代码,请提供一个好的 MCVE。

标签: c# wpf data-binding observablecollection settings


【解决方案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

原文:

我不熟悉 ApplicationSettingsBase,但也许这对 https://msdn.microsoft.com/en-in/library/8eyb2ct1(en-us).aspx 有帮助:

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

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

【讨论】:

  • 谢谢 - 这似乎是正确的。那么我需要在哪里实现IBindableComponent?在ObservableCollection?在CollectionViewSource?当您说引用本身不会改变时,您指的是哪个引用?有趣的是,在 Settings.CustomSettings.Default.Save(); 之前添加 Settings.CustomSettings.Default.coll = Settings.CustomSettings.Default.coll; 可以解决问题,但当然感觉就像是 hack。
  • @ChrisMack 请查看我的更新答案。我认为你应该以不同的方式处理这个问题。
猜你喜欢
  • 2019-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-12
相关资源
最近更新 更多