【问题标题】:Xamarin "BindableProperty propertyChanged" does not fired when "RaisePropertyChanged"“RaisePropertyChanged”时不会触发 Xamarin“BindableProperty propertyChanged”
【发布时间】:2020-07-03 09:50:01
【问题描述】:

我将 MvvmCross 与 Xamarin 表单一起使用。

因此,我使用 RaisePropertyChanged 来通知 View。 但是,RaisePropertyChanged 不会在 ViewA 中触发 propertyChanged

我不知道从哪里开始调试或检查局部变量...

流程

如果我在某处更改 Data.Value,流程如下所示。

  1. 已调用事件 Data.ValueChanged
  2. ModelA.OnValueChanged 调用 OnPropertyChanged
  3. ViewModelA.OnModelPropertyChanged 调用 RaisePropertyChanged
  4. 预计 ViewA.OnChanged 已调用,但失败...

XAML

我运行并检查 XAML 绑定是否正常工作。

<DataTemplate x:Key="ViewB">
    <ViewB Data="{Binding Data}" />
</DataTemplate>

查看

我将 BindableProperty 定义如下。

// this class is abstract!
public abstract class ViewA : MvxContentView
{
        public static readonly BindableProperty DataProperty =
            BindableProperty.Create(
                propertyName: "Property",
                returnType: typeof(Data),
                declaringType: typeof(ViewA),
                defaultValue: null,
                propertyChanged: OnChanged);
        static void OnChanged(BindableObject bindable, object oldValue, object newValue)
        {
            if (newValue is null) { return; }
            
            // some codes
        }
}
// actual class

public partial class ViewB : ViewA
{
        public ViewB()
        {
            InitializeComponent();
        }
}

视图模型

// this is also abstract!
public abstract class ViewModelA<T> : MvxViewModel<T>
{
        protected T _model;
        public Data Data
        {
            get => _model.Data;
        }

        
        public T Model
        {
            get => _Model;
            set
            {
                if (SetProperty(ref _model, value))
                {
                    // Register event handler
                    _model.PropertyChanged += OnModelPropertyChanged;
                }
            }
        }

        private void OnModelPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "DataChanged":
                    {
                        // I expect this will fire 'propertyChanged' of BindableProperty.
                        // But it is not fired...
                        RaisePropertyChanged(() => Data);
                    }
                    break;
            }
        }
}

// actual class
public class ViewModelB : ViewModelA<ModelA>
{
        public ViewModelB() : base()
        {

        }
}

型号

public class LayerModel : INotifyPropertyChanged
{
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

        private Data _data;
        public Data Data
        {
            get
            {
                return _data;
            }
            set
            {
                if (_data != value)
                {
                    _data = value;
                    _data.ValueChanged += OnValueChanged;
                    OnPropertyChanged("DataChanged");
                }
            }
        }

        private void OnValueChanged(object sender, EventArgs e)
        {
            OnPropertyChanged(""DataChanged"");
        }
}

数据

public class Data
{
        private int _value;
        public int Value
        {
            get => _value;
            set
            {
                if(_value != value)
                {
                    // 2020.07.06 Edited
                    var evetArg = new DataChangedArgs
                    {
                        OldData = _value;
                        NewData = value;
                    };
                    _value = value;
                    ValueChanged?.Invoke(this, evetArg);
                }
            }
        }

        public event EventHandler ValueChanged;
}

2020.07.06 添加

public class DataChangedArgs : EventArgs
{
    public int OldData { get; set; }  
    public int NewData { get; set; }
}

【问题讨论】:

    标签: c# xamarin mvvmcross


    【解决方案1】:

    我建议你简化一些类。您创建了一个名为ValueChanged 的自定义事件,它与INotifyPropertyChanged 基本相同。推荐使用接口,有一个类MvxNotifyPropertyChanged(在MvvmCross中)实现了之前的接口。

    这是一个近似值(这个 ViewModel 不是从 MvxViewModel 继承的,但正如我所说,它是一个近似值)。

    ***编辑 问题更新:无法修改类Data

    您正在尝试使用 BindableProperty 绑定 Data,在这种情况下,Data 应实现 INotifyPropertyChanged 以通知更改。我建议你阅读BindableObject

    在这种情况下,我提出以下解决方案:

    • 创建一个继承自Data的类:它将实现INotifyPropertyChanged来通知值的变化。

    结构应该类似于:

    ...
    public class MainPageViewModel : MvxNotifyPropertyChanged
    {
        public Data Data => _model?.Data;
    
        private LayerModel _model;
        public LayerModel Model
        {
            get => _model;
            set
            {
                SetProperty(ref _model, value, () =>
                {
                    RaisePropertyChanged(nameof(Data));
                });
            }
        }
    }
    
    public class LayerModel : MvxNotifyPropertyChanged
    {
        private Data _data;
    
        public Data Data
        {
            get => _data;
            set => SetProperty(ref _data, value);
        }
    }
    
    public class Data
    {
        private int _value;
        public int Value
        {
            get => _value;
            set
            {
                if (_value == value)
                    return;
                var eventArgs = new DataChangedEventArgs(_value, value);
                _value = value;
                ValueChanged?.Invoke(this, eventArgs);
            }
        }
        public event EventHandler<EventArgs> ValueChanged;
    }
    
    public class DataChangedEventArgs : EventArgs
    {
        public DataChangedEventArgs(int oldData, int newData)
        {
            OldData = oldData;
            NewData = newData;
        }
    
        public int OldData { get; }
        public int NewData { get; }
    }
    
    public class NotificationData : Data, INotifyPropertyChanged
    {
        public static NotificationData FromData(Data data)
        {
            return new NotificationData {Value = data.Value};
        }
    
        public NotificationData()
        {
            ValueChanged += delegate
            {
                OnPropertyChanged(nameof(Value));
            };
        }
        
        public event PropertyChangedEventHandler PropertyChanged;
    
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    ...
    

    以下是一个带有抽象视图的演示,以及一个使用 MainPageViewModel 的子视图。

    抽象视图应该类似于

    public abstract class ViewA : ContentPage
    {
        public Data ModelA
        {
            get => (Data)GetValue(ModelAProperty);
            set => SetValue(ModelAProperty, value);
        }
    
        public static readonly BindableProperty ModelAProperty =
            BindableProperty.Create(
                propertyName: nameof(ModelA),
                returnType: typeof(Data),
                declaringType: typeof(ViewA),
                defaultValue: null,
                propertyChanged: OnChanged);
        static void OnChanged(BindableObject bindable, object oldValue, object newValue)
        {
            if (newValue is null) { return; }
    
            // some codes
        }
    }
    
    1. 子视图。

    后面的代码:

    ...
    public partial class MainPage : ViewA
    {
        private int _counter = 10;
        private MainPageViewModel ViewModel => (MainPageViewModel) BindingContext;
    
        public MainPage()
        {
            InitializeComponent();
    
            //--example initialization
            var data = new Data
            {
                Value = _counter
            };
            ViewModel.Model = new LayerModel
            {
                Data = NotificationData.FromData(data)
            };
        }
    
        private void Button_OnClicked(object sender, EventArgs e)
        {
            ViewModel.Model.Data.Value = ++_counter;
        }
    }
    ...
    

    XAML:

    <?xml version="1.0" encoding="utf-8" ?>
    <xamstack:ViewA xmlns="http://xamarin.com/schemas/2014/forms"
           xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
           xmlns:d="http://xamarin.com/schemas/2014/forms/design"
           xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
           xmlns:xamstack="clr-namespace:xamstack;assembly=xamstack"
           mc:Ignorable="d"
           x:Class="xamstack.MainPage"
           ModelA="{Binding Data}">
        <ContentPage.BindingContext>
            <xamstack:MainPageViewModel/>
        </ContentPage.BindingContext>
    
        <StackLayout Padding="20">
            <Button Text="Increment" Clicked="Button_OnClicked"/>
            <Label>
                <Label.FormattedText>
                    <FormattedString>
                        <Span Text="Value: "/>
                        <Span Text="{Binding Path=ModelA.Value, Mode=OneWay, Source={RelativeSource AncestorType={x:Type ContentPage}}}" 
                              TextColor="Red"
                              />
                    </FormattedString>
                </Label.FormattedText>
            </Label>
        </StackLayout>
    </xamstack:ViewA>
    

    我希望它符合你的情况。

    【讨论】:

    • 我尝试了您建议的使用 MvxNotifyPropertyChanged 的解决方案,但失败了。而且,我很抱歉错过了必要的解释。我使用事件处理程序而不是 MvxNotifyPropertyChanged 的原因是 Data 类被其他项目使用。那些其他项目正在使用 DataChangedArgs 属性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-10
    • 1970-01-01
    • 2020-10-28
    相关资源
    最近更新 更多