首页 > 解决方案 > 用于编辑 user.settings 的 WPF 窗口。为什么价值观不变?

问题描述

我正在使用 wpf 窗口来编辑用户设置。

这是我到目前为止所做的:

<ListView Grid.Row="1"
        ItemsSource="{Binding Source={x:Static properties:Settings.Default}, Path=PropertyValues}"
        HorizontalContentAlignment="Stretch" Background="LightGray"
        ScrollViewer.HorizontalScrollBarVisibility="Disabled">
    <ListView.ItemTemplate>
        <DataTemplate>
            <DockPanel HorizontalAlignment="Stretch"
                    IsEnabled="{Binding DataContext.Enabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">

                <Label Width="200" Content="{Binding Name}"/>
                <Label Width="200" Content="{Binding Path=Property.PropertyType}" Foreground="Gray" FontStyle="Italic"/>
                <ContentControl VerticalContentAlignment="Center" Content="{Binding Path=PropertyValue}">
                    <ContentControl.Resources>
                        <ResourceDictionary>
                            <DataTemplate DataType="{x:Type sys:Boolean}">
                                <CheckBox IsChecked="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                            </DataTemplate>
                            <DataTemplate DataType="{x:Type sys:String}">
                                <TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                            </DataTemplate>
                            <DataTemplate DataType="{x:Type sys:Int32}">
                                <TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                            </DataTemplate>
                        </ResourceDictionary>
                    </ContentControl.Resources>
                </ContentControl>
            </DockPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

这些值正在显示,Properties.Settings.Default但当我更改并使用Properties.Settings.Default.Save();. 双向绑定是否正确?

谢谢

标签: c#wpfdata-binding

解决方案


正如评论和这个答案中所建议的,我需要将其封装System.Configuration.SettingsPropertyValue到实现的 ViewModel 中INotifyPropertyChanged。否则绑定将不起作用。

视图模型:

public class SettingsPropertyValueProxy : INotifyPropertyChanged
{
    public string Name { get; }
    public Type PropertyType => PropertyValue.GetType();
    public object PropertyValue
    {
        get
        {
            return Properties.Settings.Default[Name];
        }
        set
        {
            try
            {
                Properties.Settings.Default[Name] = Convert.ChangeType(value, PropertyType);
                Properties.Settings.Default.Save();
            }
            catch
            { }
        }
    }

    public SettingsPropertyValueProxy(string name)
    {
        Name = name;
        Properties.Settings.Default.PropertyChanged += (sender, e) => _OnPropertyChanged(e.PropertyName);
    }

    private void _OnPropertyChanged(string propertyName)
    {
        if (propertyName == Name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PropertyValue)));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

要绑定的新属性:

public IEnumerable<SettingsPropertyValueProxy> Values { get; } 
    = Properties.Settings.Default.Properties
    .Cast<SettingsProperty>()
    .Select(p => new SettingsPropertyValueProxy(p.Name))
    .OrderBy(p => p.Name)
    .ToArray();

正确的视图和正确的数据模板:

<ListView Grid.Row="1"
        ItemsSource="{Binding Path=Values}"
        HorizontalContentAlignment="Stretch" Background="LightGray"
        ScrollViewer.HorizontalScrollBarVisibility="Disabled">
    <ListView.ItemTemplate>
        <DataTemplate>
            <DockPanel HorizontalAlignment="Stretch"
                    IsEnabled="{Binding DataContext.Enabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">

                <Label Width="200" Content="{Binding Path=Name}"/>
                <Label Width="200" Content="{Binding Path=PropertyType}" Foreground="Gray" FontStyle="Italic"/>
                <!--<TextBox Text="{Binding Path=PropertyValue, Mode=TwoWay}"/>-->
                <ContentControl VerticalContentAlignment="Center" Content="{Binding Path=PropertyValue}">
                    <ContentControl.Resources>
                        <ResourceDictionary>
                            <DataTemplate DataType="{x:Type sys:Boolean}">
                                <CheckBox IsChecked="{Binding Path=PropertyValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
                                            DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DockPanel}}, Path=DataContext}"/>
                            </DataTemplate>
                            <DataTemplate DataType="{x:Type sys:String}">
                                <TextBox Text="{Binding Path=PropertyValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                            DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DockPanel}}, Path=DataContext}"/>
                            </DataTemplate>
                            <DataTemplate DataType="{x:Type sys:Int32}">
                                <TextBox Text="{Binding Path=PropertyValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                                            DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DockPanel}}, Path=DataContext}"/>
                            </DataTemplate>
                        </ResourceDictionary>
                    </ContentControl.Resources>
                </ContentControl>
            </DockPanel>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

推荐阅读