C# 数据绑定看不到viewmodel属性

C# 数据绑定看不到viewmodel属性,c#,mvvm,xamarin.forms,C#,Mvvm,Xamarin.forms,问题: 我正在尝试创建一个简单的MVVM视图设置。然而,无论我修改什么,我似乎都无法使PropertyChanged连接连接到.xaml,反之亦然 以下是观点: VpicInformationPage.xaml <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas

问题

我正在尝试创建一个简单的MVVM视图设置。然而,无论我修改什么,我似乎都无法使PropertyChanged连接连接到.xaml,反之亦然

以下是观点:

VpicInformationPage.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"
             xmlns:viewModels="clr-namespace:ScanditBarcodeScanner.ViewModels"
             x:Class="ScanditBarcodeScanner.Pages.VehicleOperationPages.VpicInformationPage">
    <!--<ContentPage.BindingContext>
        <viewModels:VpicInformationPageViewModel />
    </ContentPage.BindingContext>-->
    <StackLayout VerticalOptions="CenterAndExpand" Padding="5">
        <StackLayout.BindingContext>
            <viewModels:VpicInformationPageViewModel />
        </StackLayout.BindingContext>
        <Entry x:Name="VinEntry" Placeholder="VIN (Manual Entry)" />
        <Label Text="{Binding VinType.Make}" />
        <Label Text="{Binding VinType.Model}" />
        <Label Text="{Binding VinType.ModelYear}" />
        <Label Text="{Binding VinType.BodyClass}" />
        <Label Text="{Binding VinType.ErrorCode}" />
        <Button Text="Scan/Check VIN" Clicked="ScanOrCheckVin_Clicked"/>
    </StackLayout>
</ContentPage>
ViewModel:

VPICInformation PageViewModel.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Scandit.BarcodePicker.Unified;
using Scandit.BarcodePicker.Unified.Abstractions;
using ScanditBarcodeScanner.ViewModels;
using ScanditBarcodeScanner.ViewModels.Base;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace ScanditBarcodeScanner.Pages.VehicleOperationPages
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class VpicInformationPage : ContentPage, INotifyPropertyChanged
    {
        IBarcodePicker _picker;

        VpicInformationPageViewModel ViewModel;
        public VpicInformationPage()
        {
            InitializeComponent ();

            ViewModel = BindingContext as VpicInformationPageViewModel;

            ViewModel.VinType = VehicleApi.EmptyVinType;

            _picker = ScanditService.BarcodePicker;

            SetVinSettings();

            _picker.DidScan += OnDidScan;

            VinEntry.Text = "";
        }
    //...
    }
}
using ScanditBarcodeScanner.ViewModels.Base;
using System.ComponentModel;

namespace ScanditBarcodeScanner.ViewModels
{
    public class VpicInformationPageViewModel : ViewModelBase
    {
        #region VinType
        private VehicleApi.VinType _vinType;

        public VehicleApi.VinType VinType
        {
            get { return _vinType; }
            set
            {
                SetValue(ref _vinType, value, "VinType");
                Make = _vinType.Make;
                Model = _vinType.Model;
                ModelYear = _vinType.ModelYear;
                BodyClass = _vinType.BodyClass;
                ErrorCode = _vinType.ErrorCode;
            }
        }
        #endregion VinType

        #region VinType.Make
        private string _make;
        public string Make
        {
            get { return _vinType.Make; }
            private set
            {
                SetValue(ref _make, value);
            }
        }
        #endregion VinType.Make

        #region VinType.Model
        private string _model;

        public string Model
        {
            get { return _vinType.Model; }
            private set
            {
                SetValue(ref _model, value);
            }
        }
        #endregion VinType.Model

        #region VinType.ModelYear
        private string _modelYear;

        public string ModelYear
        {
            get { return _vinType.ModelYear; }
            private set
            {
                SetValue(ref _modelYear, value);
            }
        }
        #endregion VinType.ModelYear

        #region VinType.BodyClass
        private string _bodyClass;

        public string BodyClass
        {
            get { return _vinType.BodyClass; }
            private set
            {
                SetValue(ref _bodyClass, value);
            }
        }
        #endregion VinType.BodyClass

        #region VinType.ErrorCode
        private string _errorCode;

        public string ErrorCode
        {
            get { return _vinType.ErrorCode; }
            private set
            {
                SetValue(ref _errorCode, value);
            }
        }
        #endregion VinType.ErrorCode

        public VpicInformationPageViewModel()
        {
            _vinType = new VehicleApi.VinType();
        }
    }
}
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace ScanditBarcodeScanner.ViewModels.Base
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string PropertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }

        protected bool SetValue<T>(ref T BackingField, T Value, [CallerMemberName] string PropertyName = null)
        {
            if(EqualityComparer<T>.Default.Equals(BackingField, Value))
            {
                return false;
            }
            BackingField = Value;
            OnPropertyChanged(PropertyName);
            return true;
        }
    }
namespace ScanditBarcodeScanner
{
    public static class VehicleApi
    {
        public static VinType EmptyVinType { get; } = new VinType
        {
            Make = "Make",
            Model = "Model",
            ModelYear = "Model Year",
            BodyClass = "Body Class",
            ErrorCode = "Status/Error Code"
        };

        public class VinType
        {
            public string Make { get; set; }
            public string Model { get; set; }
            public string ModelYear { get; set; }
            public string BodyClass { get; set; }
            public string ErrorCode { get; set; }
        }
}
ViewModelBase:

ViewModelBase.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Scandit.BarcodePicker.Unified;
using Scandit.BarcodePicker.Unified.Abstractions;
using ScanditBarcodeScanner.ViewModels;
using ScanditBarcodeScanner.ViewModels.Base;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace ScanditBarcodeScanner.Pages.VehicleOperationPages
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class VpicInformationPage : ContentPage, INotifyPropertyChanged
    {
        IBarcodePicker _picker;

        VpicInformationPageViewModel ViewModel;
        public VpicInformationPage()
        {
            InitializeComponent ();

            ViewModel = BindingContext as VpicInformationPageViewModel;

            ViewModel.VinType = VehicleApi.EmptyVinType;

            _picker = ScanditService.BarcodePicker;

            SetVinSettings();

            _picker.DidScan += OnDidScan;

            VinEntry.Text = "";
        }
    //...
    }
}
using ScanditBarcodeScanner.ViewModels.Base;
using System.ComponentModel;

namespace ScanditBarcodeScanner.ViewModels
{
    public class VpicInformationPageViewModel : ViewModelBase
    {
        #region VinType
        private VehicleApi.VinType _vinType;

        public VehicleApi.VinType VinType
        {
            get { return _vinType; }
            set
            {
                SetValue(ref _vinType, value, "VinType");
                Make = _vinType.Make;
                Model = _vinType.Model;
                ModelYear = _vinType.ModelYear;
                BodyClass = _vinType.BodyClass;
                ErrorCode = _vinType.ErrorCode;
            }
        }
        #endregion VinType

        #region VinType.Make
        private string _make;
        public string Make
        {
            get { return _vinType.Make; }
            private set
            {
                SetValue(ref _make, value);
            }
        }
        #endregion VinType.Make

        #region VinType.Model
        private string _model;

        public string Model
        {
            get { return _vinType.Model; }
            private set
            {
                SetValue(ref _model, value);
            }
        }
        #endregion VinType.Model

        #region VinType.ModelYear
        private string _modelYear;

        public string ModelYear
        {
            get { return _vinType.ModelYear; }
            private set
            {
                SetValue(ref _modelYear, value);
            }
        }
        #endregion VinType.ModelYear

        #region VinType.BodyClass
        private string _bodyClass;

        public string BodyClass
        {
            get { return _vinType.BodyClass; }
            private set
            {
                SetValue(ref _bodyClass, value);
            }
        }
        #endregion VinType.BodyClass

        #region VinType.ErrorCode
        private string _errorCode;

        public string ErrorCode
        {
            get { return _vinType.ErrorCode; }
            private set
            {
                SetValue(ref _errorCode, value);
            }
        }
        #endregion VinType.ErrorCode

        public VpicInformationPageViewModel()
        {
            _vinType = new VehicleApi.VinType();
        }
    }
}
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace ScanditBarcodeScanner.ViewModels.Base
{
    public class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string PropertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }

        protected bool SetValue<T>(ref T BackingField, T Value, [CallerMemberName] string PropertyName = null)
        {
            if(EqualityComparer<T>.Default.Equals(BackingField, Value))
            {
                return false;
            }
            BackingField = Value;
            OnPropertyChanged(PropertyName);
            return true;
        }
    }
namespace ScanditBarcodeScanner
{
    public static class VehicleApi
    {
        public static VinType EmptyVinType { get; } = new VinType
        {
            Make = "Make",
            Model = "Model",
            ModelYear = "Model Year",
            BodyClass = "Body Class",
            ErrorCode = "Status/Error Code"
        };

        public class VinType
        {
            public string Make { get; set; }
            public string Model { get; set; }
            public string ModelYear { get; set; }
            public string BodyClass { get; set; }
            public string ErrorCode { get; set; }
        }
}
据我所知,我已经正确地实现了这些文件,并将它们正确地链接在一起。但是,每次运行应用程序时,我都会得到:

[0:]在“ScanditBarcodeScanner.ViewModels.VPICInformation PageViewModel”的目标属性“Xamarin.Forms.Label.Text”上找不到绑定:“VinType”属性

我已将视图绑定到ViewModel,并实现了
INotifyPropertyChanged

我尝试了许多解决方案,例如更改绑定上下文的设置位置(在ContentPage或StackLayout中,甚至两者都有),尝试不同的方法通知视图属性已更改,以及将标签绑定到
VinType
的底层成员,并允许
VinType
修改和提高为其更改的属性。我甚至尝试过PropertyChanged.Fody,但问题似乎不是通知代码,而是我将视图和ViewModel绑定在一起的方式,或者可能是属性的定义方式

问题:

绑定连接的哪个部分缺失/不正确?文档说明,我应该只能够使用我提供的代码访问
VinType
及其成员


上面这个项目的问题是,虽然它没有给出与我所说的相同的错误,但它仍然没有在应该更改字段时更改字段。

因此,这个问题的解决实际上相当简单

我只需要更改标签定义

格式字符串中需要有
{}
是因为在定义StringFormat时使用单引号
'
而不是双引号
'
时出现错误

编辑:虽然StringFormat问题很重要,但更大的问题是我使用的对象中的属性没有定义
get
set
。 只要一个类型是这样的:

public class TypeName
{
    public Type Property1 { get; set; }
    public Type Property2 { get; set; }
}

然后绑定引擎将能够查看和编辑成员属性。

我创建了一些代码进行测试,但它工作正常,没有问题,因此您能否提供一个简单的方法,在github上重现您的问题,然后在此处共享链接?在标签上使用双向绑定的目的是什么?@Jason我实际上不需要双向绑定,所以我删除了它。您发布的
ScanditBarcodeScanner.ViewModels.VPICInformation PageViewModel
类显然有一个公共
VinType
属性,因此,错误消息有意义的唯一方式是运行时的上下文对象是另一个版本的
ScanditBarcodeScanner.ViewModels.VPICInformation PageViewModel
类。如果没有可靠地再现问题的好方法,就不可能回答这个问题,但看起来您可能只是拥有一个旧的/不同版本的DLL,该类在运行时使用。尝试一个干净的构建,确保您拥有正确的版本。另外,我不熟悉XAML的Xamarin方言,但是在WPF/WinRT/等中。语法
VinType.make
VinType.Model
等只有在
VehicleApi.VinType
类具有这些属性时才是正确的。从视图模型中的包装器属性来看,似乎应该是这样的(如果这真的是问题的话,在任何情况下错误都会不同)。但是既然您有包装器属性,为什么不直接绑定到这些属性,而不是绑定到
VinType
版本呢?
VinType
类是否实现了
INotifyPropertyChanged