【发布时间】: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