模板绑定的propertyChanged事件仅在包含四个模板的页面上被调用一次

时间:2018-11-03 08:45:49

标签: xamarin xamarin.forms

我有一个用于按钮的模板。我将其中四个按钮放在这样的页面上:

<template:Button Meta="gst" Grid.Column="1" Selected="{Binding Mode[0].Selected}" Text="{Binding Mode[0].Name}" TapCommand="{Binding ModeBtnCmd }" />
<template:Button Meta="gst" Grid.Column="2" Selected="{Binding Mode[1].Selected}" Text="{Binding Mode[1].Name}" TapCommand="{Binding ModeBtnCmd }" />
<template:Button Meta="gst" Grid.Column="3" Selected="{Binding Mode[2].Selected}" Text="{Binding Mode[2].Name}" TapCommand="{Binding ModeBtnCmd }" />
<template:Button Meta="gst" Grid.Column="4" Selected="{Binding Mode[3].Selected}" Text="{Binding Mode[3].Name}" TapCommand="{Binding ModeBtnCmd }" />

我声明了一个名为Mode的数组,该数组用于将Selected参数的一些值存储在这样声明的数组中:

public partial class HomePageViewModel : ObservableObject
{
   ParamViewModel[] _mode;
   public ParamViewModel[] Mode { get => _mode; set => SetProperty(ref _mode, value); }

在OnAppearing中,我为数组中的每个元素设置Selected的值:

protected async override void OnAppearing()
{
   base.OnAppearing();
   vm.Mode[0].Selected = false;
   vm.Mode[1].Selected = false;
   vm.Mode[2].Selected = true;
   vm.Mode[3].Selected = false;

但是,我没有看到每个按钮需要HandleSelectedPropertyChanged。如果在HandleSelectedPropertyChanged事件中设置调试点,则只会看到它被调用一次。

我希望只选择第三个按钮,但是当代码运行时,按钮显示为:

  

已选择已选择已选择已选择

有人可以给我建议吗? 当我一个接一个地单击按钮时,代码起作用,并且后端更改了所选内容,然后调用了上面的SetButtons。但这最初并不起作用,并且所有按钮都显示为选中状态。

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace Japanese.Templates
{
    public partial class Button : Frame
    {

        public static readonly BindableProperty TapCommandProperty = BindableProperty.Create("TapCommand", typeof(Command), typeof(Button), defaultBindingMode: BindingMode.TwoWay, defaultValue: default(Command));
        public static readonly BindableProperty TapCommandParamProperty = BindableProperty.Create(nameof(TapCommandParam), typeof(object), typeof(Button), default(object));
        public static readonly BindableProperty
            SelectedProperty = BindableProperty.Create(nameof(Selected),
            typeof(bool),
            typeof(Button),
            false,
            propertyChanged: HandleSelectedPropertyChanged);
        public static readonly BindableProperty
            TextProperty = BindableProperty.Create(nameof(Text),
            typeof(string),
            typeof(Button),
            default(string));
        public static readonly BindableProperty 
            MetaProperty = BindableProperty.Create("Meta", 
            typeof(string), 
            typeof(Button), 
            default(string),
            propertyChanged: HandleMetaPropertyChanged);
        public static readonly BindableProperty VisibleProperty = BindableProperty.Create(nameof(Visible), typeof(bool), typeof(Button), true);
        public string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
        public string Meta { get => (string)GetValue(MetaProperty); set => SetValue(MetaProperty, value); }
        public Command TapCommand { get => (Command)GetValue(TapCommandProperty); set => SetValue(TapCommandProperty, value); }
        public object TapCommandParam { get => (object)GetValue(TapCommandParamProperty); set => SetValue(TapCommandParamProperty, value); }
        public bool Selected { get => (bool)GetValue(SelectedProperty); set => SetValue(SelectedProperty, value); }
        public bool Visible { get => (bool)GetValue(VisibleProperty); set => SetValue(VisibleProperty, value); }

        string b;
        string s; 
        string f;

        public Button()
        {
            InitializeComponent();
            HasShadow = false;
            HorizontalOptions = LayoutOptions.Center;
            VerticalOptions = LayoutOptions.Center;
        }

        private static void HandleMetaPropertyChanged(BindableObject bindable, object oldValue, object meta)
        {
            var control = (Button)bindable;
            if (control != null) {
                control.b = ((string)meta).Substring(0, 1); // background surface
                control.s = ((string)meta).Substring(1, 1); // shape
                control.f = ((string)meta).Substring(2, 1); // font

                control.BackgroundColor = control.b == "g" ?
                    (Color)Application.Current.Resources["GridButtonBackgroundColor"] :
                    (Color)Application.Current.Resources["PageButtonBackgroundColor"];

                control.BorderColor = control.b == "g" ?
                   (Color)Application.Current.Resources["GridButtonBorderColor"] :
                   (Color)Application.Current.Resources["PageButtonBorderColor"];

                if (control.s == "s")
                {
                    control.CornerRadius = 5;
                    control.Padding = new Thickness(10, 5);
                }
                else
                {
                    control.WidthRequest = 50;
                    control.HeightRequest = 50;
                    control.CornerRadius = 25;
                    control.Padding = new Thickness(0);
                }

                Application.Current.Resources.TryGetValue("TextButtonsLabelRes", out object textRes);
                Application.Current.Resources.TryGetValue("IconButtonsLabelRes", out object iconRes);

                control.ButtonLabel.Style = (control.f == "t") ?
                    (Style)textRes :
                    (Style)iconRes;
            }
        }

        private static void HandleSelectedPropertyChanged(BindableObject bindable, object oldValue, object selected)
        {

            var control = (Button)bindable;
            if (control != null)
            control.ButtonLabel.TextColor =
                ((bool)selected) ?
                       (control.b == "g" ?
                            (Color)Application.Current.Resources["GridEButtonTextColor"] :
                            (Color)Application.Current.Resources["PageEButtonTextColor"]) :
                       (control.b == "g" ?
                            (Color)Application.Current.Resources["GridButtonTextColor"] :
                            (Color)Application.Current.Resources["PageButtonTextColor"]);
        }

        private async void ChangeTheColours(Object sender, EventArgs e)
        {
            if ((string)this.ButtonLabel.Text.Substring(0, 1) != " ")
            {
                BackgroundColor = this.b == "g" ?
                       (Color)Application.Current.Resources["GridCButtonBackgroundColor"] :
                       (Color)Application.Current.Resources["PageCButtonBackgroundColor"];
                BorderColor = this.b == "g" ?
                   (Color)Application.Current.Resources["GridCButtonBorderColor"] :
                   (Color)Application.Current.Resources["PageCButtonBorderColor"];
                await Task.Delay(500);
                BackgroundColor = this.b == "g" ?
                       (Color)Application.Current.Resources["GridButtonBackgroundColor"] :
                       (Color)Application.Current.Resources["PageButtonBackgroundColor"];
                BorderColor = this.b == "g" ?
                   (Color)Application.Current.Resources["GridButtonBorderColor"] :
                   (Color)Application.Current.Resources["PageButtonBorderColor"];
            }
        }

    }
}

下面是SetProperty函数供参考:

public class ObservableObject : INotifyPropertyChanged
{

    protected virtual bool SetProperty<T>(
        ref T backingStore, T value,
        [CallerMemberName]string propertyName = "",
        Action onChanged = null)
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value))
            return false;

        backingStore = value;
        onChanged?.Invoke();
        OnPropertyChanged(propertyName);
        return true;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

}

提醒一下,这里的代码和方法运行良好,但初始设置似乎并未按预期调用propertyChanged事件。

1 个答案:

答案 0 :(得分:0)

您的SelectedProperty的默认值为false,仅当当前值与新值不同时才会触发OnPropertyChanged。因此,仅触发其值设置为true的第三个按钮。

您必须在控件的构造函数中定义控件的初始状态。更改任何属性后,后续更改将在PropertyChanged处理程序中处理