【问题标题】:Xamarin databinding MVVM not rendering text content on UIXamarin 数据绑定 MVVM 未在 UI 上呈现文本内容
【发布时间】:2021-11-05 07:35:30
【问题描述】:

这是我在 Xamarin 中使用 MVVM 的第一次测试。我正在尝试显示由 Web API 提供的被标记为 ExampleList 的对象(示例)列表。它接收正确的数据和对象结构,API 和应用模型几乎是 1:1。

当页面加载时,它会显示所有 9 个正确的元素,尽管这些元素的文本不会显示。我知道这个数据是正确的,因为当单击一个元素时,它会在显示警报中显示对象的字段。

我确定问题出在标记上,正如您在下面看到的,它被声明为: object.property ,通常我会将其声明为没有对象的属性,但是在执行此方法时,应用程序将无法编译并显示以下错误:

"Severity   Code    Description Project File    Line    Suppression State
Error   XFC0045 Binding: Property "ID" not found on "App2.ViewModels.ExampleListViewModel". App2    E:\App2\App2\App2\Views\ExampleListPage.xaml    22  " 

据我所知,它应该假设并绑定属性对象的父对象是视图模型中的一个示例。

我还尝试在 ViewModel 中手动声明对象字段,这确实可以编译,但具有类似的不显示文本的结果。

关于这个问题的奇怪之处在于,当应用程序运行时,文本单元格属性声明可以从 Object.Property 更改为 Property 并显示文本,但在重新编译时会再次产生上述相同的错误。

xaml的代码如下:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="App2.Views.ExampleListPage"
             xmlns:viewmodels="clr-namespace:App2.ViewModels"
             x:DataType="viewmodels:ExampleListViewModel" 
             Title="Example List">


    <ContentPage.BindingContext>
        <viewmodels:ExampleListViewModel/>
    </ContentPage.BindingContext>

    <ListView x:Name="listView" Margin="20" SelectedItem="{Binding SelectedElement, Mode=TwoWay}" ItemsSource="{Binding ExampleList}"
              RefreshCommand="{Binding RefreshCommand}" IsRefreshing="{Binding IsBusy, Mode=OneWay}" IsPullToRefreshEnabled="True">
        <ListView.ItemTemplate>
            <DataTemplate>
                <TextCell Text="{Binding ID}" Detail="{Binding Example.Desc}"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>

页面背后的代码如下:

using App2.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace App2.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class ExampleListPage : ContentPage
    {
        public ExampleListPage()
        {
            InitializeComponent();
            BindingContext = new ExampleListViewModel();
        }
    }
}

该页面的视图模型如下:

using App2.Models;
using System;
using System.Collections.Generic;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;

namespace App2.ViewModels
{
    public class ExampleListViewModel : BindableObject // < this is needed for mvvm
    {
        public ExampleListViewModel()
        {
            //buttons delcared here also need to be declared below as icommands
            ListElementSelect = new Command(ElementSelect);
            RefreshCommand = new Command(OnRefresh);
            AddElement = new Command(OnAddElement);

            OnRefresh(); // unsure if ok to call here
        }
        /// <summary>
        /// private elements are here
        /// </summary>
        private example _example;
        private List<example> _exampleList = new List<example>();
        private bool _isBusy = false;
        private example _selectedElement;

        /// <summary>
        /// public elements are here
        /// </summary>
        /// 


        // this does fix compile error but doesn't display text still
        //public int ID {
        //    get => this.Example.ID;
        //}
        //private string _desc;
        //public string Desc {
        //    get => this.Example.Desc;
        //}

        public example SelectedElement
        {
            get => _selectedElement;
            set
            {
                if (value != null)
                {
                    Application.Current.MainPage.DisplayAlert("Selected", value.ID + value.Content, "x");
                    value = null;
                }
                _selectedElement = value;
                OnPropertyChanged();
            }
        }
        public example Example
        {
            get => _example;
            set
            {
                _example = value;
                OnPropertyChanged(nameof(Example));
            }
        }
        public List<example> ExampleList
        {
            get => _exampleList;
            set
            {
                _exampleList = value;
                OnPropertyChanged(nameof(ExampleList));
            }
        }
        public bool IsBusy
        {
            get => _isBusy;
            set
            {
                if (value == _isBusy) return;
                _isBusy = value;
                OnPropertyChanged(nameof(IsBusy));
            }
        }
        /// <summary>
        /// button commands are here
        /// </summary>
        public ICommand ListElementSelect { get; }
        public ICommand RefreshCommand { get; }
        public ICommand AddElement { get; }

        /// <summary>
        /// functions here
        /// </summary>
        /// 
        async void ElementSelect()
        {

        }

        async void OnAddElement()
        {

        }

        async void OnRefresh()
        {
            OnBusy();
            await Load(); // request list from API service
            await Task.Delay(2000); //artifical package delay
            OnBusy();
        }

        void OnBusy() // flip instance from true or false - for displaying loading symbol while performing requests
        {
            switch (IsBusy)
            {
                case true:
                    IsBusy = false;
                    break;
                case false:
                    IsBusy = true;
                    break;
            }
        }

        async Task Load()
        {
            string searchTerm = "";
            Expression<Func<example, bool>> searchLambda = x => x.Content.Contains("SearchTerm"); // instanciate searchLambda
            string stringLambda = searchLambda.ToString().Replace("SearchTerm", $"{searchTerm}"); //crashes here
            searchLambda = DynamicExpressionParser.ParseLambda<example, bool>(new ParsingConfig(), true, stringLambda);
            ExampleList = await App.DataService.GetAllAsync<example>();

            //listView.ItemsSource = ExampleList;

        }
    }
}

这是示例类:

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using App2.ViewModels;
using Newtonsoft.Json;

namespace App2.Models
{
    public class example
    {
        public event PropertyChangedEventHandler PropertyChanged; //this does event things
        public int ID { get; set; }
        public string Content { get; set; }
        public bool Status { get; set; }

        public string WebLink { get; set; }
        [JsonIgnore] // need newtonsoft
        public string Desc
        { // for list display, not transfered to backend
            get
            {
                return ID.ToString() + " - " + Content.ToString() + " - " + Status.ToString();
            }
        }

        public example(int id, string content, bool status, string webLink)
        { this.ID = id; this.Content = content; this.Status = status; this.WebLink = webLink; }

        public example() { }

        public string toString()
        {
            return ID.ToString() + " - " + Content.ToString() + " - " + Status.ToString() + " - " + WebLink.ToString();
        }
    }

}

【问题讨论】:

  • 首先,摆脱x:DataType="viewmodels:ExampleListViewModel" 。二、将Binding Example.Desc改为Binding Desc
  • 嗨 Jason,我知道示例。Desc 需要更改.正如您提到的那样删除数据类型标记确实有效,但我更好奇为什么这会阻止文本不显示。如果您能提供更多信息或链接来解释这一点,我们将不胜感激。你让我免于头疼!
  • DataType 给 VS 和编译器一个关于绑定中使用什么类型的提示,但它还不够“聪明”地意识到每个 row 的 BindingContext ListView 与整个页面的 BindingContext 不同。

标签: c# xamarin xamarin.forms mvvm


【解决方案1】:

您不需要删除页面级x:DataType 属性。您可以通过在 DataTemplate 本身上指定 x:DataType 属性来通知编译器 DataTemplate 的 DataType(请注意,您需要添加一个 xmlns 引用才能使其工作,类似于您的 xmlns:viewmodels 引用):

<DataTemplate x:DataType="models:example">
     <TextCell Text="{Binding ID}" Detail="{Binding Example.Desc}"/>
</DataTemplate>

至于没有显示任何项目的列表,我会仔细检查这一行:

ExampleList = await App.DataService.GetAllAsync<example>();

正在返回一个非空的项目列表。由于您没有在问题中包含该方法,因此我无法对其作用做出任何假设。

最后一点:您不一定需要在 ViewModel 中继承 BindableObject 才能使 MVVM 工作;只需INotifyPropertyChanged 就足够了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-10
    • 2018-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-14
    相关资源
    最近更新 更多