无法理解INotifyPropertyChanged

时间:2016-09-23 16:49:45

标签: c# xaml uwp

我只是C#中的新手,我在MSDN博客上阅读了INotifyPropertyChanged Event Handler并在此处搜索了“stackoverflow”。但我真的不明白如何在我的代码中实现它以及如何将事件和属性绑定在一起。

我已经使用BindingClass制作了INotifyPropertyChanged,代码为:

namespace Testing.Pages
{
  class BindingClass : INotifyPropertyChanged
  {
    private string _setting;
    public event PropertyChangedEventHandler PropertyChanged;

    public BindingClass()
    {

    }

    public BindingClass(string value)
    {
        _setting = value;
    }

    public string SettingProperty
    {
        get { return _setting; }
        set
        {
            _setting = value;
            // calling OnPropertyChanged whenever the property gets updated

        }
    }

    protected void OnPropertyChanged([CallerMemberName] string _setting = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(_setting));
    }
  }
}


SettingsPage.xaml

<TextBlock x:Name="PopupText"
               Grid.Row="0" 
               HorizontalAlignment="Center"
               VerticalAlignment="Center"
               Margin="0,0,0,20"
               Text="Your theme will be updated next time you start the app."
               TextWrapping="Wrap"
               Visibility="Collapsed">
        <TextBlock.Resources>
            <Storyboard x:Name="popup_animate">
                <DoubleAnimation Duration="0:0:2"
                                 Storyboard.TargetName="PopupText"
                                 AutoReverse="True"
                                 From="0.0"
                                 To="1.0"
                                 BeginTime="{x:Bind }"
                                 Storyboard.TargetProperty="(TextBlock.Opacity)"
                        ></DoubleAnimation>
            </Storyboard>
        </TextBlock.Resources>
    </TextBlock>
<TextBlock Text="Change Theme?" 
                   Margin="10,10,0,0"></TextBlock>
        <RadioButton x:Name="DarkTheme_btn" 
                     Click="ChangeTheme_btn_Click" 
                     Content="Dark Theme" 
                     Margin="10,0,0,0"
                     GroupName="theme"></RadioButton>
        <RadioButton x:Name="LightTheme_btn" 
                     Click="ChangeTheme_btn_Click" 
                     Content="Light Theme" 
                     Margin="10,0,0,0"
                     GroupName="theme"></RadioButton>

和代码隐藏文件SettingsPage.xaml.cs是:

namespace Testing.Pages
{

/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
  public sealed partial class SettingsPage : Page
  {
    BindingClass notifyProperty = new BindingClass();

    public SettingsPage()
    {
        this.InitializeComponent();
        NavigationCacheMode = NavigationCacheMode.Enabled;
    }

    private void ChangeTheme_btn_Click(object sender, RoutedEventArgs e)
    {
        DataContext = notifyProperty;
        int notifySettings = 0;
        if ((bool)DarkTheme_btn.IsChecked)
        {
            notifySettings = 2;
            AppSettings.saveThemeSettings(notifySettings);
            PopupText.Visibility = Visibility.Visible;
            popup_animate.Begin();
        }
        else if ((bool)LightTheme_btn.IsChecked)
        {
            notifySettings = 1;
            AppSettings.saveThemeSettings(notifySettings);
            PopupText.Visibility = Visibility.Visible;
            popup_animate.Begin();
        }
     }
  }
}

我已使用int notifySettings更改LocalSettingsFolder中应用的设置,每次重启应用时,都会加载App.xaml中的设置。每次我更改设置时,我都会调用其他class中的某个功能,并更改设置,当我点击animation中的radiobuttons中的一个SettingsPage.xaml时,会播放int notifySettings。这是旧方法。
现在我想将这些事件绑定在一起,这样我就不必使用PopupTextTheme Settings动画应该像INotifyPropertyChanged Event更新时一样播放。这就是我如何了解int notifySettings的方法。
int传递Settings Class值以相应地更改设置。 1 = LightTheme和2 = DarkTheme。

以下是namespace Testing.Pages { class AppSettings { public static void saveThemeSettings(int value) { ApplicationDataContainer themeSettings = ApplicationData.Current.LocalSettings; StorageFolder localFolder = ApplicationData.Current.LocalFolder; themeSettings.Values["AppThemeSetting"] = value.ToString(); } public static string readThemeSettings() { ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings; StorageFolder localFolder = ApplicationData.Current.LocalFolder; string appSettingsString = "error, nothing found"; if (localSettings.Values.ContainsKey("AppThemeSetting")) { appSettingsString = localSettings.Values["AppThemeSetting"]?.ToString(); } return appSettingsString; } public static void removeLocalSettings(string settingValue) { ApplicationData.Current.LocalSettings.Values.Remove(settingValue); } } }

Animation

如果仍有任何歧义,请告诉我,我可以尝试进一步解释。我希望有人可以帮助我。

更新
我根据Danny Bogers的回答对我的项目进行了更改,但是所需的Target range and source range must be on the same spreadsheet没有启动,我认为这是因为函数甚至没有被调用。我做了一些更改,并尝试自己做一些事情,但它没有真正解决,所以我将使用自己的方法进行更改,直到有人提出解决方案。

2 个答案:

答案 0 :(得分:1)

我不善于解释事情,我希望你能得到它的要点! 我也没有时间对它进行适当的测试,但这应该可以胜任。

您可以通过+=语法订阅活动。现在,每当引发事件时,都会使用给定的参数触发所有订阅者。在这种情况下,PropertyChangedEventArgs的实例。这允许您的订阅者根据给定EventArgs的值来表现不同(您还可以将EventArgs的空实例传递给订阅者,这意味着您的订阅者将不会根据值执行任何操作来自EventArgs)。

一些小改动:

  • 为主题添加了枚举。在检查选择了哪个主题时添加类型安全性。这比检查硬编码字符串要好。

  • 无法找到ThemeSetting时添加了异常。您可以在try / catch块中相应地处理异常。这再次比检查硬编码字符串更好。

  • 为您的appsetting键添加了一个const字符串。这就像前两点一样,可以防止由于输入错误导致的运行时错误/为您提供一个管理值的中心位置。

    namespace Testing.Pages
    {
        public enum Themes
        {
            Light = 1,
            Dark = 2
        }
    }
    
    namespace Testing.Pages
    {
        public class ThemeSettingNotFoundException : Exception
        {
            public ThemeSettingNotFoundException() : base("error, nothing found")
            {
            }
        }
    
    }
    
    namespace Testing.Pages
    {
        class BindingClass : INotifyPropertyChanged
        {
            private string _setting;
            public event PropertyChangedEventHandler PropertyChanged;
    
            public BindingClass() {
    
            }
    
            public BindingClass(string value) {
                _setting = value;
            }
    
            public string SettingProperty
            {
                get { return _setting; }
                set
                {
                    if(!_setting.Equals(value)){
                        _setting = value;
                        OnPropertyChanged();
                    }
                }
            }
    
            protected void OnPropertyChanged([CallerMemberName] string _setting = "") {
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(_setting));
            }
        }
    }
    
    
    namespace Testing.Pages
    {
    
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public sealed partial class SettingsPage : Page
        {
            BindingClass notifyProperty = new BindingClass();
    
            public SettingsPage() {
                this.InitializeComponent();
                NavigationCacheMode = NavigationCacheMode.Enabled;
    
                //Subscribe to the PropertyChanged event
                notifyProperty.PropertyChanged += OnThemeSettingChanged;
            }
    
            private void ChangeTheme_btn_Click(object sender, RoutedEventArgs e) {
                DataContext = notifyProperty;
                SaveThemeSettings();
            }
    
            private void SaveThemeSettings()
            {
                var notifySettings = 0;
                if ((bool)DarkTheme_btn.IsChecked)
                    notifySettings = 2;
                else if ((bool)LightTheme_btn.IsChecked)
                    notifySettings = 1;
    
                //Only save theme settings when a button was checked
                if (notifySettings != 0)
                    AppSettings.saveThemeSettings((Themes)notifySettings);
            }
    
            private void OnThemeSettingChanged(object sender, PropertyChangedEventArgs args)
            {
                PopupText.Visibility = Visibility.Visible;
                popup_animate.Begin();
            }
        }
    }
    
    
    namespace Testing.Pages
    {
        class AppSettings
        {
            private const string ThemeSettingKey = "AppThemeSetting";
    
            public static void saveThemeSettings(Themes theme) {
                ApplicationDataContainer themeSettings = ApplicationData.Current.LocalSettings;
                themeSettings.Values[ThemeSettingKey] = theme.ToString();
            }
    
            public static Themes readThemeSettings() {
                ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
                if (!localSettings.Values.ContainsKey(ThemeSettingKey))
                    throw new ThemeSettingNotFoundException();
                var appSettingsString = localSettings.Values[ThemeSettingKey];
                return (Themes)Enum.Parse(typeof(Themes), appSettingsString);
            }
    
            public static void removeLocalSettings(string settingValue) {
                ApplicationData.Current.LocalSettings.Values.Remove(settingValue);
            }
        }
    }
    

答案 1 :(得分:1)

PropertyChanged需要知道哪个属性已更改,因此您必须传递它的名称。

让它看起来像这样:

public string SettingProperty
{
    get { return _setting; }
    set
    {
        if(_setting != value) // Or String.Equals(_setting, value, ...)
        {
             _setting = value;
             OnPropertyChanged(); // Invoke using no argument.
        }
    }
}

protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

OnPropertyChanged方法在其参数上使用CallerMemberName属性,该属性也是可选的。这意味着编译器将使用此参数并将ith替换为调用者的实际名称。所以不要自己传递任何价值。

相当于PropertyChanged("SettingProperty")。然而,属性使得折射器安全。

关于你提出的其余问题:我认为这个问题本身太过严重,至少包含一个以上的问题。因此,一旦您修复了propertychanged部分,而是询问具体问题的新问题。