XAML in Xamarin.Forms 基礎篇 電子書

XAML in Xamarin.Forms 基礎篇 電子書
XAML in Xamarin.Forms 基礎篇 電子書

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書
顯示具有 Xamarin.Essentials 標籤的文章。 顯示所有文章
顯示具有 Xamarin.Essentials 標籤的文章。 顯示所有文章

2019/04/13

使用 Xamarin.Essentials 進行檔案的同步或非同步存取

使用 Xamarin.Essentials 進行檔案的同步或非同步存取

Xamarin.Essentials 提供非常多豐富好用的功能,其中一個 [檔案系統協助程式] 功能,可以用於在應用程式所在的儲存空間內使用快取與資料目錄存取功能,一旦取得了該特定目錄下的檔案之後,就可以使用 .NET Framework 的 System.IO 命名空間下的 API,進行目錄的新增、建立或刪除,也可以針對檔案進行建立、寫入、刪除的功能。
當在開發一個行動應用 App 的時候,往往需要能夠將這個 App 當時的執行狀態寫入到裝置內,等到下次這個 App 再度重新啟動的時候,可以從裝置的儲存體中將資料讀取回來。絕大部分的行動開發者應該都會直接想使用到 SQLite 來做到這樣的事情,但是作者卻比較偏好輕巧的解決方案,那就是把程式中的 .NET 物件經過 Json.NET 序列化之後,把這些 JSON 文字寫入到檔案內;當想要取回的時候,只需要從檔案把這些字串讀取出來,接著透過 Json.NET 反序列化這些 JSON 字串,這樣,當初的 .NET 中使用的物件,就還原回來了。
由於 Xamarin.Forms 屬於一個 GUI 類型的應用程式,也就是說,所有要針對 UI 相關控制項變更的行為需求,都需要在 UI 執行緒 Thread 下來進行執行,否則會發生例外異常;因此,當要進行檔案存取的動作是在 UI 執行緒執行的時候,會有可能因為檔案存取的動作需要花費些時間,因此,會造成整個應用程式的畫面不太流暢;故,這個時候可以將這些檔案 I/O 相關的動作,以非同步的方式來使用,就可以解決這些問題。
在這個範例中,將會說明如何使用同步與非同步的方式來進行檔案的存取。

建立一個 使用 Xamarin.Essentials 進行檔案存取的 專案

  • 開啟 Visual Studio 2019 程式
  • 當 Visual Studio 2019 開始 視窗 出現之後,請點選左下角的 [建立新專案] 選項
  • 當 [建立新專案] 對話窗出現之後,請在中間最上方的搜尋文字輸入盒中輸入 [prism] 關鍵字,搜尋所有與 Prism 有關的專案樣板
  • 請選擇 [Prism Blank App (Xamarin.Forms)] 這個專案樣板
  • 當出現 [設定新的專案] 對話窗,請在 [專案名稱] 輸入 [FileAccess]
  • 最後點選該對話窗右下方的 [建立] 按鈕
  • 現在將會看到 [PRISM PROJECT WIZARD] 對話窗,請勾選 ANDROID, iOS, UWP 三個行動裝置平台,接著在底下 [Container] 下拉選單,選擇 Unity 項目
  • 最後,點選 [CREATE PROJECT] 按鈕,以便產生 Xamarin.Forms 專案

安裝需要用到的 PropertyChanged.Fody NuGet 套件

  • 當這個 Xamarin.Forms 專案建立成功之後,請在該方案中,找到 Xamarin.Forms 使用的專案,請在該專案中,使用滑鼠右擊 [相依性] 節點,選擇 [管理 NuGet 套件] 選項
  • 在 [NuGet: XXX] 視窗中,點選 [瀏覽] 標籤頁次,並且在下方的搜尋文字輸入盒中,輸入 [propertychanged.fody] 關鍵字,搜尋出這個 NuGet 套件
  • 當出現 [PropertyChanged.Fody] NuGet 套件,請點選該套件,並且點選右方的 [安裝] 按鈕,將這個套件安裝到 Xamarin.Forms 專案內
  • 請查看 Xamarin.Forms 專案內,並沒有 [FodyWeavers.xml] 這個檔案,因此,使用滑鼠右擊 Xamarin.Forms 專案節點,選擇 [建置] 選項
  • 當建置完成之後,在這個 Xamarin.Forms 專案內將會出現 [FodyWeavers.xml] 檔案

安裝需要用到的 Newtonsoft.Json NuGet 套件

  • 在 [NuGet: XXX] 視窗中,搜尋文字輸入盒中,輸入 [Newtonsoft.Json] 關鍵字,搜尋出這個 NuGet 套件
  • 當出現 [Newtonsoft.Json] NuGet 套件,請點選該套件,並且點選右方的 [安裝] 按鈕,將這個套件安裝到 Xamarin.Forms 專案內

安裝需要用到的 Xamarin.Essentials NuGet 套件

  • 在 [NuGet: XXX] 視窗中,搜尋文字輸入盒中,輸入 [Xamarin.Essentials] 關鍵字,搜尋出這個 NuGet 套件
  • 當出現 [Xamarin.Essentials] NuGet 套件,請點選該套件,並且點選右方的 [安裝] 按鈕,將這個套件安裝到 Xamarin.Forms 專案內

修正因為安裝 Xamarin.Essentials 帶來的錯誤

現在,可以從 Visual Studio 2019 的錯誤視窗中,看到底下的錯誤訊息
NU1107    偵測到 Xamarin.Android.Support.Compat 有版本衝突。請將 Xamarin.Android.Support.Compat 28.0.0.1 直接安裝/參考到專案 FileAccess.Android 來解決此問題。 
 FileAccess.Android -> FileAccess -> Xamarin.Essentials 1.1.0 -> Xamarin.Android.Support.Compat (>= 28.0.0.1) 
 FileAccess.Android -> Xamarin.Android.Support.Design 27.0.2.1 -> Xamarin.Android.Support.Compat (= 27.0.2.1).    FileAccess.Android    D:\Vulcan\GitHub\Xamarin2019\FileAccess\FileAccess\FileAccess.Android\FileAccess.Android.csproj    1
想要解決此一問題:
  • 使用滑鼠右擊方案節點,選擇 [管理方案的 NuGet 套件]
  • 點選 [更新] 標籤頁次
  • 勾選該標籤頁次內的所有項目
  • 點選右上方的更新按鈕,就可以升級這些套件到最新版本了

建立資料存取模型

  • 滑鼠右擊 Xamarin.Forms 專案,選擇 [加入] > [新增資料夾]
  • 將新增資料夾的名稱設定為 [DataModels]
  • 滑鼠右擊 [DataModels] 資料夾,選擇 [加入] > [類別]
  • 在 [新增項目] 對話窗下方的 [名稱] 欄位中,輸入 [UserInfo]
  • 點選右下方的 [新增] 按鈕
  • 將底下程式碼填入到這個新建立的類別檔案內
C Sharp / C#
using System;
using System.Collections.Generic;
using System.Text;

namespace FileAccess.DataModels
{
    public class UserInfo
    {
        public string Account { get; set; }
        public string Password { get; set; }
    }
}

修正 View 與 ViewModel

  • 在 Xamarin.Forms 專案內的 [Views] 資料夾內,找到 MainPage.xaml 檔案,並且打開它
  • 使用底下 XAML 語言替換掉這個檔案內的 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"
             x:Class="FileAccess.Views.MainPage"
             Title="檔案存取之開發">

    <StackLayout HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand"
                 Margin="20">
        <Label Text="帳號" />
        <Entry Text="{Binding Account}" Placeholder="請輸入帳號"/>
        <Label Text="密碼" />
        <Entry Text="{Binding Password}" Placeholder="請輸入密碼"/>
        <Label Text="檔案路徑" />
        <Button Text="清空輸入" Command="{Binding CleanCommand}"/>
        <Label Text="{Binding FilePath}" FontSize="14" />
        <StackLayout Orientation="Horizontal">
            <Button Text="同步讀取" Command="{Binding SyncFileReadCommand}"/>
            <Button Text="同步寫入" Command="{Binding SyncFileWriteCommand}"/>
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Button Text="簡易非同步讀取" Command="{Binding AsyncSimpleFileReadCommand}"/>
            <Button Text="簡易非同步寫入" Command="{Binding AsyncSimpleFileWriteCommand}"/>
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Button Text="非同步方法讀取" Command="{Binding AsyncFileReadCommand}"/>
            <Button Text="非同步方法寫入" Command="{Binding AsyncFileWriteCommand}"/>
        </StackLayout>
    </StackLayout>

</ContentPage>
  • 在 Xamarin.Forms 專案內的 [ViewModels] 資料夾內,找到 MainPageViewModel.cs 檔案,並且打開它
  • 使用底下 C# 敘述替換掉這個檔案內的 C# 敘述
C Sharp / C#
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FileAccess.ViewModels
{
    using System.ComponentModel;
    using System.IO;
    using System.Threading.Tasks;
    using FileAccess.DataModels;
    using Newtonsoft.Json;
    using Prism.Events;
    using Prism.Navigation;
    using Prism.Services;
    using Xamarin.Essentials;

    public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public string Account { get; set; }
        public string Password { get; set; }
        public string FilePath { get; set; }
        public DelegateCommand CleanCommand { get; set; }
        public DelegateCommand SyncFileReadCommand { get; set; }
        public DelegateCommand SyncFileWriteCommand { get; set; }
        public DelegateCommand AsyncSimpleFileReadCommand { get; set; }
        public DelegateCommand AsyncSimpleFileWriteCommand { get; set; }
        public DelegateCommand AsyncFileReadCommand { get; set; }
        public DelegateCommand AsyncFileWriteCommand { get; set; }
        string filename = "User.txt";
        private readonly INavigationService navigationService;

        public MainPageViewModel(INavigationService navigationService)
        {
            this.navigationService = navigationService;
            CleanCommand = new DelegateCommand(() =>
            {
                Account = ""; Password = "";
            });
            SyncFileReadCommand = new DelegateCommand(() =>
            {
                string path = Path.Combine(FileSystem.AppDataDirectory, "Datas");
                if (Directory.Exists(path) == false) Directory.CreateDirectory(path);
                FilePath = Path.Combine(path, $"Sync{filename}");
                if (File.Exists(FilePath))
                {
                    var content = File.ReadAllText(FilePath);
                    var userInfo = JsonConvert.DeserializeObject<UserInfo>(content);
                    Account = userInfo.Account;
                    Password = userInfo.Password;
                }
            });
            SyncFileWriteCommand = new DelegateCommand(() =>
            {
                string path = Path.Combine(FileSystem.AppDataDirectory, "Datas");
                if (Directory.Exists(path) == false) Directory.CreateDirectory(path);
                FilePath = Path.Combine(path, $"Sync{filename}");
                var userInfo = new UserInfo()
                {
                    Account = Account,
                    Password = Password,
                };
                var content = JsonConvert.SerializeObject(userInfo);
                File.WriteAllText(FilePath, content);
            });
            AsyncSimpleFileReadCommand = new DelegateCommand(async () =>
            {
                string path = Path.Combine(FileSystem.AppDataDirectory, "Datas");
                if (Directory.Exists(path) == false) Directory.CreateDirectory(path);
                FilePath = Path.Combine(path, $"AsyncSimple{filename}");
                if (File.Exists(FilePath))
                {
                    var content = await Task.Run(() =>
                    {
                        return File.ReadAllText(FilePath);
                    });
                    var userInfo = JsonConvert.DeserializeObject<UserInfo>(content);
                    Account = userInfo.Account;
                    Password = userInfo.Password;
                }
            });
            AsyncSimpleFileWriteCommand = new DelegateCommand(async () =>
            {
                string path = Path.Combine(FileSystem.AppDataDirectory, "Datas");
                if (Directory.Exists(path) == false) Directory.CreateDirectory(path);
                FilePath = Path.Combine(path, $"AsyncSimple{filename}");
                var userInfo = new UserInfo()
                {
                    Account = Account,
                    Password = Password,
                };
                var content = JsonConvert.SerializeObject(userInfo);
                await Task.Run(() =>
                {
                    File.WriteAllText(FilePath, content);
                });
            });
            AsyncFileReadCommand = new DelegateCommand(async () =>
            {
                string path = Path.Combine(FileSystem.AppDataDirectory, "Datas");
                if (Directory.Exists(path) == false) Directory.CreateDirectory(path);
                FilePath = Path.Combine(path, $"Async{filename}");
                if (File.Exists(FilePath))
                {
                    using (var fileStream = File.Open(FilePath, FileMode.Open))
                    {
                        using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
                        {
                            var content = await streamReader.ReadToEndAsync();
                            var userInfo = JsonConvert.DeserializeObject<UserInfo>(content);
                            Account = userInfo.Account;
                            Password = userInfo.Password;
                        }
                    }
                }
            });
            AsyncFileWriteCommand = new DelegateCommand(async () =>
            {
                string path = Path.Combine(FileSystem.AppDataDirectory, "Datas");
                if (Directory.Exists(path) == false) Directory.CreateDirectory(path);
                FilePath = Path.Combine(path, $"Async{filename}");
                using (var fileStream = File.Create(FilePath))
                {
                    using (var streamWriter = new StreamWriter(fileStream, Encoding.UTF8))
                    {
                        var userInfo = new UserInfo()
                        {
                            Account = Account,
                            Password = Password,
                        };
                        var content = JsonConvert.SerializeObject(userInfo);
                        await streamWriter.WriteAsync(content);
                    }
                }
            });
        }

        public void OnNavigatedFrom(INavigationParameters parameters)
        {
        }

        public void OnNavigatedTo(INavigationParameters parameters)
        {
        }

        public void OnNavigatingTo(INavigationParameters parameters)
        {
        }

    }
}

同步檔案讀寫的程式碼用法

當要寫入 .NET 物件到檔案內,在這裡範例中,將會把使用者輸入的帳號與密碼寫入到 UserInfo 類別物件內,接著使用 JsonConvert.SerializeObject(userInfo) 敘述將這個 .NET 物件轉換成為 JSON 字串表示內容。
然後透過 Xamarin.Essentinals 的 檔案系統協助程式 所提供的 API,取得該應用程式專屬的檔案存取沙箱目錄,這裡使用 FileSystem.AppDataDirectory 來取得,在這個沙箱目錄下所建立的檔案,僅僅提供該應用程式來存取,其他的應用程式、甚至該手機的使用者,無法看到與讀寫這些存在於沙箱目錄內的檔案。
在 Android 平台下,這個沙箱目錄會類似這樣的字串:/data/user/0/com.companyname.appname/files/Datas/SyncUser.txt
在 iOS 平台下,這個沙箱目錄會類似這樣的字串:/Users/vulcan/Library/Developer/CoreSimulator/Devices/EED56A19-C96E-4AF5-A5FA-83E07AB7E2A3/data/Containers/Data/Application/177E5C36-AC6F-450E-97EF-DA1AF562D29F/Library/Datas/SyncUser.txt
有了一個檔案系統下的目錄,接著便可以使用 System.IO 命名空間所提供的 File.WriteAllText API 來將剛剛產生的 JSON 字串寫入到指定的目錄下,而想要讀取特定檔案,可以使用 File.ReadAllText API 來讀取該檔案內的所有字串內容,再取得這些內容之後,便可以使用 JsonConvert.DeserializeObject(content) 這個敘述,將原先的 JSON 內容,還原成為 .NET 物件。
不過,在 .NET Standard 2.0 下,對於 System.IO 命名空間下的 File.WriteAllText / File.ReadAllText 這兩個 API,僅提供了同步呼叫的使用方式,若想要使用非同步方式來讀寫文字檔案內容,可以參考底下兩種做法;至於為什麼要使用非同的方式來進行檔案的讀寫工作呢?這一切都是要保持該 Xamarin.Forms 應用程式的 GUI 以最佳流暢的狀態下來執行。

簡單將同步檔案讀寫的程式碼轉換成為 Task 物件之用法

首先,最簡單的做法就是把這些同步運作的程式碼,指定到 Task.Run(() =>{}) 這個靜態方法內的委派方法內,因此,就會得到一個 Task 的物件,代表一個非同步的工作。現在,可以在這個方法內使用 await 關鍵字來等候剛剛取得的 工作 Task 類別物件,不過,要再方法函式內使用 await 關鍵字,需要在該方法回傳型別前,加入 async 這個修飾詞,並且該函式的迴船型別僅能為 Task, Task, void 這三種而已,若指定了其他型別,會造成編譯時期的錯誤。

非同步檔案讀寫的程式碼用法

當然 System.IO 命名空間內還提供了其他的關於檔案存取的非同步 API,在此,可以透過 File.Creeate 方法建立一個 FileStream 的物件,準備針對這個產生的檔案進行寫入的動作;有了這個 FileStream 物件,接著使用 StreamWriter 類別,使用剛剛的 FileStream 物件來建立起一個 StreamWriter 物件,如此,便可以使用 await streamWriter.WriteAsync 這樣的敘述來將文字內容,以非同步呼叫方式來寫入到檔案內。
反之,若要使用非同步方式讀取出檔案內的內容,可以使用 File.Open API,開啟指定的檔案(當然,最好還是事先檢查一下該檔案是否存在於該裝置的檔案系統內),便可以得到一個 FileStream 的物件,然後使用該物件來建立起一個 StreamReader 物件,如此,便可以使用 await streamReader.ReadToEndAsync() 這樣的非同步程式碼寫法,將檔案內的字串讀取出來,接著,使用 JsonConvert.DeserializeObject() 方法,把剛剛讀取出來的字串,還原成為 .NET 中的物件。

建置與執行和測試結果

現在,可以分別在不同的行動平台下來執行這個專案,只要將內容寫入到應用程式沙箱目錄下的檔案,除非該應用程式移除後又重新安裝起來,否則,該檔案會持續存在於這個裝置內,就算該應用程式進行升級動作,這些檔案也同樣的會持續存在。因此,可以嘗試將已經啟動的 App,強制進行關閉,讓這個 App 不再存在於裝置記憶體中,接著重新再度啟動,將會發現到,還是可以看到剛剛寫入的檔案內容。

在 Android 上進行測試

  • 請設定預設起始專案為 Android 的專案
  • 指定要在哪個模擬器或者實體裝置下來執行這個專案
  • 在工具列上點選率色三角形按鈕,執行這個 Android 專案
  • 底下是在 Android 平台下執行結果

在 iOS 上進行測試

  • 請設定預設起始專案為 iOS 的專案
  • 在工具列視窗中,在平台方案之下拉選單中,選擇 [iPhoneSimulator] 這個選項
  • 在平台方案右方綠色三角形之啟動按鈕,點選該按鈕右方的下拉選單的黑色三角形符號,現在可以看到該 Mac 電腦上所有可用的模擬器,在此選擇 [iPhone 8 iOS 12.2] 這個選項
  • 現在可以直接點選剛剛的綠色按鈕,啟動這個專案在 iOS 模擬器上來執行
  • 底下是在 iOS 平台下執行結果




2018/08/07

Xamarin.Essentials 體驗 6 : Connectivity 連線

根據 Xamarin.Essentials 官方文件中指出:Xamarin.Essentials 跨平台應用程式開發介面的開發人員提供他們的行動應用程式。透過 Xamarin.Essentials 的套件幫忙,可以將這些功能整合到您的應用程式:加速計 應用程式資訊 電池 剪貼簿 指南針 資料傳輸 裝置顯示資訊 裝置資訊 電子郵件 檔案系統的協助程式 手電筒 地理編碼 地理位置 迴轉儀 磁力計 開啟瀏覽器 電話撥號員 喜好設定 螢幕鎖定 安全儲存體 SMS 文字轉換語音 追蹤版本 震動。
我們現在來體驗關於 Connectivity 連線 這項功能,因此,可以參考底下的專案範例,該專案範例

建立測試專案

  • 首先,我們先使用 Prism Template Pack 擴充功能所提供的專案樣板,建立起一個 Xamarin.Forms 專案,在這裡我們僅選擇 Android / iOS / UWP 類型的專案;接著,我們需要把 PropertyChanged.Fody NuGet 套件安裝到 .NET Standard 專案類別庫內,並且安裝 FodyWeavers.xml 檔案。
  • 緊接著,我們要開始安裝 Xamarin.Essentials NuGet 套件到所有的專案內,在這裡,請使用滑鼠右擊方案節點,選擇 管理方案的 NuGet 套件 選項
  • 請在 NuGet - 解決方案 的視窗中,輸入要搜尋的套件名稱 : Xamarin.Essentials
    並且,請勾選 包括搶鮮版 的文字檢查盒,並且安裝這個套件;我寫這篇文章的時候, Xamarin.Essentials 的最新版本為 0.9.1-preview 版本,因此,我們安裝這個版本。
    Xamarin Essentials NuGet Installation
  • 當 Xamarin.Essentials 套件安裝完成之後,您將會發現到在 Visual Studio 2017 的錯誤視窗中,出現了底下錯誤訊息
Warning
偵測到 Xamarin.Android.Support.Compat 的版本衝突。請直接從專案參考套件,以解決此問題。 
 XFEFConnectivity.Android -> XFEFConnectivity -> Xamarin.Essentials 0.9.1-preview -> Xamarin.Android.Support.CustomTabs 27.0.2 -> Xamarin.Android.Support.Compat (= 27.0.2) 
 XFEFConnectivity.Android -> Xamarin.Android.Support.Design 25.4.0.2 -> Xamarin.Android.Support.Compat (= 25.4.0.2).
偵測到 Xamarin.Android.Support.Compat 的版本衝突
  • 現在讓我們來修正這個錯誤,在這裡,我使用了 Visual Studio Code 開啟了 Android 專案下的 .csproj 檔案,請搜尋這個關鍵字 PackageReference Include="Xamarin.Essentials"
    此時,您將會看到底下的內容
XML
  <ItemGroup>
    <PackageReference Include="Xamarin.Essentials">
      <Version>0.7.0.17-preview</Version>
    </PackageReference>
    <PackageReference Include="Xamarin.Forms" Version="2.5.0.122203" />
    <PackageReference Include="Xamarin.Android.Support.Design" Version="25.4.0.2" />
    <PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="25.4.0.2" />
    <PackageReference Include="Xamarin.Android.Support.v4" Version="25.4.0.2" />
    <PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="25.4.0.2" />
    <PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="25.4.0.2" />
    <PackageReference Include="Prism.Unity.Forms" Version="7.0.0.396" />
  </ItemGroup>
  • 我們需要將 Xamarin.Android.Support.xxx 的套件,全部都變更成從 "25.4.0.2" 到 "27.0.2.1"
    修改完成結果如下所示,請進行存檔該 .csproj 檔案,並且回到 Visual Studio 2017 下,此時,將會重新載入這個 Android 專案
XML
  <ItemGroup>
    <PackageReference Include="Xamarin.Essentials">
      <Version>0.7.0.17-preview</Version>
    </PackageReference>
    <PackageReference Include="Xamarin.Forms" Version="2.5.0.122203" />
    <PackageReference Include="Xamarin.Android.Support.Design" Version="27.0.2.1" />
    <PackageReference Include="Xamarin.Android.Support.v7.AppCompat" Version="27.0.2.1" />
    <PackageReference Include="Xamarin.Android.Support.v4" Version="27.0.2.1" />
    <PackageReference Include="Xamarin.Android.Support.v7.CardView" Version="27.0.2.1" />
    <PackageReference Include="Xamarin.Android.Support.v7.MediaRouter" Version="27.0.2.1" />
    <PackageReference Include="Prism.Unity.Forms" Version="7.0.0.396" />
  </ItemGroup>
  • 現在,我們需要安裝 Xamarin.Android.Support.CustomTabs 這個套件到 Android 專案下,在這個時間點,我們將會安裝最新版本的 "27.0.2.1"
    Xamarin Essentials NuGet Installation
  • 如此,最初產生的錯誤訊息便會消失了。
  • 為了要使用連線這項功能,我們需要在 Android 原生專案內設定授權具有 AccessNetworkState 權限。
    關於 iOS & UWP 專案,則不需要做任何的修正
    • 在這裡,請打開 Android 原生專案的 MainActivity.cs 檔案
    • 請在 namespace 之前,加入底下程式碼,宣告具有這使用權限
C#
[assembly: UsesPermission(Android.Manifest.Permission.AccessNetworkState)]
  • 我們將開始要進行 View ViewModel Model 的設計了,首先,我們開啟 MainPage.xaml 這個檔案,底下是我們測試的頁面 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"
             x:Class="XFEFConnectivity.Views.MainPage"
             Title="Connectivity 連線">

    <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
        <Label Text="Welcome to Xamarin Forms and Prism!" />
        <Label
            Text="{Binding ConnectivityStatus}"/>
        <Label
            Text="{Binding Profiles}"/>
    </StackLayout>

</ContentPage>
  • 請打開 MainPageViewModel.cs 這個檔案
C#
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace XFEFConnectivity.ViewModels
{
    using System.ComponentModel;
    using Prism.Events;
    using Prism.Navigation;
    using Prism.Services;
    using Xamarin.Essentials;

    public class MainPageViewModel : INotifyPropertyChanged, INavigationAware
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public string ConnectivityStatus { get; set; }
         public string Profiles { get; set; }
       private readonly INavigationService _navigationService;

        public MainPageViewModel(INavigationService navigationService)
        {
            _navigationService = navigationService;

            Init();
        }

        private void Init()
        {
            var current = Connectivity.NetworkAccess;

            if (current == NetworkAccess.Internet)
            {
                ConnectivityStatus = "網際網路-本機和網際網路存取";
            }
            else if (current == NetworkAccess.ConstrainedInternet)
            {
                ConnectivityStatus = "ConstrainedInternet – 受限的網際網路存取。 指出網頁驗證入口網站的連線能力,其中提供入口網站的本機存取,但存取網際網路需要透過入口網站提供了特定的憑證。";
            }
            else if (current == NetworkAccess.Local)
            {
                ConnectivityStatus = "本機– 本機網路僅限存取。";
            }
            else if (current == NetworkAccess.None)
            {
                ConnectivityStatus = "無– 沒有連線可用。";
            }
            else if (current == NetworkAccess.Unknown)
            {
                ConnectivityStatus = "未知– 無法判定網際網路連線。";
            }

            var profiles = Connectivity.Profiles;
            Profiles = "裝置中正在使用的連線類型 : ";
            if (profiles.Contains(ConnectionProfile.WiFi))
            {
                Profiles += " WiFi ";
            }
            if (profiles.Contains(ConnectionProfile.Bluetooth))
            {
                Profiles += " Bluetooth ";
            }
            if (profiles.Contains(ConnectionProfile.Cellular))
            {
                Profiles += " Cellular ";
            }
            if (profiles.Contains(ConnectionProfile.Ethernet))
            {
                Profiles += " Ethernet ";
            }
            if (profiles.Contains(ConnectionProfile.Other))
            {
                Profiles += " Other ";
            }
            if (profiles.Contains(ConnectionProfile.WiMAX))
            {
                Profiles += " WiMAX ";
            }
        }

        public void OnNavigatedFrom(NavigationParameters parameters)
        {

        }

        public void OnNavigatingTo(NavigationParameters parameters)
        {

        }

        public void OnNavigatedTo(NavigationParameters parameters)
        {

        }

    }
}
  • Android 平台執行結果
    這是使用 WiFi 的方式,且有連上 Internet 的執行結果
    Xamarin Essentials
    這是沒有網路連線的執行結果。
    Xamarin Essentials
    這是使用 4G 網路連上 Internet 的執行結果
    Xamarin Essentials
  • UWP 平台執行結果
    這是使用 WiFi 的方式,且有連上 Internet 的執行結果
    Xamarin Essentials

關於 Xamarin 在台灣的學習技術資源

Xamarin 實驗室 粉絲團
歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
Xamarin.Forms @ Taiwan
歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
Xamarin 實驗室 部落格
Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
Xamarin.Forms 系列課程
Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程