简体   繁体   English

绑定值不适用于 Xamarin Forms

[英]Binding values not working on Xamarin Forms

I am building an application on Xamarin Forms using MVVM.我正在使用 MVVM 在 Xamarin Forms 上构建应用程序。 The problem is that when I run the app, the data does not appear, the values are default.问题是当我运行应用程序时,数据没有出现,值是默认值。 But if I change something in the Xaml file, in the name of the binding property, that value is updated and appears as it should be without restarting the application.但是,如果我在 Xaml 文件中以绑定属性的名称更改某些内容,则该值将更新并显示为应有的样子,而无需重新启动应用程序。 I mention that I initialized BindingContext in ViewModel.我提到我在 ViewModel 中初始化了 BindingContext。

Here is the code:这是代码:

ViewModel:视图模型:

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

The Xaml file: 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>

The Xaml.cs file: 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();
        }
    }
}

And the BaseViewModel:和 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 is setting the value of the public field GetDefaultScreen正在设置公共字段的值

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

instead, you need to set the value of the public properties相反,您需要设置公共属性的值

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

Re "I forgot to put field= value" (in the setters).回复“我忘记输入字段 = 值”(在设置器中)。

Here is a way to streamline your setters.这是一种简化设置器的方法。 Used in some mvvm libraries, and shown in Xamarin Forms Binding Mode .在一些 mvvm 库中使用,并显示在Xamarin Forms 绑定模式中。

In your BaseViewModel class, add this method:在您的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;
}

Then each property is simply:那么每个属性就是:

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

NOTE: property name is automatically picked up at compile time via [CallerMemberName] attribute on optional third parameter.注意:属性名称在编译时通过可选第三个参数的[CallerMemberName]属性自动获取。

NOTE: The field ( budget ) is usually made private .注意:字段budget )通常是private的。 No other class should be using that.没有其他 class 应该使用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM