我将我的Xamarin.Forms.ContentPage的标题绑定到我的视图模型(VM)中的属性BuggyTitle
。 VM派生自MvxViewModel。这是简化版本:
BuggyPage.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<local:ContentPage Title="{Binding BuggyTitle}"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyProject.BuggyPage"
xmlns:local="clr-namespace:Xamarin.Forms;assembly=MyProject">
<ContentPage.Content NavigationPage.HasNavigationBar="false">
<Grid>
<ScrollView>
<!--and so on-->
</ContentPage.Content>
</local:ContentPage>
BuggyViewModel.cs:
namespace MyProject
{
[ImplementPropertyChanged]
public class BuggyViewModel : MvxViewModel
{
private Random _random;
public string BuggyTitle {get; set;}
public BuggyViewModel()
{
_random = new Random();
}
public override void Start()
{
base.Start();
BuggyTitle = "" + _random.Next(1000);
RaisePropertyChanged("BuggyTitle"); // this seems to make no difference
}
}
}
除了在构造函数中调用InitializeComponent()
之外,代码中没有太多内容。
页面一般映射到我的项目中的VM(实际上不是'我的'项目,它是现有的设计),它归结为这些(再次,简化的)代码行:
public static Page CreatePage(MvxViewModelRequest request)
{
var viewModelName = request.ViewModelType.Name;
var pageName = viewModelName.Replace ("ViewModel", "Page");
var pageType = (typeof (MvxPagePresentationHelpers)).GetTypeInfo ().Assembly.CreatableTypes().FirstOrDefault(t => t.Name == pageName);
var viewModelLoader = Mvx.Resolve<IMvxViewModelLoader>();
var viewModel = viewModelLoader.LoadViewModel(request, null);
var page = Activator.CreateInstance(pageType) as Page;
page.BindingContext = viewModel;
return page;
}
问题:
加载BuggyPage
时,我最初得到标题的正确值。每当它显示之后,即使我可以在调试器中看到BuggyTitle
正在更新正确,更改也不会出现在页面中。
题:
为什么BuggyTitle
的更新不会反映在页面中?
编辑1:
为了进一步描述这种怪异,我在Label
和ContentPage
上添加了x:Name="BuggyLabel"
。在我的代码隐藏中,我添加了这个:
Text="{Binding BuggyLabelText}"
我在var binding_context = (BindingContext as BuggyViewModel);
if (binding_context != null)
{
BuggyLabel.Text = binding_context.BuggyLabelText;
}
设置了一个断点。每次页面加载时它都会被击中,并且BuggyLabel.Text =
似乎已经具有正确的值(即,无论BuggyLabel.Text
设置为什么)。但是,仅显示的实际页面会显示此标签中的文本最初设置为的内容。
是的,干净/建造了大约一百万次。
编辑2(更奇怪):
我把它放在代码隐藏中,以便它在页面加载期间运行:
binding_context.BuggyLabelText
这再次更改调试器中的值,但这些更改不会反映在显示的页面中。
然后我在页面上添加了一个按钮,并将其绑定到var binding_context = (BindingContext as BuggyViewModel);
if (binding_context != null)
{
Device.BeginInvokeOnMainThread(() =>
{
binding_context.RefreshTitleCommand.Execute(null);
});
}
,并且!页面更新其显示。
不幸的是我不能用这个。它不仅令人难以置信的hackish,我不能让用户按下按钮让页面显示它在加载时的意义。
我想知道MvvmCross或Xamarin是否有一些缓存。
您需要在BuggyTitle属性声明中添加RaisePropertyChanged。
RefreshTitleCommand
namespace MyProject
{
[ImplementPropertyChanged]
public class BuggyViewModel : MvxViewModel
{
private Random _random;
string _BuggyTitle { get; set; }
public string BuggyTitle
{
get { return _BuggyTitle; }
set { _BuggyTitle = value; RaisePropertyChanged(() => BuggyTitle); }
}
public BuggyViewModel()
{
_random = new Random();
}
public override void Start()
{
base.Start();
BuggyTitle = "" + _random.Next(1000);
}
}
}
我对Xamarin没有任何经验(但我确实希望将来尝试使用UWP时尽可能舒适),但我想数据绑定过程应该与我使用的类似到那里......
您提到您在页面首次加载时设置的值没有问题,但是当您实际更新值时,没有“链接”到可视层,尽管在调试时您实际看到值被设置为某个值与它的初始状态完全不同。由于您正在处理仅属性的viewmodel(例如,UWP中的集合是需要公开的另一级事件),因此RaisePropertyChanged似乎是正确的选择。
我无法理解的是,当您第一次创建页面时,您创建的Binding至少被指定为单向模式,因此当调用set访问器方法时,viewmodel属性中的更改会传播到UI上。
您正在将页面上下文设置为viewmodel(每个图形与UWP / WPF中的DataContext相同),因此您可以使用{Binding}标记实际访问这些属性。但是Xamarin中此操作的默认模式是什么? (在UWP中,它实际上是OneWay,因此对于这种情况它可以正常工作......)。我在Xamarin中看到它可能有点不同,因为你也有Default选项。可以吗?
PS。希望这可能对你有用,尽管我对Xamarin缺乏经验。
Edit2实现INotifyPropertyChanged,
var binding_context = (BindingContext as BuggyViewModel);
if (binding_context != null)
{
Device.BeginInvokeOnMainThread(() =>
{
BuggyLabel.Text = binding_context.BuggyLabelText;
});
}