【问题标题】:How to implement INotifyPropertyChanged in Xamarin.Forms如何在 Xamarin.Forms 中实现 INotifyPropertyChanged
【发布时间】:2015-12-16 12:16:29
【问题描述】:

我正在 Xamarin.Forms 中实现一个购物车。在我的购物车页面中有一个带有数据的ListView。每个单元格都包含一个按钮,用于选择itemamount 的计数。在购物车视图中有一个总计标签。

我的问题是总数没有更新,而数字选择器发生变化。该计算方法在添加视图单元的项目时调用。我知道我需要为此实现INotifyProperty,但我不确定该怎么做。

我有一个基础视图模型,它继承了包含一个事件的 INotifyProperty

 public class BaseViewModel : INotifyPropertyChanged
{
    private double  _price;
    public double Price
    {
        get 
        { 
            return _price; 
        }
        set 
        { 
            _price = value;
            OnPropertyChanged("Price");}
        } 

 protected virtual void OnPropertyChanged(string propertyName)
 {
     if (PropertyChanged != null)
     {
         PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
     }
 }

查看模型

    public BaseViewModel()
    {
        App.Instance.ViewModel = this;
        TempList = TempList ?? new ObservableCollection<cm_items>();
        this.Title = AppResources.AppResource.Cart_menu_title;
        this.Price = CartCell.price;
    }

【问题讨论】:

标签: c# xamarin xamarin.forms


【解决方案1】:

作为一种设计方法,最好将 MVVM 实现为子类并将其实现到您的ViewModel

示例实现:

public class ObservableProperty : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null)
            handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

我还强烈建议将 ICommand 实现为字典结构,例如:

public abstract class ViewModelBase : ObservableProperty
{
    public Dictionary<string,ICommand> Commands { get; protected set; }

    public ViewModelBase()
    {
        Commands = new Dictionary<string,ICommand>();
    }
}

所以你的 ViewModel 中的所有 todo 只是继承 ViewModelBase 类并使用它

class LoginViewModel : ViewModelBase
{
    #region fields
    string userName;
    string password;
    #endregion

    #region properties
    public string UserName 
    {
         get {return userName;}
        set 
        {
            userName = value;
            OnPropertyChanged("UserName");
        }
     }

    public string Password 
    {
        get{return password;}
        set
        {
            password = value;
            OnPropertyChanged("Password");
        }
    }
    #endregion

    #region ctor
    public LoginViewModel()
    {
        //Add Commands
        Commands.Add("Login", new Command(CmdLogin));
    }
    #endregion


    #region UI methods

    private void CmdLogin()
    {
        // do your login jobs here
    }
    #endregion
}

最后:Xaml 用法:

<Entry Placeholder="Username"  Text="{Binding UserName}"/>
<Entry Placeholder="Password" Text="{Binding Password}" IsPassword="True"/>
<Button Text="Login" Command="{Binding Commands[Login]}"/>

【讨论】:

  • 我知道这是 5 年前的事,但对于任何发现这一点的人来说,我不确定使用字典以这种方式实现 ICommand 是否是一种好习惯,因为它会破坏编译时绑定,在我看来是一个有价值的工具。
  • @henda79 对于此更改,您推荐的解决方案是什么?
  • @RandomNumberFun,你想达到什么目的?
  • @henda79 只是想找出有关您的评论的最佳做法。
  • 不要在属性更改中使用硬编码名称。像这样使用 nameof():nameof(UserName)。这样,您可以更轻松地重命名并确保您的名称中没有拼写错误。
【解决方案2】:

当我开始 Xamarin 编码时,MVVM 有点混乱,直到我发现 ViewModel 上的 PropertyChangedEvent 向视图 (ContentPage) 发出信号,并更新了标签/文本框/等。

对于那些寻找“最新最好的”的人...这里有一些修改后的代码:

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

在你的属性 Setter 上:

public string SomeProperty
{
    get { return _somProperty; }
    set
    {
       _someProperty= value;
            OnPropertyChanged();
        }
    }
}

好看吗?不?省去了每次都传递属性名!

【讨论】:

    【解决方案3】:

    例如试试这个视图模型:

    public abstract class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected bool SetPropertyValue<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
        {
            if (value == null ? field != null : !value.Equals(field))
            {
                field = value;
    
                var handler = this.PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(propertyName));
                }
                return true;
            }
            return false;
        }
    }
    

    在继承的类中像这样使用它:

        private int myProperty;
        public int MyProperty
        {
            get { return this.myProperty; }
            set { this.SetPropertyValue(ref this.myProperty, value); }
        }
    

    【讨论】:

    • 现在可以了,我已经解决了我认为的问题,我错过了在视图中添加 BindingContext。但现在我面临着与此相同的其他问题。我在列表视图中有“+”按钮,而我将商品添加到购物车时,图像必须更改为“-”。当我从购物车中删除商品时,图像再次变为“+”。我希望在这里我也需要通知,但如何在这里实施。我现在有一个布尔属性,我的逻辑是:在添加和删除时更改布尔值但我不知道如何将图像更改为按钮,
    • 哦,这里的一切都更有趣了。您应该添加一个转换器并像这样使用它: 但这实际上是一个不同的问题。
    • 你能给我一个例子,我可以检查一下我使用 C# 进行设计
    • UxBoolConvert 的示例不适合答案注释,字符太多。创建一个单独的问题并在此处发布链接,我将其放在答案中。
    猜你喜欢
    • 1970-01-01
    • 2016-06-05
    • 2021-06-29
    • 1970-01-01
    • 1970-01-01
    • 2018-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多