65.9K
CodeProject 正在变化。 阅读更多。
Home

VS2017 中 Xamarin 的演练 – 第二部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (3投票s)

2017年8月1日

CPOL

2分钟阅读

viewsIcon

17285

将 Microsoft Xamarin 天气教程提升到更高水平的第二部分。

引言

在第一部分中,我们创建了一个基本的应用程序来检索给定邮政编码的天气信息 - 请参阅 https://codeproject.org.cn/Articles/1191947/Walkthrough-of-Xamarin-in-VS2017-Part-One

....

下一步是什么?  目前,该应用程序使用代码隐藏 (C#) 从输入/编辑框中获取邮政编码,并将结果分配给文本框。  然而,在现代应用程序中,我们期望将这些文本属性绑定到我们的数据类,并让系统在数据更改时更新屏幕。

我们将更新应用程序以使用类似于 MVVM 的设计模式来使用 XAML 绑定。

首先,我们需要更新 Core.cs 以将视图(屏幕 XAML)链接到 ViewModel(core.cs)。 

using System.ComponentModel;

...


public class Core : INotifyPropertyChanged
并添加属性更改实现
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
    var changed = PropertyChanged;
    if (changed != null)
    {
       PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
=> 此声明和代码允许框架在底层数据更改时更新我们的屏幕。
 
并添加一个变量来保存邮政编码以及访问该变量的属性
        private string _ZipCode;
        public string ZipCode
        { get
            { return _ZipCode; }
          set
            { _ZipCode = value; }
        }
现在,我们更新 (MainPage) XAML 以填充邮政编码。  首先,添加一个名为 vm 的 ViewModel 引用。
<?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:WeatherApp"
             xmlns:vm="clr-namespace:WeatherApp"
             x:Class="WeatherApp.MainPage">
并添加一个 BindingContext 到 Core.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:WeatherApp"
             xmlns:vm="clr-namespace:WeatherApp"
             x:Class="WeatherApp.MainPage">

    <ContentPage.BindingContext>
        <vm:Core/>
    </ContentPage.BindingContext>
最后,更新编辑框的 Text 属性以绑定到 Core 类中的 ZipCode 属性。
                <StackLayout  Orientation="Horizontal" VerticalOptions="Start">
                    <Entry WidthRequest="100" x:Name="edtZipCode"  VerticalOptions="Start" Text="{Binding ZipCode, Mode=TwoWay}"/>
                    <Button Text="Get Weather" x:Name="btnGetWeather"  VerticalOptions="Start"/>
                </StackLayout>
此时,应用程序没有使用绑定的数据,但是可以通过在 ZipCode 属性的 get/set 方法上设置断点并运行来检查它是否有效。
 
在 Core.cs 中添加一个新的变量来保存任何错误状态和 Weather 类的一个实例
public string ErrorMessage { get; set; }
private Weather weather = new Weather();
将 GetWeather 函数更改为私有并添加第二个参数;
private static async Task<Weather> GetWeather(string pZipCode, string pErrorMessage)
并添加一个新的过程来调用原始函数。  由于我们没有设置单个参数,我们需要调用 OnPropertyChanged 事件来更新屏幕
public async void GetWeather()
{
      weather = await GetWeather(ZipCode, ErrorMesage);

      OnPropertyChanged("Title");
      OnPropertyChanged("Temperature");
      OnPropertyChanged("Wind");
      OnPropertyChanged("Humidity");
      OnPropertyChanged("Visibility");
      OnPropertyChanged("Sunrise");
      OnPropertyChanged("Sunset");
}
 
为我们要显示的每个值添加属性
        public string Title
        {
            get
            {
                return weather.Title;
            }
            set
            {
                weather.Title = value;
                OnPropertyChanged("Title");
            }
        }
        public string Temperature
        {
            get
            {
                return weather.Temperature;
            }
            set
            {
                weather.Temperature = value;
                OnPropertyChanged("Temperature");
            }
        }
        public string Wind
        {
            get
            {
                return weather.Wind;
            }
            set
            {
                weather.Wind = value;
                OnPropertyChanged("Wind");
            }
        }
        public string Humidity
        {
            get
            {
                return weather.Humidity;
            }
            set
            {
                weather.Humidity = value;
                OnPropertyChanged("Humidity");
            }
        }
        public string Visibility
        {
            get
            {
                return weather.Visibility;
            }
            set
            {
                weather.Visibility = value;
                OnPropertyChanged("Visibility");
            }
        }
        public string Sunrise
        {
            get
            {
                return weather.Sunrise;
            }
            set
            {
                weather.Sunrise = value;
                OnPropertyChanged("Sunrise");
            }
        }
        public string Sunset
        {
            get
            {
                return weather.Sunset;
            }
            set
            {
                weather.Sunset = value;
                OnPropertyChanged("Sunset");
            }
        }
将显示结果的 XAML 字段绑定到这些新属性
 
       <StackLayout VerticalOptions="StartAndExpand">
            <Label Text ="Location" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Title}" Margin="10,0,0,10" x:Name="txtLocation"/>
            <Label Text ="Temperature" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Temperature}" Margin="10,0,0,10" x:Name="txtTemperature"/>
            <Label Text ="Wind Speed" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Wind}" Margin="10,0,0,10" x:Name="txtWind"/>
            <Label Text ="Humidity" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Humidity}" Margin="10,0,0,10" x:Name="txtHumidity"/>
            <Label Text ="Visibility" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Visibility}" Margin="10,0,0,10" x:Name="txtVisibility"/>
            <Label Text ="Sunrise" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Sunrise}" Margin="10,0,0,10" x:Name="txtSunrise"/>
            <Label Text ="Sunset" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Sunset}" Margin="10,0,0,10" x:Name="txtSunset"/>
        </StackLayout>
在 MainPage.xaml.cs 中删除 代码隐藏链接的 按钮单击事件
public MainPage()
{
   InitializeComponent();
   //btnGetWeather.Clicked += btnGetWeather_Click;
}
在 MainPage.xaml 中绑定到 XAML 中的单击事件
      <Button Text="Get Weather" x:Name="btnGetWeather"  VerticalOptions="Start" Clicked="BtnGetWeather_Click"/>
 
最后,更新按钮单击事件以删除填充屏幕字段的代码隐藏并调用新的 GetWeather 过程
 
private async void btnGetWeather_Click(object sender, EventArgs e)
{

        //if (!String.IsNullOrEmpty(edtZipCode.Text))
        //{
        //    Weather weather = await Core.GetWeather(edtZipCode.Text);

        //    if (weather != null)
        //    {
        //        txtLocation.Text = weather.Title;
        //        txtTemperature.Text = weather.Temperature;
        //        txtWind.Text = weather.Wind;
        //        txtVisibility.Text = weather.Visibility;
        //        txtHumidity.Text = weather.Humidity;
        //        txtSunrise.Text = weather.Sunrise;
        //        txtSunset.Text = weather.Sunset;

        //        btnGetWeather.Text = "Search Again";
        //    }
        //}

        if (!String.IsNullOrEmpty(edtZipCode.Text))
           ((Core)BindingContext).GetWeather();
}
 
已经应用了很多更改,所以这是我们当前应用程序的列表
 
MainPage.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:local="clr-namespace:WeatherApp"
             xmlns:vm="clr-namespace:WeatherApp"
             x:Class="WeatherApp.MainPage">

    <ContentPage.BindingContext>
        <vm:Core/>
    </ContentPage.BindingContext>
   
    <StackLayout>
        <StackLayout Margin="10,0,0,0" VerticalOptions="Start" HorizontalOptions="Start" WidthRequest="400" BackgroundColor="#545454">
            <Label Text="Weather App" x:Name="lblTitle"/>

            <StackLayout HorizontalOptions="Start" Margin="10,10,0,0" VerticalOptions="Start"  WidthRequest="400">
                <Label Text="Search by Zip Code" FontAttributes="Bold" TextColor="White" Margin="10" x:Name="lblSearchCriteria" VerticalOptions="Start"/>
                <Label Text="Zip Code" TextColor="White" Margin="10" x:Name="lblZipCode"/>
                <StackLayout  Orientation="Horizontal" VerticalOptions="Start">
                    <Entry WidthRequest="100" x:Name="edtZipCode"  VerticalOptions="Start" Text="{Binding ZipCode, Mode=TwoWay}"/>
                    <Button Text="Get Weather" x:Name="btnGetWeather"  VerticalOptions="Start" Clicked="btnGetWeather_Click"/>
                </StackLayout>
            </StackLayout>
        </StackLayout>
       
        <StackLayout VerticalOptions="StartAndExpand">
            <Label Text ="Location" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Title}" Margin="10,0,0,10" x:Name="txtLocation"/>
            <Label Text ="Temperature" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Temperature}" Margin="10,0,0,10" x:Name="txtTemperature"/>
            <Label Text ="Wind Speed" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Wind}" Margin="10,0,0,10" x:Name="txtWind"/>
            <Label Text ="Humidity" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Humidity}" Margin="10,0,0,10" x:Name="txtHumidity"/>
            <Label Text ="Visibility" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Visibility}" Margin="10,0,0,10" x:Name="txtVisibility"/>
            <Label Text ="Sunrise" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Sunrise}" Margin="10,0,0,10" x:Name="txtSunrise"/>
            <Label Text ="Sunset" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="{Binding Sunset}" Margin="10,0,0,10" x:Name="txtSunset"/>
        </StackLayout>
    </StackLayout>
</ContentPage>
 
MainPage.xaml.cs
using System;
using Xamarin.Forms;

namespace WeatherApp
{
    public partial class MainPage : ContentPage
    {

        public MainPage()
        {
            InitializeComponent();
        }

        private void BtnGetWeather_Click(object sender, EventArgs e)
        {
            if (!String.IsNullOrEmpty(edtZipCode.Text))
              ((Core)BindingContext).GetWeather();
        }
    }
}
 
Core.cs
using System;
using System.ComponentModel;
using System.Threading.Tasks;

namespace WeatherApp
{
    public class Core: INotifyPropertyChanged
    {

        private string _ZipCode;
        public string ZipCode
        { get
            { return _ZipCode; }
          set
            { _ZipCode = value; }
        }
        public string ErrorMesage { get; set; }

        private Weather weather = new Weather();

        public async void GetWeather()
        {
            weather = await Core.GetWeather(ZipCode, ErrorMesage);

            OnPropertyChanged("Title");
            OnPropertyChanged("Temperature");
            OnPropertyChanged("Wind");
            OnPropertyChanged("Humidity");
            OnPropertyChanged("Visibility");
            OnPropertyChanged("Sunrise");
            OnPropertyChanged("Sunset");
        }

        public string Title
        {
            get
            {
                return weather.Title;
            }
            set
            {
                weather.Title = value;
                OnPropertyChanged("Title");
            }
        }
        public string Temperature
        {
            get
            {
                return weather.Temperature;
            }
            set
            {
                weather.Temperature = value;
                OnPropertyChanged("Temperature");
            }
        }
        public string Wind
        {
            get
            {
                return weather.Wind;
            }
            set
            {
                weather.Wind = value;
                OnPropertyChanged("Wind");
            }
        }
        public string Humidity
        {
            get
            {
                return weather.Humidity;
            }
            set
            {
                weather.Humidity = value;
                OnPropertyChanged("Humidity");
            }
        }
        public string Visibility
        {
            get
            {
                return weather.Visibility;
            }
            set
            {
                weather.Visibility = value;
                OnPropertyChanged("Visibility");
            }
        }
        public string Sunrise
        {
            get
            {
                return weather.Sunrise;
            }
            set
            {
                weather.Sunrise = value;
                OnPropertyChanged("Sunrise");
            }
        }
        public string Sunset
        {
            get
            {
                return weather.Sunset;
            }
            set
            {
                weather.Sunset = value;
                OnPropertyChanged("Sunset");
            }
        }

        public static async Task<Weather> GetWeather(string pZipCode, string pErrorMessage)
        {
            //Sign up for a free API key at http://openweathermap.org/appid 
            string key = "f3748390cfea7374d3fb0580af0cf4ae";
            string queryString = "http://api.openweathermap.org/data/2.5/weather?zip="
                + pZipCode + ",us&appid=" + key + "&units=imperial";
                       
            //Make sure developers running this sample replaced the API key
            if (key != "f3748390cfea7374d3fb0580af0cf4ae")
            {
                pErrorMessage = "You must obtain an API key from openweathermap.org/appid and save it in the 'key' variable.";
                return null;
            }

            try
            {
                dynamic results = await DataService.getDataFromService(queryString).ConfigureAwait(false);

                if (results["weather"] != null)
                {
                    Weather weather = new Weather();
                    weather.Title = (string)results["name"];
                    weather.Temperature = (string)results["main"]["temp"] + " F";
                    weather.Wind = (string)results["wind"]["speed"] + " mph";
                    weather.Humidity = (string)results["main"]["humidity"] + " %";
                    weather.Visibility = (string)results["weather"][0]["main"];

                    DateTime time = new System.DateTime(1970, 1, 1, 0, 0, 0, 0);
                    DateTime sunrise = time.AddSeconds((double)results["sys"]["sunrise"]);
                    DateTime sunset = time.AddSeconds((double)results["sys"]["sunset"]);
                    weather.Sunrise = sunrise.ToString() + " UTC";
                    weather.Sunset = sunset.ToString() + " UTC";
                    return weather;
                }
                else
                {
                    pErrorMessage = (string)results["message"];
                    return null;
                }
            }
            catch (Exception ex)
            {

                pErrorMessage = ex.Message;
                return null;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            var changed = PropertyChanged;
            if (changed != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

 

就这样,第二部分到此结束。从功能上讲,该应用程序与第一部分结束时没有更好或不同的表现 - 但我们的代码现在正在使用 Xamarin 应用程序中预期的现代技术。

让我们在第三部分中继续改进该应用程序 (https://codeproject.org.cn/Articles/1192813/Walkthrough-for-Xamarin-in-VS2017-Part-Three)。

© . All rights reserved.