视图未按型号

时间:2018-06-13 09:00:05

标签: c# xaml mvvm xamarin.forms

我为这个冗长的问题道歉,但我希望你能坚持下去。我试图尽力包含所有相关的代码片段,以便准确解释我想要实现的目标。

我正在尝试将我的移动应用程序转换为MVVM设计模式,我在使用模型中发生的更改更新View时遇到了一些困难。

我已将我的应用程序拆分为经典的模型,视图,视图模型结构。

目前,我正在尝试将数据从我的模型SoundScapeData.cs传播到我的视图HomePage.xaml

我的HomePage.xaml代码隐藏文件如下所示:

namespace AX2018
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class HomePage : ContentPage
    {
        public HomePage()
        {
            InitializeComponent();
            BindingContext = new HomePageViewModel();
        }

        private void OnConnectButtonClicked(object sender, EventArgs e)
        {
            Bluetooth bluetooth = new Bluetooth();
            bluetooth.Connect();
        }
    }
}

如您所见,我使用BindingContext关键字绑定数据并将其绑定到HomePageViewModel类的新实例。

我正在连接蓝牙设备,因此拨打bluetooth.Connect()的电话会在点击Button视图中的HomePage.xaml时调用。然后,此蓝牙设备使用特定值更新应用程序。

我想强调的是,蓝牙连接运行良好,并且已经过验证,可以使用View previous 转换为MVVM设计模式。

我的ViewModel HomePageViewModel.cs,如下所示:

namespace AX2018
{
    public class HomePageViewModel
    {
        private SoundScapeData _soundScapeData;

        public SoundScapeData SoundScapedata { get { return _soundScapeData; } }

        public HomePageViewModel()
        {
            _soundScapeData = new SoundScapeData();
        }
    }
}

另一方面,我对如何设计视图模型感到有些困惑,因为模型SoundScapeData.cs目前非常简单。这个中间视图模型甚至是必要的吗?

这是我的SoundScapeData.cs模型:

namespace AX2018
{
    public class SoundScapeData : INotifyPropertyChanged
    {
        private double _spl;

        public double SPL
        {
            get { return _spl; }
            set
            {
                _spl = value;
                OnPropertyChanged();
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

从模型中可以看出,它实现了INotifyPropertyChanged接口,并具有相关函数来处理方法OnPropertyChanged()中的事件。

我的观点HomePage.xaml相当冗长 - 它由一个9 x 3网格组成,标签放置在某些位置。这些标签应该反映SoundScapeData.cs模型中的值(以及其他模型,但这是在路上)。以下是相关摘要:

<?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:local="clr-namespace:AX2018"
         x:Class="AX2018.HomePage">

<ContentPage.BindingContext>
    <local:HomePageViewModel/>
</ContentPage.BindingContext>

...

<Label Text="{Binding SoundScapedata.SPL}" 
FontSize="68" Style="{StaticResource ColoredLabel}" 
Grid.Row="4" 
Grid.Column="1" 
Margin="-20"></Label>

正如您所看到的,我已将视图HomePage.xaml中的数据绑定到视图模型HomePageViewModel.cs,该模型包含SoundScapeData.cs模型的实例,后者又实现{ {1}}界面。

据我所知,在使用MVVM设计模式时,这是数据绑定方面的正确方法。但是,SPL中的更改未反映在我的视图INotifyPropertyChanged中。

我在单独的类HomePage.xaml中更新SPL值,该类继承自Bluetooth.cs模型。以下是来自SoundScapeData.cs类的Connect()方法的摘录:

Bluetooth

再次,我为这个相当长的问题道歉,但我希望有人可以向我指出我做错了什么。我想重申一下,SPL值在View previous 中更新为转换为MVVM,所以我肯定在做数据绑定时出错了。

提前谢谢你,

kdhansen

2 个答案:

答案 0 :(得分:1)

我会在这里回答,因为评论太短暂无法解释。如果我误解了一些名字,请原谅我:P 让我们总结一下你的情景:

  • 主页是您的视图
  • HomePageViewModel是您的VM
  • SoundScapeData是您的模型

现在:

主页(查看)

我真的不知道在Xamarin oyu中是否必须复制此内容,请记住在后面的代码中设置它。

<ContentPage.BindingContext>
<local:HomePageViewModel/>
</ContentPage.BindingContext>

你也应该改变这个:

<Label Text="{Binding Spl}" <!--You dont need SoundScapeData anymore this is the public VM property -->
FontSize="68" Style="{StaticResource ColoredLabel}" 
Grid.Row="4" 
Grid.Column="1" 
Margin="-20"></Label>

<强> HomePageViewModel

它应该具有您需要的任意数量的属性。假设您的模型只有一个属性 SPL 。那就是你的模型现在你必须通过你的VM把它放到View中。因此,您的VM应该具有一个属性(公共/私有),以使其适应View和您的模型。

private string spl;
public string Spl
       {
         get {return this.spl;}
         set
            {
            if (this.spl != value)
            {
            this.spl = value;
            OnPropertyChanged("SPL);
            }

单击按钮并连接到蓝牙或其所做的任何事情(; P),因为它更改了模型,您必须更改VM属性。请记住,您的模型必须与VM中的实例相同。

所以...你应该附加到Model属性更改,以便更改VM属性,最好的方法是创建一个类(让我们尝试尽可能多地应用SOLID)

public class YourNewDataSource
{
    #region Attributes

    private readonly HomePageViewModel homePageViewModel;

    #endregion

    #region Public Methods


    public YourNewDataSource(HomePageViewModel homePageViewModel)
    {
      this.homePageViewModel = homePageViewModel;
    }

    public void Initialize()
    {
        this.homePageViewModel.SoundScapeData.PropertyChanged += this.OnHomePageModelPropertyChanged;
    }

    #endregion

    #region Event Handlers

    private void OnHomePageModelPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        switch (e.PropertyName)
        {
            case "SPL":
                this.homePageViewModel.Spl = this.homePageViewModel.SoundScapeData.Spl;
                break;
        }
    }

    #endregion
}

现在,当您启动应用程序或想要显示视图时,您必须使用VM创建新的YourNewDataSource:

public HomePage()
    {
        InitializeComponent();
        HomePageViewModel homePageViewModel = new HomePageViewModel();
        YourNewDataSource yourNewDataSource = new YourNewDataSource(homePageViewModel)
        BindingContext = homePageViewModel;
    }

看看,尝试一下,然后问你是不是得到了一些我在这里写的:D

我刚刚看了你的bluetooth.Connect。

我不知道蓝牙类是否必须从SoundScapeData继承,但是现在它不起作用会导致您在连接时丢失VM的模型。如果您不需要继承SoundScapeData,只需在connect方法中添加一个参数,并将Vm.SoundScapeData传递给它。

答案 1 :(得分:0)

您的问题是您正在更改模型的实例并失去视图的绑定。 Juan Carlos的回答是正确的,你不是应用纯MVVM,视图不应该知道模型,viewmodel负责使模型适应视图。 您在视图后面的代码中的OnConnectButtonClicked也不应用MVVM模式,您应该使用命令。链接http://www.learnmvvm.com/是一个很好的起点。

如果您将代码的示例项目上传到某个存储库,我将通过应用MVVM和SOLID原则对其进行修改。