繁体   English   中英

Xamarin.Forms 绑定在初始值后未更新

[英]Xamarin.Forms binding not updating after initial value

我将 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:

为了进一步描述奇怪之处,我在我的ContentPage中添加了一个Label ,其中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设置为什么)。 但是,实际显示的页面只显示此标签中的文本最初设置的内容。

是的,已经清洁/建造了大约一百万次。

编辑2(进一步的怪异):

我把它放在代码隐藏中,以便它在页面加载期间运行:

var binding_context = (BindingContext as BuggyViewModel);
if (binding_context != null)
{
    Device.BeginInvokeOnMainThread(() =>
    {
        binding_context.RefreshTitleCommand.Execute(null);
    });
}

这再次更改了调试器中的值,但这些更改不会反映在显示的页面中。

然后我在页面上添加了一个按钮并将其绑定到RefreshTitleCommand ,然后哇! 页面更新其显示。

不幸的是我不能使用这个。 它不仅令人难以置信,而且我不能让用户按下按钮来让页面显示它在加载时的含义。

我想知道 MvvmCross 或 Xamarin 是否有一些缓存。

回答

您需要在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;
    });
}

我对 Xamarin 完全没有任何经验(但我确实想在将来尽可能熟悉 UWP 时尝试一下),但我想数据绑定过程的工作方式应该与我使用的类似到那里...

您提到您对页面首次加载时设置的值没有任何问题,但是当您实际更新这些值时,没有“链接”到可视层,尽管在调试时您实际上看到该值被设置为某些东西与初始状态完全不同。 由于您正在处理仅属性的视图模型(例如 UWP 中的集合是需要公开的另一级事件),因此 RaisePropertyChanged 似乎是正确的选择。

我无法理解的是,如果当您第一次创建页面时,您正在创建的绑定至少被指定为单向模式,因此当调用它们的设置访问器方法时,您的视图模型属性中的更改会传播到您的 UI 上。

您正在将页面上下文设置为 viewmodel(每个 i 图都与 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));
        }
   }

我正在从其属性之一的属性更改事件中设置控件绑定上下文。

这使我的控件停止跟踪更改,尽管其他所有内容仍然正确绑定,并且控件最初也会正确绑定(第一次很好,进一步的更改不会再次触发属性更改事件)。

page.BindingContext = viewModel;

暂无
暂无

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

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