簡體   English   中英

Xamarin.Forms 僅綁定更新 ViewModel 屬性與默認值

[英]Xamarin.Forms Binding only updating ViewModel property with default values

在 Xamarin.Forms 中考慮以下有點復雜的情況(在Xamarin.Forms模擬器上測試):

  • 一個繼承自ContentPage並包含BindablePropertyGenericPage超類
  • Page class 繼承自GenericPage ,其ViewModel使用OneWayToSource綁定模式綁定到BindableProperty
  • 一個繼承自ContentView並包含BindablePropertyGenericControl超類
  • 一個Control class 繼承自GenericControl ,其ControlViewModel使用OneWayToSource綁定模式綁定到BindableProperty
  • The Control class is embedded in the Page class using XAML and the BindableProperty of GenericControl is bound to the Property from the ViewModel class using a OneWay binding mode

在此處輸入圖像描述

我可以驗證從PageGenericControlBindableProperty的“連接”確實有效,因為在GenericControl中使用GenericPage中的BindableProperty的默認值調用propertyChanged方法。 我還可以驗證從GenericControlControlViewModel的“連接”是否正在工作,因為ControlViewModel中的屬性設置器是使用GenericControl中的BindableProperty的默認值調用的。

但是,由於某種原因,到達GenericControl中的BindableProperty的更改(來自GenericPage的默認值或外部設置)不會傳播到ControlViewModel


完整代碼位於: https://github.com/mlxyz/Xamarin-Forms-Binding-Repro

通用頁面:

 public static readonly BindableProperty TestProperty =
            BindableProperty.Create(nameof(Test), typeof(Vector3), typeof(GenericPage), new Vector3(1, 2, 3));

        public Vector3 Test
        {
            get => (Vector3)GetValue(TestProperty);
            set => SetValue(TestProperty, value);
        }

頁:

<views:GenericPage Test="{Binding Test, Mode=OneWayToSource}" x:Name="Root">
<views:GenericPage.BindingContext>
        <viewModels:ViewModel />
    </views:GenericPage.BindingContext>
<ContentPage.Content>

   <controls:Control Test="{Binding Source={x:Reference Root}, Path=BindingContext.Test, Mode=OneWay}" />
</ContentPage.Content>
</views:GenericPage>

視圖模型:

public Vector3 Test
        {
            get => _test;
            set
            {
                _test = value;
                OnPropertyChanged();
            }
        }

通用控制:

// bindable property and associated property is defined basically the same as in GenericPage except for propertyChanged property set and different default values
        private static void PropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
        {
            System.Diagnostics.Debug.WriteLine("property changed"); // <- this is called with default values from GenericPage (1,2,3)
        }

控制:

<controls:GenericControl Test="{Binding Test, Mode=OneWayToSource}">
    <controls:GenericControl.BindingContext>
        <viewModels:ControlViewModel />
    </controls:GenericControl.BindingContext>
</controls:GenericControl>

控制視圖模型:

 public Vector3 Test
        {
            get => _test;
            set => _test = value; // <- this gets called only with default values from `GenericControl` (4,5,6)
        }

我檢查了你的樣品並找出了原因。

當你設置ControlBindingContext

<controls:GenericControl.BindingContext>
     <viewModels:ControlViewModel />
</controls:GenericControl.BindingContext>

ControlPage1之間的綁定將不再起作用。您需要直接處理Control.xaml.cs中的邏輯。

另外,既然你已經將Page1BindingContext設置為ViewModel ,為什么還要將Test綁定到自身呢?

Test="{Binding Test, Mode=OneWayToSource}"

您應該將所有綁定源放在 ViewModel 中並在其中處理邏輯。

注意:使用數據綁定時,超類中的屬性在子類中不可用。 因此,您需要再次在子類中定義它們。

所以你可以像下面這樣修改代碼。 我向 Page1 添加了一個按鈕。 單擊它時,Test 的值會發生變化。

在第1頁.xaml

<?xml version="1.0" encoding="utf-8"?>

<views:GenericPage 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:controls="clr-namespace:ReproProjectBindingIssue.Controls;assembly=ReproProjectBindingIssue"
                   xmlns:views="clr-namespace:ReproProjectBindingIssue.Views;assembly=ReproProjectBindingIssue"
                   xmlns:viewModels="clr-namespace:ReproProjectBindingIssue.ViewModels;assembly=ReproProjectBindingIssue"
                   mc:Ignorable="d"
                   x:Class="ReproProjectBindingIssue.Views.Page1" x:Name="Root">
    <views:GenericPage.BindingContext>
        <viewModels:ViewModel />
    </views:GenericPage.BindingContext>
    <ContentPage.Content>
        <StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="Center">
            <Button Text="Change Value"
                   VerticalOptions="CenterAndExpand"
                   HorizontalOptions="CenterAndExpand"
                    Command="{Binding command}"/>

            <controls:Control Test="{Binding Source={x:Reference Root}, Path=BindingContext.Test, Mode=OneWay}" />
        </StackLayout>
    </ContentPage.Content>
</views:GenericPage>

在視圖模型中

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows.Input;
using ReproProjectBindingIssue.Annotations;
using Xamarin.Forms;

namespace ReproProjectBindingIssue.ViewModels
{
    public class ViewModel : INotifyPropertyChanged
    {

        public ICommand command { get; set; }

        private Vector3 _test;

        public event PropertyChangedEventHandler PropertyChanged;

        public Vector3 Test
        {
            get => _test;
            set
            {
                _test = value;
                OnPropertyChanged();
            }
        }

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


        public ViewModel()
        {
            Test = new Vector3(9,10,11);

            command = new Command(()=> {

                Test = new Vector3(12, 13, 14);

            });

        }

    }
}

在控制中。xaml

<?xml version="1.0" encoding="UTF-8"?>

<controls:GenericControl 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:controls="clr-namespace:ReproProjectBindingIssue.Controls;assembly=ReproProjectBindingIssue"
                         xmlns:viewModels="clr-namespace:ReproProjectBindingIssue.ViewModels;assembly=ReproProjectBindingIssue"
                         mc:Ignorable="d"

                         x:Name="CustomView"


                         x:Class="ReproProjectBindingIssue.Controls.Control" Test="{Binding Test, Mode=OneWayToSource}">
    <controls:GenericControl.BindingContext>
        <viewModels:ControlViewModel />
    </controls:GenericControl.BindingContext>
    <ContentView.Content>
        <StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
            <Label  x:Name="label"/>
        </StackLayout>
    </ContentView.Content>
</controls:GenericControl>

在 Control.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using ReproProjectBindingIssue.ViewModels;
using ReproProjectBindingIssue.Views;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace ReproProjectBindingIssue.Controls
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Control : GenericControl
    {
        public Control()
        {
            InitializeComponent();
        }


        public static readonly BindableProperty TestProperty =
            BindableProperty.Create(nameof(Test), typeof(Vector3), typeof(Control), new Vector3(4, 5, 6), propertyChanged: PropertyChanged);

        public Vector3 Test
        {
            get => (Vector3)GetValue(TestProperty);
            set => SetValue(TestProperty, value);
        }

        private static void PropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
        {
            var control = bindable as Control;

            Vector3 value = (Vector3)newvalue;

            control.label.Text = "X:" + value.X + " Y:" + value.Y + " Z:" + value.Z;
        }

    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM