第十五章:互動式介面(三)

wangccsy發表於2018-09-06

常見的陷阱
假設您希望Slider值的範圍是1到100.您可以設定最小值和最大值,如下所示:

<Slider ValueChanged="OnSliderValueChanged"
        Minimum="1"
        Maximum="100"
        VerticalOptions="CenterAndExpand" />

但是,當您執行該程式的新版本時,會引發ArgumentException,並帶有文字說明“Value是Minimum的無效值。”這是什麼意思?
當XAML解析器遇到Slider標記時,將例項化Slider,然後按照它們在Slider標記中出現的順序設定屬性和事件。但是,當Minimum屬性設定為1時,最大值現在等於最小值。那不可能。 Maximum屬性必須大於Minimum。 Slider通過引發異常來表明這個問題。
在Slider類的內部,在回撥方法集中將Minimum和Maximum值與用於建立Minimum和Maximum可繫結屬性的BindableProperty.Create方法呼叫的validateValue引數進行比較。如果Minimum小於Maximum,則validateValue回撥返回true,表示值有效。此回撥觸發的返回值為false表示異常。這是可繫結屬性實現有效性檢查的標準方法。
這不是XAML特有的問題。如果您在程式碼中按此順序例項化和初始化Slider屬性,也會發生這種情況。解決方案是顛倒設定最小值和最大值的順序。首先將Maximum屬性設定為100.這是合法的,因為現在範圍介於0和100之間。然後將Minimum屬性設定為1:

<Slider ValueChanged="OnSliderValueChanged"
        Maximum="100"
        Minimum="1"
        VerticalOptions="CenterAndExpand" />

但是,這會導致另一個執行時錯誤。 現在,它是ValueChanged處理程式中的NullReferenceException。 這是為什麼?
Slider的Value屬性必須在Minimum和Maximum值的範圍內,因此當Minimum屬性設定為1時,Slider會自動將其Value屬性調整為1。
在內部,Value在一個回撥方法中調整,該方法設定為BindableProperty.Create方法的coerceValue引數,該方法呼叫Minimum,Maximum和Value屬性。 回撥方法返回經受此強制後設定的屬性的調整值。 在此示例中,當Minimum設定為1時,coerceValue方法將滑塊的Value屬性設定為1,而coerceValue回撥則返回新值Minimum,其值保持為1。
但是,由於強制,Value屬性已更改,這會導致ValueChanged事件觸發。 程式碼隱藏檔案中的ValueChanged處理程式嘗試設定Label的Text屬性,但XAML解析器尚未例項化Label元素。 標籤欄位為空。
這個問題有幾個解決方案。 最安全和最通用的解決方案是在事件處理程式中檢查標籤許可權的空值:

void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
{
    if (label != null)
    {
        label.Text = String.Format("Slider = {0}", args.NewValue);
    }
}

但是,您還可以通過將TagC中的ValueChanged事件的賦值移動到已設定Maximum和Minimum屬性之後來解決此問題:

<Slider Maximum="100"
        Minimum="1"
        ValueChanged="OnSliderValueChanged"
        VerticalOptions="CenterAndExpand" />

在設定了Minimum屬性後,Value屬性仍然被強制為1,但尚未分配ValueChanged事件處理程式,因此不會觸發任何事件。
假設Slider的預設範圍是0到1.您可能希望Label在程式首次啟動時顯示Slider的初始值。 您可以在XAML檔案中將Label的Text屬性初始化為“Slider = 0”,但如果您想將文字更改為稍微不同的文字,則需要在兩個位置更改它。
您可以嘗試在XAML檔案中為Slider指定一個滑塊名稱,然後將一些程式碼新增到建構函式中:

public SliderDemoPage()
{
    InitializeComponent();
    slider.Value = 0;
}

當InitializeComponent返回時,XAML檔案中的所有元素都已建立並初始化,因此如果此程式碼導致Slider觸發ValueChanged事件,那應該不是問題。
但它不會起作用。 Slider的值已經為0,因此再次將其設定為0不會執行任何操作。 你可以試試這個:

public SliderDemoPage()
{
    InitializeComponent();
    slider.Value = 1;
    slider.Value = 0;
}

那可行。 但是您可能希望在程式碼中新增註釋,以便其他程式設計師以後不會刪除將Value設定為1的語句,因為它似乎是不必要的。
或者您可以通過直接呼叫處理程式來模擬事件。 ValueChangedEventArgs建構函式的兩個引數是舊值和新值(按此順序),但OnSliderValueChanged處理程式僅使用NewValue屬性,因此其他引數是什麼或它們是否相等無關緊要:

public partial class SliderDemoPage : ContentPage
{
    public SliderDemoPage()
    {
        InitializeComponent();
        OnSliderValueChanged(null, new ValueChangedEventArgs(0, 0));
    }
    void OnSliderValueChanged(object sender, ValueChangedEventArgs args)
    {
        label.Text = String.Format("Slider = {0}", args.NewValue);
    }
}

這也有效。 但是請記住將引數設定為對OnSliderValueChanged的呼叫,以便它們與處理程式所期望的一致。 如果使用將sender引數強制轉換為Slider物件的程式碼替換處理程式主體,則需要在OnSliderValueChanged呼叫中使用有效的第一個引數。
當您使用資料繫結將Label與Slider連線時,涉及事件處理程式的問題就會消失,您將在下一章中瞭解到這些問題。 您仍然需要以正確的順序設定Slider的屬性,但是您將不會遇到事件處理程式的任何問題,因為事件處理程式將消失。


相關文章