绑定值不适用于 Xamarin Forms

Binding values not working on Xamarin Forms

我正在使用 MVVM 在 Xamarin Forms 上构建应用程序。问题是我运行这个app的时候,数据没有出现,数值都是默认的。但是,如果我在 Xaml 文件中以绑定 属性 的名称更改某些内容,则该值将更新并显示为应有的样子,而无需重新启动应用程序。 我提到我在 ViewModel 中初始化了 BindingContext。

代码如下:

视图模型:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
using XamarinApp.Services;
using XamarinApp.Views.Month;

namespace XamarinApp.ViewModels.Month
{
    public class DefaultScreenViewModel : BaseViewModel
    {
        private readonly IMonthService _monthService;
        public decimal budget;
        public decimal spendings;
        public decimal economies;
        public decimal percent;
        public DefaultScreenViewModel(IMonthService monthService)
        {
            _monthService = monthService;
            UpdateBudgetCommand = new Command(async () => await GoToUpdateBudget());
            Title= "My Money";
        }
        public async void GetDefaultScreen()
        {
            try
            {
                var screen = await _monthService.GetDefaultScreen();
                budget = screen.Budget;
                economies = screen.Economies;
                spendings = screen.Spendings;
                percent = spendings/budget;

               
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
        public decimal Budget
        {
            get => budget;
            set => OnPropertyChanged(nameof(Budget));
        }
        public decimal Spendings
        {
            get => spendings;
            set => OnPropertyChanged(nameof(Spendings));
        }
        public decimal Economies
        {
            get => economies;
            set => OnPropertyChanged(nameof(Economies));
        }
        public decimal Percent
        {
            get => percent;
            set => OnPropertyChanged(nameof(Percent));
        }

        private async Task GoToUpdateBudget()
            => await Shell.Current.GoToAsync(nameof(UpdateBudgetPage));

        public ICommand UpdateBudgetCommand { get; }
    }
}

Xaml 文件:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:month="clr-namespace:XamarinApp.ViewModels.Month" 
             x:Class="XamarinApp.Views.Month.DefaultScreenPage"
             BackgroundColor="{StaticResource BackgroundColor}"
             Title="{Binding Title}">
    <ContentPage.Content>
       
        <StackLayout>
            <Frame Padding="30" Margin="30" BackgroundColor="{StaticResource Primary}">

                <StackLayout>
                        <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
                        <StackLayout Orientation="Vertical" FlexLayout.AlignSelf="Start" HorizontalOptions="FillAndExpand">
                            <Label Text="{Binding Spendings}" FontSize="Subtitle" VerticalOptions="FillAndExpand" />
                            <Label Text="Spendings" FontSize="Subtitle" VerticalOptions="FillAndExpand"/>
                            
                        </StackLayout>
                        <StackLayout Orientation="Vertical" FlexLayout.AlignSelf="End" HorizontalOptions="End">
                            <Label Text="{Binding Budget}" FontSize="Subtitle" VerticalOptions="FillAndExpand" />
                            <Label Text="Budget" FontSize="Subtitle" VerticalOptions="FillAndExpand"/>
                            
                        </StackLayout>
                        
                        </StackLayout>
                        <ProgressBar Progress="{Binding Percent}" BackgroundColor="#69B811" ProgressColor="#750909" FlexLayout.AlignSelf="Center" Margin="10"/>
                    <Label/>
                    <Label Text="Current Month Savings: " />
                    <Label Text="{Binding Economies}"/>
                </StackLayout>
        </Frame>
        <Button Text="Update your budget" Command="{Binding UpdateBudgetCommand}" Margin="20,40,20,0" CornerRadius="10"/>
        </StackLayout>
    </ContentPage.Content>
</ContentPage>

Xaml.cs 文件:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using XamarinApp.ViewModels.Month;

namespace XamarinApp.Views.Month
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class DefaultScreenPage : ContentPage
    {
        public readonly DefaultScreenViewModel _defaultScreenViewModel;
        public DefaultScreenPage()
        {
            InitializeComponent();
            _defaultScreenViewModel = Startup.Resolve<DefaultScreenViewModel>();
            BindingContext = _defaultScreenViewModel;
        }
        protected override void OnAppearing()
        {
            _defaultScreenViewModel?.GetDefaultScreen();
        }
    }
}

和 BaseViewModel:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

using Xamarin.Forms;

using XamarinApp.Models;
using XamarinApp.Services;

namespace XamarinApp.ViewModels
{
    public class BaseViewModel : INotifyPropertyChanged
    {
        public IDataStore<Item> DataStore => DependencyService.Get<IDataStore<Item>>();

        bool isBusy = false;
        public bool IsBusy
        {
            get { return isBusy; }
            set { SetProperty(ref isBusy, value); }
        }

        string title = string.Empty;
        public string Title
        {
            get { return title; }
            set { SetProperty(ref title, value); }
        }

        protected 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;
        }

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            var changed = PropertyChanged;
            if (changed == null)
                return;

            changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }
}

GetDefaultScreen 正在设置 public 字段的值

var screen = await _monthService.GetDefaultScreen();
budget = screen.Budget;
economies = screen.Economies;
spendings = screen.Spendings;
percent = spendings/budget;

相反,您需要设置 public 属性的值

var screen = await _monthService.GetDefaultScreen();
Budget = screen.Budget;
Economies = screen.Economies;
Spendings = screen.Spendings;
Percent = spendings/budget;

关于“我忘记输入字段=值”(在设置器中)。

这是一种简化二传手的方法。在一些 mvvm 库中使用,并显示在 Xamarin Forms Binding Mode.

在您的 BaseViewModel class 中,添加此方法:

bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
    if (object.Equals(storage, value))
        return false;

    storage = value;
    OnPropertyChanged(propertyName);
    return true;
}

那么每个 属性 就是:

private decimal budget;
public decimal Budget
{
    get => budget;
    set => SetProperty(ref budget, value);
}

注意:属性 名称在编译时通过可选第三个参数的 [CallerMemberName] 属性自动选取。

注意:字段 (budget) 通常设为private。没有其他 class 应该使用它。