【问题标题】:Xamarin.Forms binding not updating after initial valueXamarin.Forms 绑定在初始值后未更新
【发布时间】:2018-04-17 11:51:32
【问题描述】:

我将 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() 之外,后面的代码并没有发生太多事情。

该页面通常在我的项目中映射到虚拟机(实际上不是“我的”项目,它是现有设计),它归结为这些(同样,简化的)代码行:

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 是否有一个公共字段来获取和设置属性?
  • 是的,抱歉,忘记在问题中添加它。我已经编辑了问题。
  • @Ash,删除代码隐藏代码。不需要它
  • 不是我想要表达的意思。是的,我知道我不need 它。试图表明事情是多么奇怪。绑定显然是有效的,但视图只是拒绝显示更新。
  • 因为你有 local:ContentPage,它可能做错了什么,但你没有显示代码。

标签: c# xamarin.ios xamarin.forms mvvmcross


【解决方案1】:

回答

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

【讨论】:

  • 也试过了,然后是干净/构建。仍然没有运气:/看看我所做的编辑。我知道绑定工作正常,因为在调试器中我可以看到标签的文本属性始终设置为正确的值。但是,它并没有反映在实际显示的页面上。在 iOS 模拟器和物理设备上试过这个。两者都有同样的问题。
  • @Ash 我在代码后面有更新代码。检查并告诉我
  • 哦,迫不及待想试试这个;似乎很有希望。任何与主线程相关的东西通常似乎总是能让 UI 的东西神奇地工作。会及时通知您。
  • @Ash, 添加 BuggyLabel.Text = binding_context.BuggyLabelText;在 BeginInvokeOnMainThread 方法中
  • 如果没有绑定,那么初始值怎么看起来不错呢?当用户事件(例如按钮点击)触发时,值如何更新?
【解决方案2】:

我对 Xamarin 没有任何经验(但我确实想在将来尽可能熟悉 UWP 时尝试一下),但我猜数据绑定过程的工作方式应该类似于我已经习惯了...

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

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

您正在将页面上下文设置为 viewmodel(每个 i 图都与 UWP/WPF 中的 DataContext 相同),因此您实际上可以使用 {Binding} 标记访问这些属性。但是 Xamarin 中此操作的默认模式是什么? (在 UWP 中它实际上是 OneWay,因此在这种情况下它可以正常工作......)。 我已经看到在 Xamarin 中它可能有点不同,因为您也有默认选项。可以这样吗?

附言。尽管我缺乏使用 Xamarin 的经验,但希望这对您有用。

编辑2 实现 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));
        }
   }

【讨论】:

  • 绑定方向不是问题。来自 VM 的更改正在传播到 View 属性,只是没有显示。
  • 事件,如果在调试时,通过检查视图中的属性绑定,您会看到具有更新值的属性,我认为您实际上无法得出结论,视图实际上已绑定到该值。
  • 那么当我按下一个绑定到 VM 中的命令的按钮时,它如何在显示中更新,该命令会更新相关的 VM 属性?参考:编辑 2。
  • 如果绑定模式实际上没有正确设置,或者您没有正确引发事件(事件应该清楚地识别已更改的属性),它实际上可能仍然具有先前属性的值。
  • 嗯,我正在使用 MvvmCross,这意味着我受限于它的导航系统。所以我不能只实现INotifyPropertyChanged,并以通常的方式做事。相反,我尝试实现IMvxNotifyPropertyChanged,而RaisePropertyChanged 是一个帮助器,根据 MvvmCross 文档,它本身使用了PropertyChanged
猜你喜欢
  • 1970-01-01
  • 2023-03-27
  • 2018-01-05
  • 2016-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多