绑定值不适用于 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 应该使用它。
我正在使用 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 应该使用它。