简体   繁体   English

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

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

I'm binding the title of my Xamarin.Forms.ContentPage to a property BuggyTitle in my view model (VM).我将 Xamarin.Forms.ContentPage 的标题绑定到我的视图模型 (VM) 中的属性BuggyTitle The VM derives from MvxViewModel. VM 派生自 MvxViewModel。 Here's the simplified version:这是简化版:

BuggyPage.xaml: 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: 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
        }
    }
}

There's not much going on in the code behind other than a call to InitializeComponent() in the constructor.除了在构造函数中调用InitializeComponent()之外,后面的代码并没有发生太多事情。

The page is mapped to the VM generically in my project (not actually 'my' project, it's existing design), and it boils down to these (again, simplified) lines of code:该页面通常在我的项目中映射到 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;
}

The problem:问题:

When BuggyPage loads, I initially get the correct value for the title.BuggyPage加载时,我最初得到了正确的标题值。 Whenever it is displayed after that, even though I can see in the debugger that BuggyTitle is getting updated correctly, the change does not appear in the page.此后无论何时显示,即使我可以在调试器中看到BuggyTitle正在正确更新,但更改不会出现在页面中。

Question:问题:

Why don't updates to BuggyTitle get reflected in the page?为什么对BuggyTitle的更新不反映在页面中?

Edit 1:编辑1:

To further describe the weirdness, I added a Label to my ContentPage , with x:Name="BuggyLabel" and Text="{Binding BuggyLabelText}" .为了进一步描述奇怪之处,我在我的ContentPage中添加了一个Label ,其中x:Name="BuggyLabel"Text="{Binding BuggyLabelText}" In my code-behind, I added this:在我的代码隐藏中,我添加了这个:

var binding_context = (BindingContext as BuggyViewModel);
if (binding_context != null)
{
    BuggyLabel.Text = binding_context.BuggyLabelText;
}

I set a breakpoint at BuggyLabel.Text = .我在BuggyLabel.Text =处设置了一个断点。 It gets hit every time the page loads, and BuggyLabel.Text already seems to have the correct value (ie, whatever binding_context.BuggyLabelText is set to).每次页面加载时它都会被命中,并且BuggyLabel.Text似乎已经具有正确的值(即,无论binding_context.BuggyLabelText设置为什么)。 However, the actual page displayed only ever shows what the text in this label is initially set to.但是,实际显示的页面只显示此标签中的文本最初设置的内容。

And yes, have clean/built about a million times.是的,已经清洁/建造了大约一百万次。

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

I put this in the code-behind so that it runs during page load:我把它放在代码隐藏中,以便它在页面加载期间运行:

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

This again changes values in the debugger, but these changes don't get reflected in the displayed page.这再次更改了调试器中的值,但这些更改不会反映在显示的页面中。

I then added a button to the page and bound it to RefreshTitleCommand , and wham!然后我在页面上添加了一个按钮并将其绑定到RefreshTitleCommand ,然后哇! the page updates its display.页面更新其显示。

Unfortunately I can't use this.不幸的是我不能使用这个。 Not only is it incredibly hackish, I can't have the user pressing buttons to have the page display what it's meant to on load.它不仅令人难以置信,而且我不能让用户按下按钮来让页面显示它在加载时的含义。

I wonder if there's some caching going on with MvvmCross or Xamarin.我想知道 MvvmCross 或 Xamarin 是否有一些缓存。

Answer回答

You need to add RaisePropertyChanged in BuggyTitle property declaration.您需要在BuggyTitle属性声明中添加RaisePropertyChanged

ViewModel视图模型

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);
        }
    }
}

-----New Update------ -----新更新-----

Code behind code代码背后的代码

var binding_context = (BindingContext as BuggyViewModel);
if (binding_context != null)
{
    Device.BeginInvokeOnMainThread(() =>
    {
        BuggyLabel.Text = binding_context.BuggyLabelText;
    });
}

I don't have any experience at all with Xamarin (but i do want to try it out in the future when i get as comfortable as possible with UWP), but i guess the Data Binding process should be working similar to what i am used to there ...我对 Xamarin 完全没有任何经验(但我确实想在将来尽可能熟悉 UWP 时尝试一下),但我想数据绑定过程的工作方式应该与我使用的类似到那里...

You are mentioning that you have no problem with the values that are set when the page first loads, however when you actually update the values there's no "linking" to the visual layer, despite at debug time you actually seeing the value being set to something completely different from it's initial state.您提到您对页面首次加载时设置的值没有任何问题,但是当您实际更新这些值时,没有“链接”到可视层,尽管在调试时您实际上看到该值被设置为某些东西与初始状态完全不同。 Since you are dealing with properties-only viewmodel (Collections for instance in UWP are another level of events which need to be exposed), RaisePropertyChanged seems like the correct choice.由于您正在处理仅属性的视图模型(例如 UWP 中的集合是需要公开的另一级事件),因此 RaisePropertyChanged 似乎是正确的选择。

What i cannot understand is if when you first create your page, the Binding which you are creating is at least specified as One-Way mode, so changes in your viewmodel properties are propagated onto your UI when their set accessor methods are called.我无法理解的是,如果当您第一次创建页面时,您正在创建的绑定至少被指定为单向模式,因此当调用它们的设置访问器方法时,您的视图模型属性中的更改会传播到您的 UI 上。

You are setting your page context to viewmodel (each i figure is the same as DataContext in UWP/WPF), and therefore you can actually access those properties with the {Binding } markup.您正在将页面上下文设置为 viewmodel(每个 i 图都与 UWP/WPF 中的 DataContext 相同),因此您实际上可以使用 {Binding} 标记访问这些属性。 But what is the default mode for this operation in Xamarin ?但是 Xamarin 中此操作的默认模式是什么? (in UWP it is actually OneWay, and therefore it would work right of the bat for this situation ...). (在 UWP 中它实际上是 OneWay,因此在这种情况下它可以正常工作......)。 I have seen that in Xamarin it might be a bit different , since you also have the Default option.我已经看到在 Xamarin 中它可能有点不同,因为您也有 Default 选项。 Can that be it?可以这样吗?

PS. PS。 Hopefully this might be useful to you, despite my lack of experience with Xamarin.尽管我缺乏使用 Xamarin 的经验,但希望这对您有用。

Edit2 Implementing the INotifyPropertyChanged, 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));
        }
   }

I was setting a controls binding context from the property changed event of one of its properties.我正在从其属性之一的属性更改事件中设置控件绑定上下文。

This made my control stop tracking changes despite everything else binding correctly still, and the control would also correctly bind initially (first time is fine, further changes do not fire the property changed event again).这使我的控件停止跟踪更改,尽管其他所有内容仍然正确绑定,并且控件最初也会正确绑定(第一次很好,进一步的更改不会再次触发属性更改事件)。

page.BindingContext = viewModel;

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

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