星期五, 8月 28, 2020

[X.Form] MVVM - Picker

在 MVVM 內練習 Picker 的使用

ColorViewModel
using System.Drawing;

namespace MVVMEnumDemo
{
    public class ColorViewModel
    {
        public Color Color { get; set; }

        public string DisplayName { get; set; }
    }
}
EnumDemoViewModel
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using Xamarin.Forms.Internals;
using System.Linq;

namespace MVVMEnumDemo
{
    public class EnumDemoViewModel : INotifyPropertyChanged
    {

        public EnumDemoViewModel()
        {
            this.ColorsList = new List<ColorViewModel>()
            {
                new ColorViewModel(){ Color = Color.Gray , DisplayName = "灰色"} ,
                new ColorViewModel(){ Color = Color.Red , DisplayName = "紅色"} ,
                new ColorViewModel(){ Color = Color.Green , DisplayName = "綠色"} ,
                new ColorViewModel(){ Color = Color.Yellow , DisplayName = "黃色"}
            };

            // 初始化顏色
            SelectedColor = Color.Black;
        }

        public IEnumerable<ColorViewModel> ColorsList { get; private set; }

        /// <summary>
        /// 資料來源
        /// </summary>
        private Color _selectedcolor;
        public Color SelectedColor
        {
            get { return _selectedcolor; }
            set
            {
                _selectedcolor = value;
                OnPropertyChanged(nameof(SelectedColor));
            }
        }

        /// <summary>
        /// 負責 Picker Index 轉換
        /// </summary>
        public int SelectedIndex
        {
            get { return this.ColorsList.IndexOf(c => c.Color == this.SelectedColor); }
            set
            {
                this.SelectedColor = this.ColorsList.ElementAt(value).Color;
                OnPropertyChanged(nameof(SelectedIndex));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
Xaml View 配置
<?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:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:local="clr-namespace:MVVMEnumDemo"
             mc:Ignorable="d"
             x:Class="MVVMEnumDemo.MainPage">

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

    <StackLayout Margin="20,20,20,20">
        
        <Picker        
            FontSize="Large"
            ItemsSource="{Binding ColorsList}" 
            ItemDisplayBinding="{Binding DisplayName}" 
            SelectedIndex="{Binding SelectedIndex,Mode=TwoWay}"/>

        <BoxView Color="{Binding SelectedColor}"
             CornerRadius="10"
             WidthRequest="300"
             HeightRequest="300"
             VerticalOptions="Center"
             HorizontalOptions="Center" />

    </StackLayout>
</ContentPage>

啟動畫面

[X.Form] MVVM - Picker & Enum-1

選擇顏色

[X.Form] MVVM - Picker & Enum-2

原以為 SelectedIndex 會有對應的 Command 可以使用,會來才發現沒有,不是每個事件都會有對應的 Command


星期四, 8月 27, 2020

[X.Form] MVVM - 顏色變化

MVVM 練習,透過 Xaml 搭配 DataTrigger 來變化 View 顏色,也加深對 DataTrigger 的理解

ColorDemoViewModel
using System.ComponentModel;
using System.Windows.Input;
using Xamarin.Forms;

namespace MVVMColorDemo
{
    public class ColorDemoViewModel : INotifyPropertyChanged
    {
        private int _score;
        public int Score
        {
            get { return _score; }
            set
            {
                _score = value;
                OnPropertyChanged(nameof(Score));
            }
        }

        /// <summary>
        /// LabelView.DataTrigger Binding 該 Property 來進行顏色變化
        /// </summary>
        public bool IsScoreOver60
        {
            get { return Score >= 60; }
        }

        public string _result;
        public string Result
        {
            get { return _result; }
            set
            {
                _result = value;
                OnPropertyChanged(nameof(Result));
                // 在這通知 View IsScoreOver60 Property 有變化啦
                OnPropertyChanged(nameof(IsScoreOver60));
            }
        }

        public ICommand ShowScoreCommand
        {
            get
            {
                return new Command(() =>
                {
                    Result = $"使用者輸入分數為:{Score}";
                });
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}
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:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:local="clr-namespace:MVVMColorDemo"
             mc:Ignorable="d"
             x:Class="MVVMColorDemo.MainPage">

    <!--直接在 Xaml 內設定 BindingContext-->
    <ContentPage.BindingContext>
        <local:ColorDemoViewModel/>
    </ContentPage.BindingContext>

    <StackLayout>
        <Entry Placeholder="請輸入分數" FontSize="Large" Text="{Binding Score, Mode=TwoWay}"/>
        <Button Text="顯示輸入分數" Command="{Binding ShowScoreCommand}" FontSize="Large"/>
        <!--Label 顏色預設為 Red-->
        <Label Text="{Binding Result}" TextColor="Red" FontSize="Large">
            <!--本次重點 - DataTrigger 的使用-->
            <Label.Triggers>
                <DataTrigger 
                    TargetType="Label" 
                    Binding="{Binding IsScoreOver60}" 
                    Value="True">
                    <Setter Property="TextColor" Value="Green"/>
                </DataTrigger>
            </Label.Triggers>
        </Label>
    </StackLayout>

</ContentPage>
練習時 DataTrigger Binding 時打錯 Property,把 IsScoreOver60 打成 IsOver60,沒想到編譯階段都不會有錯誤發生,是執行階段變色效果一直出不來,回頭 debug 才發現打錯,Orz

初始畫面

[X.Form] MVVM - 顏色變化-1

測試結果

[X.Form] MVVM - 顏色變化-2

[X.Form] MVVM - 顏色變化-3

星期三, 8月 26, 2020

[X.Form] System.TypeLoadException

在 Xamarin.Form 內引用自己寫的 dll 時,在執行階段跳出下列訊息,Google 網路資訊,都是指向 Xamarin.Form 各專案套件版本不一致造成該錯誤。

[X.Form] System.TypeLoadException-1

Xamarin.Form 的 Android 專案上果然沒有安裝到 Net Standard Library,安裝上去後就可以正常引用

[X.Form] System.TypeLoadException-2

星期日, 8月 23, 2020

[X.Form] Android ClearText 設定

在 Android 模擬器內要本機 IIS 進行 Post 來 Insert 資料進 DB 時,跳出下圖的錯誤訊息

[X.Form] Android ClearText 設定-1

了解後發現原來從 Android 9 (API level 28) 開始,cleartext(non-HTTPS)設定預設是關閉的,也就是說一定要使用 https 連線,下面紀錄要如何開啟該設定

在 XamarinForm 的 Andorid 內加入 network_security_config.xml 檔案並在 Properties.AndroidManifest.xml 內引用

[X.Form] Android ClearText 設定-2

[X.Form] Android ClearText 設定-3

network_security_config.xml 內容,要開啟 10.0.2.2
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <domain-config cleartextTrafficPermitted="true">
    <!-- Android 模擬器找本機 IIS,要用 10.0.0.2 -->
    <domain includeSubdomains="true">10.0.2.2</domain>
    <domain includeSubdomains="true">xamarin.com</domain>
  </domain-config>
</network-security-config>
AndroidManifest.xml 內引用 NetworkSecurityConfig
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.ooxx">
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
  <application android:label="OOXX.Android"
               android:networkSecurityConfig="@xml/network_security_config"/>
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>
在 AndroidManifest 內引用設定時,發生點烏龍,因為沒有注意到已經有 application 存在,又建了一個 application,build 和 deploy 模擬器也都沒有任何錯誤發生,實際執行時才又跳出相同錯誤
<application android:label="OOXX.Android"/>
<application android:networkSecurityConfig="@xml/network_security_config"/>
另外一種方式是在 Properties.AssemblyInfo.cs 內加入 [assembly: Application(UsesCleartextTraffic = true)] 設定,如下圖

[X.Form] Android ClearText 設定-4

星期五, 8月 21, 2020

[IIS] 虛擬帳戶 - ApplicationPoolIdentity

把寫好的 WebAPI 部屬到本機 IIS 上,Postman 一打就發現登錄失敗訊息,了解 IIS 應用程式集區識別預設是 ApplicationPoolIdentity,可以在 SQL Server 上把該虛擬帳號加入使用者,或是把應用程式集區識別修改為具有存取 SQL Server 的自訂使用者,就可以順利存取 SQL Server

IIS 虛擬帳戶 - ApplicationPoolIdentity-1

以下筆記 ApplicationPoolIdentity 相關授權,分別為資料夾權限和存取 SQL Server

資料夾權限

資料夾權限內加入 [IIS AppPool\應用程式資料集區名稱] 並設定權限
IIS 虛擬帳戶 - ApplicationPoolIdentity-2

SQL Server 存取

建立 SQL Server 使用者,名稱為  [IIS AppPool\應用程式資料集區名稱] 並授予 db_datareader 和 db_datawriter 資料庫角色

IIS 虛擬帳戶 - ApplicationPoolIdentity-3


IIS 虛擬帳戶 - ApplicationPoolIdentity-4

在存取 SQL Server 上,當然也可以把 ApplicationPoolIdentity  替換掉,換成具有存取 SQL Server 的自訂帳戶

IIS 虛擬帳戶 - ApplicationPoolIdentity-5