【问题标题】:Xamarin Forms Settings Plugin BindableXamarin 表单设置插件可绑定
【发布时间】:2016-01-03 09:08:57
【问题描述】:

我一直是非常棒的 Xamarin 跨平台设置插件 https://components.xamarin.com/view/SettingsPlugin(准确地说是 PCL 中的 Nuget 版本)

它运行良好,但现在使用 Xamarin 表单我真的希望能够绑定到设置值。即我希望在内容页面上有一个标签,上面写着:“设置值是:[值]”并在设置更改时更新。

设置类如下所示:

 public static class Settings {

   private static ISettings AppSettings
{
  get
  {
    return CrossSettings.Current;
  }
}

#region Setting Constants

    const string HOT_TIME_COUNT_KEY = "hotTimeCount";
    private static readonly int HOT_TIME_COUNT_DEFAULT = 0;



    #endregion



    public static int HotTimeCount
    {
        get { return AppSettings.GetValueOrDefault(HOT_TIME_COUNT_KEY, HOT_TIME_COUNT_DEFAULT); }
        set { AppSettings.AddOrUpdateValue(HOT_TIME_COUNT_KEY, value); }
    }

}

我似乎无法弄清楚这项工作需要什么格式?可绑定属性。创建?它是否需要 OnPropertyChanged 方法? Settings 类是否必须从 BindableObject 派生?

【问题讨论】:

    标签: xamarin binding xamarin.forms settings bindable


    【解决方案1】:

    我创建了这个库 :) 你总是可以在 github 上发布问题,但本质上你需要创建一个视图模型,它有一个你希望数据绑定到的公共属性,然后从那里调用设置并引发一个属性如果值发生更改,则通知更改。您的 Settings.cs 可以保持不变,但您需要创建视图模型,例如:

     public class MyViewModel : INotifyPropertyChanged
    {
    
        public int Time
        {
            get { return Settings.HotTimeCount; }
            set
            {
                if (Settings.HotTimeCount == value)
                    return;
    
                Settings.HotTimeCount = value;
                OnPropertyChanged();
            }
    
        }
    
        private Command increase;
        public Command IncreaseCommand
        {
            get 
            { 
                return increase ?? (increase = new Command(() =>Time++));
            }
        }
    
        #region INotifyPropertyChanged implementation
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        public void OnPropertyChanged([CallerMemberName]string name = "")
        {
            var changed = PropertyChanged;
            if (changed == null)
                return;
            changed(this, new PropertyChangedEventArgs(name));
        }
    
        #endregion
    
    
    }
    

    然后你的 XAML 将在你的内容页面中看起来像这样:

    <StackLayout Padding="25">
    
    <Button Text="Increase" Command="{Binding IncreaseCommand}"/>
    <Label Text="{Binding Time, StringFormat='The time is {0:F0}'}"/>
    
    </StackLayout>
    

    确保在页面的 xaml.cs 中设置 BindingContext:

    public partial class MyPage : ContentPage
    {
        public MyPage()
        {
            InitializeComponent();
            BindingContext = new MyViewModel();
        }
    }
    

    这实际上并没有太多要实际实现的代码,因为您的 ViewModel 将更改实现 INotifyProprety 的 BaseViewModel,所以实际上您只是添加了

     public int Time
        {
            get { return Settings.HotTimeCount; }
            set
            {
                if (Settings.HotTimeCount == value)
                    return;
    
                Settings.HotTimeCount = value;
                OnPropertyChanged();
            }
    
        }
    

    更神奇的方式

    但是,使用 C# 的强大功能并了解数据绑定的工作原理,您可以首先创建一个所有东西都将使用的 BaseViewModel:

     public class BaseViewModel : INotifyPropertyChanged
    {
    
        public Settings Settings
        {
            get { return Settings.Current; }
        }
    
    
        #region INotifyPropertyChanged implementation
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        public void OnPropertyChanged([CallerMemberName]string name = "")
        {
            var changed = PropertyChanged;
            if (changed == null)
                return;
            changed(this, new PropertyChangedEventArgs(name));
        }
    
        #endregion
    
    
    }
    

    请注意我对 Settings.Current 的引用,我们现在需要将其实现为单例,但我们将使用 BaseViewModel,因此我们不必重新实现 INotifyPropertyChanged:

    public class Settings : BaseViewModel
    {
        static ISettings AppSettings
        {
            get
            {
                return CrossSettings.Current;
            }
        }
    
        static Settings settings;
        public static Settings Current
        {
            get { return settings ?? (settings = new Settings()); }
        }
    
        #region Setting Constants
    
        const string HOT_TIME_COUNT_KEY = "hotTimeCount";
        static readonly int HOT_TIME_COUNT_DEFAULT = 0;
    
        #endregion
    
        public int HotTimeCount
        {
            get
            { 
                return AppSettings.GetValueOrDefault<int>(HOT_TIME_COUNT_KEY, HOT_TIME_COUNT_DEFAULT); 
            }
            set
            { 
                if (AppSettings.AddOrUpdateValue<int>(HOT_TIME_COUNT_KEY, value))
                    OnPropertyChanged();
    
            }
        }
    }
    

    当然,现在我们仍然希望创建一个唯一的 ViewModel,我们的 XAML 视图将绑定到:

     public class MyViewModel : BaseViewModel
    {
        private Command increase;
        public Command IncreaseCommand
        {
            get 
            { 
                return increase ?? (increase = new Command(() =>Settings.HotTimeCount++));
            }
        }
    }
    

    请注意,我们现在继承自 BaseViewModel,这意味着我们的命令实际上可以增加 Settings.HotTimeCount!但是现在我们必须稍微调整一下我们的 Xaml,以了解我们为标签实际绑定的数据:

     <StackLayout Padding="25">
    
    <Button Text="Increase" Command="{Binding IncreaseCommand}"/>
    <Label BindingContext="{Binding Settings}" Text="{Binding HotTimeCount, StringFormat='The time is {0:F0}'}"/>
    
    </StackLayout>
    </ContentPage.Content>
    

    请注意,我正在将 BindingContext 设置为我们的 Settings,它位于 Label 的 BaseViewModel 中,必须这样做,因为它现在所在的位置。你有它。

    我将使用此信息更新我的自述文件。

    【讨论】:

    • 啊,我明白了,谢谢!我希望可能有一个非常短的方法来做到这一点?每次我添加一个需要绑定的新属性时,似乎需要大量代码。还有其他选择吗?
    • 查看我更新的答案,其实代码很少,但我为你重新编写了它,所以它更加精简。
    • @JamesMontemagno 我一直在阅读您的博客,如果我做对了,那么我可以将应用程序与默认设置放在一个文件中,我可以将其作为文件名参数传递给 GetValueOrDefault 方法,而不是硬编码默认值在第二个参数?如果是的话,你能给我举个例子吗?
    猜你喜欢
    • 2019-01-09
    • 1970-01-01
    • 2017-07-15
    • 2016-10-10
    • 2018-12-16
    • 1970-01-01
    • 1970-01-01
    • 2018-09-25
    • 1970-01-01
    相关资源
    最近更新 更多