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

VS2017 中 Xamarin 的演练 – 第一部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.94/5 (8投票s)

2017 年 7 月 20 日

CPOL

8分钟阅读

viewsIcon

54318

在微软 Xamarin in Visual Studio 教程的基础上,我已将其更新至 VS2017,并进行了一些进一步的改进。

引言

当我尝试使用微软的教程(https://docs.microsoft.com/en-au/visualstudio/cross-platform/build-apps-with-native-ui-using-xamarin-in-visual-studio)自学 Xamarin 时,遇到了困难。该教程非常基础,并且许多选项和库都已经发生了变化。因此,我决定创建这个迷你系列来扩展原有的指南。

许多提供指导的文章都非常基础。我想从 Xamarin 的基础知识开始,更新它们至当前(2017 年 6 月),然后开始扩展功能,使其不仅仅是一个“如何操作”指南,而是如何编写一个体面的应用程序——而不仅仅是示例代码片段。

设置、安装和模拟器技巧

我目前使用的是 Visual Studio 2017 社区版。

有关说明,请参阅 https://docs.microsoft.com/en-au/visualstudio/cross-platform/setup-and-install

我还应该提到,我遇到了一个奇怪的 bug,安装 VS2017 和 Xamarin 后无法启动 Windows。所以请务必先备份您的计算机,以防万一。

https://connect.microsoft.com/VisualStudio/feedback/details/1608843/after-installation-of-vs2015-windows-10-will-not-boot

您需要从 http://openweathermap.org/appid 获取免费的 API 密钥。

我有一个 Windows Phone Lumia 1520 用于测试 UWP 版本,并且我正在使用 Android 模拟器(您可以在 Visual Studio 的 工具、Android、Android 仿真管理器菜单中找到它们)。

我确实在 Android 模拟器上遇到了一些问题,直到我简化了配置(ARM 手机、ARM 平板电脑、x86 手机和 x86 平板电脑)并全部设置为运行 7.1.1,API 级别 25。

我不是 Android 专家,所以我确信会有很多人提出比这更好的解决方案的建议。

在此编辑器中,您也可以启动模拟器。同样,我在模拟器上也遇到了一些问题(也许是因为我的机器只有 4GB 内存),但是只要我这样做,一切都正常:

  1. 手动启动模拟器并让它保持运行(而不是让 VS 尝试启动它);
  2. 手动构建然后部署到模拟器;
  3. 通过 VS 调试/运行应用程序。

我还没有尝试过 iOS。有机会时,我将在 iPhone 上安装 Xamarin Live Player,并更新这篇文章……

最后一点,对于 VS 和 Xamarin,XAML 目前不会像普通的 UWP / WPF 应用程序那样显示一个“屏幕”。但是,您可以通过打开“视图”、“其他窗口”、“Xamarin Forms Previewer”来查看应用程序在设计时的样子。

创建您的项目

文件、新建项目。在“Visual C#”模板部分,您应该找到(并选择)“跨平台”。在右侧选择“跨平台应用 (Xamarin)”,然后输入名称 WeatherApp 并点击“确定”。

 

在下一个屏幕上选择“空白应用”、“Xamarin.Forms”和“可移植类库 (PCL)”。

Visual Studio 现在将创建您的解决方案,并询问您要定位哪个版本的 Windows。我选择 Windows 10 November Update (10.0; Build 10586) 作为最低版本,Windows 10 Creators Update (10.0; Build 15063) 作为目标版本。

Visual Studio 会要求您输入 Mac 的详细信息以进行 iOS 开发…… 在您想要为 iOS 编译/部署/调试之前,可以忽略此步骤。

创建的解决方案将包含四个项目:

  • WeatherApp
  • WeatherApp.Android
  • WeatherApp.iOS
  • WeatherApp.UWP (通用 Windows)

注意:第一个项目以前被称为“WeatherApp (Portable)”。

再做一些整理工作……

在“解决方案资源管理器”中,右键单击 WeatherApp.Android 项目,然后转到“属性”。

  1. 在“Android Options”选项卡上,取消选中“使用快速部署 (仅限调试模式)”。
  2. 在“Application”选项卡上,确保“Compile using Android version”设置为 7.1,以匹配上面的模拟器设置。
  3. 保存并关闭“属性”。

在“解决方案资源管理器”中,右键单击“WeatherApp”(4 个项目)解决方案,然后选择“管理解决方案的 NuGet 程序包”。

在“浏览”选项卡的搜索框中,输入 Newton.Json,然后在右侧勾选“项目”并点击“安装”按钮。

您可能会收到一些弹出窗口,要求您“审阅更改”和“接受许可”。点击“确定”/“我接受”……

在搜索框中输入 Microsoft.Net.Http,然后点击“安装”按钮。您可能会收到一些弹出窗口,要求您“审阅更改”和“接受许可”。点击“确定”/“我接受”……

在撰写本文时,模板中存在一些过时的 NuGet 程序包,应予以更新。

转到“Consolidate”(如果等待应用更新)。清除搜索框,以便显示程序包,选择程序包并点击“安装”。

转到“Update”(即使那里没有数字表示有等待更新)。勾选“全选”并点击“更新”。同样,您可能会收到一些弹出窗口,要求您“审阅更改”和“接受许可”。点击“确定”/“我接受”……

此时,我收到一条消息,提示 Visual Studio 需要重新启动。点击“重新启动”,当 VS 重新启动且解决方案重新加载后,在“解决方案资源管理器”中,右键单击“WeatherApp”(4 个项目)解决方案,然后选择“管理解决方案的 NuGet 程序包”。转到“Update”选项卡,选择任何需要更新的程序包,然后完成更新过程。

此时,我建议您构建解决方案,启动模拟器或连接您的设备,部署项目,然后通过调试器进行运行。我的示例是使用 Android 项目(设置为启动项)到模拟器——但无论您选择哪个项目(UWP、Android 或 iOS)都没关系。

请注意,VS 中“播放”按钮旁边的模拟器选择与我之前启动并部署到的模拟器匹配。

要运行(例如)在我的 Windows Phone 上,选择 ARM 作为 CPU,WeatherApp.UWP 作为项目,然后运行(设备)。

我建议让模拟器保持运行(如果您正在使用一个),只需在 Visual Studio 中点击“停止”。

 

最后,我们准备开始编码了……

代码

这个应用程序实际会做什么?很简单,对于给定的邮政编码,应用程序将显示从 Open Weather Map 网站返回的多个天气属性。

首先,我们需要一个类来包含返回的信息。右键单击 WeatherApp 项目,然后选择 Add,然后选择 Class(通常在最底部)。将类命名为 Weather.cs 并点击“Add”。
 
用以下代码替换模板代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WeatherApp
{
    public class Weather
    {
        public string Title { get; set; }
        public string Temperature { get; set; }
        public string Wind { get; set; }
        public string Humidity { get; set; }
        public string Visibility { get; set; }
        public string Sunrise { get; set; }
        public string Sunset { get; set; }

        public Weather()
        {
            //Because labels bind to these values, set them to an empty string to 
            //ensure that the label appears on all platforms by default. 
            this.Title = " ";
            this.Temperature = " ";
            this.Wind = " ";
            this.Humidity = " ";
            this.Visibility = " ";
            this.Sunrise = " ";
            this.Sunset = " ";
        }
    }
}

=> 这是一个简单的类,将保存从 Open Weather Map 网站返回的数据。构造函数将所有变量默认设置为空字符串。

 

接下来,我们需要一个类来将我们的请求传递给 Open Weather Map 网站并返回响应供我们的应用程序使用。

右键单击 WeatherApp 项目,然后选择 Add,然后选择 Class。将类命名为 DataService.cs 并点击“Add”。

using Newtonsoft.Json;
using System.Threading.Tasks;
using System.Net.Http;

namespace WeatherApp
{
    public class DataService
    {
        public static async Task<dynamic> getDataFromService(string pQueryString)
        {
            HttpClient client = new HttpClient();
            var response = await client.GetAsync(pQueryString);

            dynamic data = null;
            if (response != null)
            {
                string json = response.Content.ReadAsStringAsync().Result;
                data = JsonConvert.DeserializeObject(json);
            }

            return data;
        }
    }
}
=> 这是一个简单的类,它接收要发送到 Open Weather Map 网站的参数字符串,创建一个 HTTP 客户端来发送该信息,然后等待响应。
 
发送到 HTTP 客户端的参数字符串就是对网站的调用,其中邮政编码、AppId 和 Units 是参数:
 
 
 
当从网站收到响应后,Json 库会将字符串转换为一个基本的键/值列表对象,我们可以轻松地“查询”它来将我们的结果放入变量中。如果您查看这个示例返回的字符串:
{"coord":{"lon":-103.14,"lat":39.69},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"base":"stations","main":{"temp":58.96,"pressure":1016,"humidity":39,"temp_min":48.2,"temp_max":64.4},"visibility":16093,"wind":{"speed":6.93,"deg":140},"clouds":{"all":1},"dt":1497596100,"sys":{"type":1,"id":533,"message":0.0076,"country":"US","sunrise":1497612254,"sunset":1497666163},"id":0,"name":"Anton","cod":200}
如果您查看这个返回的字符串数据,您可以看到网站的响应,例如温度是 58.96。下一个代码块展示了我们如何查询 Json 结果对象来填充我们内部的数据结构。
 
-----
 
我们需要一个类来调用我们的 DataService 并填充我们内部的 Weather 类。
 
右键单击 WeatherApp 项目,然后选择 Add,然后选择 Class。将类命名为 Core.cs 并点击“Add”。
using System; 
using System.Threading.Tasks; 
using WeatherApp;

namespace WeatherApp 
{ 
    public class Core 
    { 
        public static async Task<Weather> GetWeather(string pZipCode) 
        { 
            //Sign up for a free API key at http://openweathermap.org/appid 
            string key = "YOUR KEY HERE"; 
            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 != "YOUR API KEY HERE")
            {
                throw new ArgumentException("You must obtain an API key from openweathermap.org/appid and save it in the 'key' variable.");
            }

            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 
            { 
                return null; 
            } 
        } 
    } 
}
=> 在这个代码块中,我们首先创建参数字符串(见上文),该字符串将发送到 getDataFromService 函数。
 
通过从网站返回的响应,我们解析键/值列表,将值放入我们内部的 Weather 类。
 
当您注册 Open Weather Map 网站时,您会收到一封电子邮件,其中包含您的 API 密钥。将该密钥复制到上面标记为“YOUR API KEY HERE”的两个位置。注意:您需要包含密钥的 `&APPID=` 部分。
 
------
 
打开 WeatherApp MainPage.xaml 并用以下 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"
             x:Class="WeatherApp.MainPage">

    <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"/>
                    <Button Text="Get Weather" x:Name="btnGetWeather"  VerticalOptions="Start"/>
                </StackLayout>
            </StackLayout>
        </StackLayout>
       
        <StackLayout VerticalOptions="StartAndExpand">
            <Label Text ="Location" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="" Margin="10,0,0,10" x:Name="txtLocation"/>
            <Label Text ="Temperature" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="" Margin="10,0,0,10" x:Name="txtTemperature"/>
            <Label Text ="Wind Speed" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="" Margin="10,0,0,10" x:Name="txtWind"/>
            <Label Text ="Humidity" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="" Margin="10,0,0,10" x:Name="txtHumidity"/>
            <Label Text ="Visibility" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="" Margin="10,0,0,10" x:Name="txtVisibility"/>
            <Label Text ="Sunrise" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="" Margin="10,0,0,10" x:Name="txtSunrise"/>
            <Label Text ="Sunset" TextColor="#FFA8A8A8" FontSize="14"/>
            <Label Text ="" Margin="10,0,0,10" x:Name="txtSunset"/>
        </StackLayout>
    </StackLayout>
</ContentPage>
此时保存所有内容。
 
=> 这个 XAML 非常基础,包含一个标题部分,允许您输入邮政编码并点击按钮开始该过程。然后是一个详细部分,用于显示结果。
 
----
 
现在打开 MainPage.xaml.cs,在 MainPage() 构造函数中添加以下代码以将一个过程链接到按钮的按下事件:
 
public MainPage()
{
   InitializeComponent();

   btnGetWeather.Clicked += btnGetWeather_Click;
}
然后添加按钮按下代码:
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";
         }
     }
}    

=> 按钮按下代码调用函数来使用从网站返回的数据填充 weather 变量,然后用结果更新每个屏幕控件。

----

如果您现在构建并部署此项目,您将得到一个显示天气数据的可用应用程序!我不住在美国,我使用邮政编码 80801 进行测试。

好了,您就拥有了一个可以部署到 Windows UWP、Android 手机和 iOS 的应用程序——使用单一代码流!

通常,教程会在此时停止,但我认为这还不是一个应用程序,只是一个代码示例。

很快我将发布下一篇文章,并继续将其构建成一个真正的应用程序。

https://codeproject.org.cn/Articles/1192180/Walkthrough-for-Xamarin-in-VS2017-Part-Two

Walkthrough of Xamarin in VS2017 - Part One - CodeProject - 代码之家
© . All rights reserved.