xamarin.forms如何初始化数据并使数据绑定正常工作?
How to initialize data and make data binding work properly in xamarin.forms?
我从这个 tutorial 中学到了东西,并尝试使用主详细信息页面构建我自己的时间跟踪器应用程序。但是我遇到了一个问题。这是针对该问题的类似快速演示:
对于MasterDetailPageDetail.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage 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"
mc:Ignorable="d"
xmlns:local="clr-namespace:QuickDemo"
x:Class="QuickDemo.MasterDetailPageDetail"
Title="Detail">
<TabbedPage.Children>
<NavigationPage Title="Tab One">
<x:Arguments>
<local:FirstPage BindingContext="{Binding FirstPageModel}" />
</x:Arguments>
</NavigationPage>
<NavigationPage Title="Tab Two">
</NavigationPage>
</TabbedPage.Children>
</TabbedPage>
对于FirstPage.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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
NavigationPage.HasNavigationBar="False"
xmlns:buttons="clr-namespace:QuickDemo.Views.Buttons"
x:Class="QuickDemo.FirstPage">
<ContentPage.Content>
<StackLayout>
<StackLayout Padding="20,20,20,5">
<Label Text="{Binding CurrentStartDate, StringFormat='{0:h:mm tt}'}"
FontSize="20"
TextColor="Black"/>
<Label Text="{Binding RunningTotal, StringFormat='{}{0:h\:mm\:ss}'}"
FontSize="50" HorizontalTextAlignment="Center"
TextColor="Black"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
对于DetailPageModel.cs:
public class DetailPageModel : PageModelBase
{
private FirstPageModel _firstPageModel;
public FirstPageModel FirstPageModel
{
get => _firstPageModel;
set => SetProperty(ref _firstPageModel, value);
}
public DetailPageModel(
FirstPageModel firstPageModel)
{
FirstPageModel = firstPageModel;
}
public override Task InitializeAsync(object navigationData = null)
{
return Task.WhenAny(base.InitializeAsync(navigationData),
FirstPageModel.InitializeAsync(null));
}
}
对于FirstPageModel.cs:
public class FirstPageModel : PageModelBase
{
private DateTime _currentStartDate;
public DateTime CurrentStartDate
{
get => _currentStartDate;
set => SetProperty(ref _currentStartDate, value);
}
private TimeSpan _runningTotal;
public TimeSpan RunningTotal
{
get => _runningTotal;
set => SetProperty(ref _runningTotal, value);
}
private Timer _timer;
public FirstPageModel()
{
this.InitializeTimer();
}
private void InitializeTimer()
{
_timer = new Timer();
_timer.Interval = 1000;
_timer.Enabled = false;
_timer.Elapsed += TimerElapsed;
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
RunningTotal += TimeSpan.FromSeconds(1);
}
public override async Task InitializeAsync(object navigationData)
{
CurrentStartDate = DateTime.Now;
RunningTotal = new TimeSpan();
await base.InitializeAsync(navigationData);
}
}
并且我在PageModelLocator.cs中注册了页面和页面模型:
public class PageModelLocator
{
static TinyIoCContainer _container;
static Dictionary<Type, Type> _lookupTable;
static PageModelLocator()
{
_container = new TinyIoCContainer();
_lookupTable = new Dictionary<Type, Type>();
// Register pages and page models
Register<DetailPageModel, MasterDetailPageDetail>();
Register<FirstPageModel, FirstPage>();
// Register services (services are registered as singletons default)
}
public static T Resolve<T>() where T : class
{
return _container.Resolve<T>();
}
public static Page CreatePageFor(Type pageModelType)
{
var pageType = _lookupTable[pageModelType];
var page = (Page)Activator.CreateInstance(pageType);
var pageModel = _container.Resolve(pageModelType);
page.BindingContext = pageModel;
return page;
}
static void Register<TPageModel, TPage>() where TPageModel : PageModelBase where TPage : Page
{
_lookupTable.Add(typeof(TPageModel), typeof(TPage));
_container.Register<TPageModel>();
}
}
PageModelBase.cs 和 ExtendedBindableObject.cs 与教程相同。当我 运行 模拟器时,我得到了这个结果:
我以为会有一个 DateTime 字符串和一个零时间跨度。我觉得 FirstPageModel 中的数据根本没有初始化。我还尝试在构造函数中设置 CurrentStartTime。得到了相同的结果。
我是否错过了在首页上显示 CurrentStartDate 和 RunningTotal 的内容?任何帮助或提示将不胜感激。提前致谢。
我相信您的 MasterDetailPageDetail
没有被初始化(或者 BindingContext
没有被初始化)。本教程中的体系结构在导航事件期间设置 BindingContext
。由于您在 MasterDetailPage
的 XAML 中显式设置了 Master 和 Detail,因此未显式设置绑定上下文并且未调用 InitializeAsync
方法。
在导航服务中添加初始化
使用以下方法更新 NavigationService
的 NavigateToAsync
方法,就在 if (setRoot)
之后和 if (page is TabbedPage tabbedPage)
之前:
if (page is MasterDetailPage mdp)
{
App.Current.MainPage = mdp;
// We need to initialize both Master's BindingContext as
// well as Detail's BindingContext if they are PageModelBases
if (mdp.Master.BindingContext is PageModelBase masterPM)
{
await masterPM.InitializeAsync(null);
}
if (mdp.Detail.BindingContext is PageModelBase detailPM)
{
await detailPM.InitializeAsync(null);
}
}
else if (page is TabbedPage tabbedPage)
// .... existing code here
确保您正在为页面(主页和详细信息)设置绑定上下文。您可以在 XAML 中执行此操作,或者像在 Tabbed Page
中那样解析 MasterDetailPage 和 PageModel
如果需要,我可以制作一个视频来介绍这个,并将其添加到系列中。希望这对您有所帮助!
干杯,
帕特里克
我从这个 tutorial 中学到了东西,并尝试使用主详细信息页面构建我自己的时间跟踪器应用程序。但是我遇到了一个问题。这是针对该问题的类似快速演示:
对于MasterDetailPageDetail.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage 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"
mc:Ignorable="d"
xmlns:local="clr-namespace:QuickDemo"
x:Class="QuickDemo.MasterDetailPageDetail"
Title="Detail">
<TabbedPage.Children>
<NavigationPage Title="Tab One">
<x:Arguments>
<local:FirstPage BindingContext="{Binding FirstPageModel}" />
</x:Arguments>
</NavigationPage>
<NavigationPage Title="Tab Two">
</NavigationPage>
</TabbedPage.Children>
</TabbedPage>
对于FirstPage.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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
NavigationPage.HasNavigationBar="False"
xmlns:buttons="clr-namespace:QuickDemo.Views.Buttons"
x:Class="QuickDemo.FirstPage">
<ContentPage.Content>
<StackLayout>
<StackLayout Padding="20,20,20,5">
<Label Text="{Binding CurrentStartDate, StringFormat='{0:h:mm tt}'}"
FontSize="20"
TextColor="Black"/>
<Label Text="{Binding RunningTotal, StringFormat='{}{0:h\:mm\:ss}'}"
FontSize="50" HorizontalTextAlignment="Center"
TextColor="Black"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
对于DetailPageModel.cs:
public class DetailPageModel : PageModelBase
{
private FirstPageModel _firstPageModel;
public FirstPageModel FirstPageModel
{
get => _firstPageModel;
set => SetProperty(ref _firstPageModel, value);
}
public DetailPageModel(
FirstPageModel firstPageModel)
{
FirstPageModel = firstPageModel;
}
public override Task InitializeAsync(object navigationData = null)
{
return Task.WhenAny(base.InitializeAsync(navigationData),
FirstPageModel.InitializeAsync(null));
}
}
对于FirstPageModel.cs:
public class FirstPageModel : PageModelBase
{
private DateTime _currentStartDate;
public DateTime CurrentStartDate
{
get => _currentStartDate;
set => SetProperty(ref _currentStartDate, value);
}
private TimeSpan _runningTotal;
public TimeSpan RunningTotal
{
get => _runningTotal;
set => SetProperty(ref _runningTotal, value);
}
private Timer _timer;
public FirstPageModel()
{
this.InitializeTimer();
}
private void InitializeTimer()
{
_timer = new Timer();
_timer.Interval = 1000;
_timer.Enabled = false;
_timer.Elapsed += TimerElapsed;
}
private void TimerElapsed(object sender, ElapsedEventArgs e)
{
RunningTotal += TimeSpan.FromSeconds(1);
}
public override async Task InitializeAsync(object navigationData)
{
CurrentStartDate = DateTime.Now;
RunningTotal = new TimeSpan();
await base.InitializeAsync(navigationData);
}
}
并且我在PageModelLocator.cs中注册了页面和页面模型:
public class PageModelLocator
{
static TinyIoCContainer _container;
static Dictionary<Type, Type> _lookupTable;
static PageModelLocator()
{
_container = new TinyIoCContainer();
_lookupTable = new Dictionary<Type, Type>();
// Register pages and page models
Register<DetailPageModel, MasterDetailPageDetail>();
Register<FirstPageModel, FirstPage>();
// Register services (services are registered as singletons default)
}
public static T Resolve<T>() where T : class
{
return _container.Resolve<T>();
}
public static Page CreatePageFor(Type pageModelType)
{
var pageType = _lookupTable[pageModelType];
var page = (Page)Activator.CreateInstance(pageType);
var pageModel = _container.Resolve(pageModelType);
page.BindingContext = pageModel;
return page;
}
static void Register<TPageModel, TPage>() where TPageModel : PageModelBase where TPage : Page
{
_lookupTable.Add(typeof(TPageModel), typeof(TPage));
_container.Register<TPageModel>();
}
}
PageModelBase.cs 和 ExtendedBindableObject.cs 与教程相同。当我 运行 模拟器时,我得到了这个结果:
我以为会有一个 DateTime 字符串和一个零时间跨度。我觉得 FirstPageModel 中的数据根本没有初始化。我还尝试在构造函数中设置 CurrentStartTime。得到了相同的结果。
我是否错过了在首页上显示 CurrentStartDate 和 RunningTotal 的内容?任何帮助或提示将不胜感激。提前致谢。
我相信您的 MasterDetailPageDetail
没有被初始化(或者 BindingContext
没有被初始化)。本教程中的体系结构在导航事件期间设置 BindingContext
。由于您在 MasterDetailPage
的 XAML 中显式设置了 Master 和 Detail,因此未显式设置绑定上下文并且未调用 InitializeAsync
方法。
在导航服务中添加初始化
使用以下方法更新 NavigationService
的 NavigateToAsync
方法,就在 if (setRoot)
之后和 if (page is TabbedPage tabbedPage)
之前:
if (page is MasterDetailPage mdp)
{
App.Current.MainPage = mdp;
// We need to initialize both Master's BindingContext as
// well as Detail's BindingContext if they are PageModelBases
if (mdp.Master.BindingContext is PageModelBase masterPM)
{
await masterPM.InitializeAsync(null);
}
if (mdp.Detail.BindingContext is PageModelBase detailPM)
{
await detailPM.InitializeAsync(null);
}
}
else if (page is TabbedPage tabbedPage)
// .... existing code here
确保您正在为页面(主页和详细信息)设置绑定上下文。您可以在 XAML 中执行此操作,或者像在 Tabbed Page
中那样解析 MasterDetailPage 和 PageModel如果需要,我可以制作一个视频来介绍这个,并将其添加到系列中。希望这对您有所帮助!
干杯, 帕特里克