我将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
处设置断点。每次页面加载时都会被命中,binding_context.BuggyLabelText
似乎已经有了正确的值(即,设置为var binding_context = (BindingContext as BuggyViewModel);
if (binding_context != null)
{
Device.BeginInvokeOnMainThread(() =>
{
binding_context.RefreshTitleCommand.Execute(null);
});
}
的任何值)。但是,仅显示的实际页面会显示此标签中的文本最初设置为的内容。
是的,干净/建造了大约一百万次。
编辑2(更奇怪):
我把它放在代码隐藏中,以便它在页面加载期间运行:
RefreshTitleCommand
这会再次更改调试器中的值,但这些更改不会反映在显示的页面中。
然后我在页面上添加了一个按钮并将其绑定到SELECT
t.product_id as ID,
product_category as Category,
product_name As Product,
product_price as Price,
product_decription as Description,
t.Stats as 'Status',
tblinventory.quantity
FROM tblproduct as t
INNER JOIN tblinventory
on t.product_id = tblinventory.product_id
WHERE
CONCAT(`t.product_id`,`product_category`,`product_name`,`t.Stats`) LIKE '%"2"%'
,并且!页面更新其显示。
不幸的是我无法使用它。它不仅令人难以置信的hackish,我不能让用户按下按钮让页面显示它在加载时的意义。
我想知道MvvmCross或Xamarin是否有一些缓存。
答案 0 :(得分:6)
您需要在 BuggyTitle 属性声明中添加 RaisePropertyChanged 。
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);
}
}
}
var binding_context = (BindingContext as BuggyViewModel);
if (binding_context != null)
{
Device.BeginInvokeOnMainThread(() =>
{
BuggyLabel.Text = binding_context.BuggyLabelText;
});
}
答案 1 :(得分:2)
我对Xamarin没有任何经验(但我确实希望将来能够尝试使用UWP时尽可能舒适),但我想数据绑定过程应该类似于什么我习惯了那里......
您提到您在页面首次加载时设置的值没有问题,但是当您实际更新值时,没有“链接”到可视层,尽管在调试时您实际看到的值是设置为与其初始状态完全不同的东西。 由于您正在处理仅属性的viewmodel(例如,UWP中的集合是需要公开的另一级事件),因此RaisePropertyChanged似乎是正确的选择。
我无法理解的是,如果您第一次创建页面时,您创建的Binding至少被指定为单向模式,那么当您的setmodel属性中的更改在调用其set访问器方法时会传播到您的UI上
您正在将页面上下文设置为viewmodel(每个图形与UWP / WPF中的DataContext相同),因此您可以使用{Binding}标记实际访问这些属性。但是Xamarin中此操作的默认模式是什么? (在UWP中,它实际上是OneWay,因此对于这种情况它可以正常工作......)。 我在Xamarin中看到它可能有点不同,因为你也有Default选项。可以吗?
PS。希望这可能对你有用,尽管我对Xamarin缺乏经验。
<强> EDIT2 强> 实现INotifyPropertyChanged,
public class BuggyViewModel : MvxViewModel, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
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);
}
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}