簡體   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