Share via


視覺狀態

.NET 多平臺應用程式 UI (.NET MAUI) Visual State Manager 提供結構化的方式,從程式代碼對使用者介面進行視覺變更。 在大部分情況下,應用程式的使用者介面是在 XAML 中定義,而這個 XAML 可以包含標記,描述 Visual State Manager 如何影響使用者介面的視覺效果。

Visual State Manager 引進視覺狀態的概念。 例如的 .NET MAUI 檢視 Button 可能會根據其基礎狀態而有不同的視覺外觀,無論是停用或按下,還是具有輸入焦點。 這些是按鈕的狀態。 視覺狀態會收集於視覺狀態群組。 視覺狀態群組中的所有視覺狀態都是互斥的。 視覺狀態和視覺狀態群組都是透過簡單的文字字串來識別。

.NET MAUI Visual State Manager 會以下列視覺狀態定義名為 CommonStates 的視覺狀態群組:

  • 一般
  • 停用
  • 焦點
  • Selected
  • PointerOver

衍生NormalVisualElement的所有類別都支援、DisabledFocusedPointerOver 視覺狀態,這是 和Page的基類View。 此外,您也可以定義自己的視覺狀態群組和視覺狀態。

使用 Visual State Manager 來定義外觀,而不是直接從程式代碼後置存取視覺元素的優點,就是您可以控制視覺元素在 XAML 中完全回應不同狀態的方式,這會將所有 UI 設計保留在一個位置。

注意

觸發程式也可以根據檢視屬性的變更或引發事件,對使用者介面中的視覺效果進行變更。 不過,使用觸發程式來處理這些變更的各種組合可能會造成混淆。 使用 Visual State Manager 時,視覺狀態群組內的視覺狀態一律互斥。 在任何時候,每個群組中只有一個狀態是目前的狀態。

常見的視覺狀態

Visual State Manager 可讓您在 XAML 檔案中包含標記,如果檢視正常、停用、已選取輸入焦點、已選取滑鼠游標停留在檢視上方但未按下,則可以變更檢視的視覺外觀。 這些稱為 常見狀態

例如,假設您在頁面上有檢視 Entry ,而且您想要以下列方式變更 的 Entry 視覺效果外觀:

  • Entry停用 時Entry,應該會有粉紅色的背景。
  • Entry通常應該有石灰背景。
  • Entry 其具有輸入焦點時,應該會展開至其正常高度的兩倍。
  • Entry當滑鼠游標停留在它上方但未按下時,應該會有淺藍色背景。

您可以將 Visual State Manager 標記附加至個別檢視,或者如果套用至多個檢視,則可以在樣式中定義它。

在檢視上定義視覺狀態

類別 VisualStateManagerVisualStateGroups 定義附加屬性,用來將視覺狀態附加至檢視。 屬性 VisualStateGroups 的類型為 VisualStateGroupList,這是物件的集合 VisualStateGroup 。 因此,附加屬性的 VisualStateManager.VisualStateGroups 子系是 VisualStateGroup 物件。 這個物件會 x:Name 定義屬性,指出群組的名稱。 或者,類別 VisualStateGroupName 定義您可以改用的屬性。 如需附加屬性的詳細資訊,請參閱 附加屬性

類別 VisualStateGroup 會定義名為 States的屬性,這是物件的集合 VisualStateStates 是類別的內容屬性 VisualStateGroups ,因此您可以將 物件包含 VisualState 為 的 VisualStateGroup子系。 每個 VisualState 物件都應該使用 x:NameName來識別。

類別 VisualState 會定義名為 Setters的屬性,這是物件的集合 Setter 。 這些是您在物件中使用的Style相同Setter物件。 Setters 不是的內容屬性 VisualState,因此必須包含 屬性的屬性項目標記 SettersSetter 對象應該插入為 的 Setters子系。 當該狀態為目前時,每個 Setter 物件都會指出屬性的值。 對象所參考 Setter 的任何屬性都必須由可繫結屬性支援。

重要

為了讓視覺狀態 Setter 物件正常運作, VisualStateGroup 必須包含 VisualState 狀態的 Normal 物件。 如果這個視覺狀態沒有任何 Setter 物件,它應該包含為空的視覺狀態 (<VisualState Name="Normal" />)。

下列範例顯示 上 Entry定義的視覺狀態:

<Entry FontSize="18">
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup Name="CommonStates">
            <VisualState Name="Normal">
                <VisualState.Setters>
                    <Setter Property="BackgroundColor" Value="Lime" />
                </VisualState.Setters>
            </VisualState>

            <VisualState Name="Focused">
                <VisualState.Setters>
                    <Setter Property="FontSize" Value="36" />
                </VisualState.Setters>
            </VisualState>

            <VisualState Name="Disabled">
                <VisualState.Setters>
                    <Setter Property="BackgroundColor" Value="Pink" />
                </VisualState.Setters>
            </VisualState>

            <VisualState Name="PointerOver">
                <VisualState.Setters>
                    <Setter Property="BackgroundColor" Value="LightBlue" />
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Entry>

下列螢幕快照顯示 Entry 其四個已定義的視覺狀態:

專案上三個已定義視覺狀態的螢幕快照。

Entry當 處於 狀態時Normal,其背景會是石灰。 當取得輸入焦點時 Entry ,其字型大小會加倍。 Entry當 變成停用時,其背景會變成粉紅色。 在 Entry 取得輸入焦點時,不會保留其石灰背景。 當滑鼠指標停留在 Entry上方,但未按下時, Entry 背景會變成淺藍色。 當 Visual State Manager 在視覺狀態之間切換時,先前狀態所設定的屬性會取消設定。 因此,視覺狀態互斥。

如果您想要 Entry 在狀態中 Focused 具有石灰背景,請將另一個 Setter 新增至該視覺狀態:

<VisualState Name="Focused">
    <VisualState.Setters>
        <Setter Property="FontSize" Value="36" />
        <Setter Property="BackgroundColor" Value="Lime" />
    </VisualState.Setters>
</VisualState>

在樣式中定義視覺狀態

通常必須在兩個或多個檢視中共用相同的視覺狀態。 在此案例中,視覺狀態可以在 中 Style定義。 您可以藉由為 屬性新增 Setter 對象 VisualStateManager.VisualStateGroups 來達成此目的。 物件的 content 屬性是其 Value 屬性Setter,因此可以指定為 物件的子系Setter。 屬性 VisualStateGroups 的類型為 VisualStateGroupList,因此物件的子系 SetterVisualStateGroupListVisualStateGroup ,可以加入包含 VisualState 物件的 。

下列範例顯示 的隱含樣式 Entry ,其定義通用視覺狀態:

<Style TargetType="Entry">
    <Setter Property="FontSize" Value="18" />
    <Setter Property="VisualStateManager.VisualStateGroups">
        <VisualStateGroupList>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="Lime" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState Name="Focused">
                    <VisualState.Setters>
                        <Setter Property="FontSize" Value="36" />
                        <Setter Property="BackgroundColor" Value="Lime" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState Name="Disabled">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="Pink" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState Name="PointerOver">
                    <VisualState.Setters>
                        <Setter Property="BackgroundColor" Value="LightBlue" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateGroupList>
    </Setter>
</Style>

當這個樣式包含在頁面層級資源字典中時, Style 物件將會套用至頁面上的所有 Entry 物件。 因此,頁面上的所有 Entry 物件都會以相同的方式回應其視覺狀態。

.NET MAUI 中的視覺狀態

下表列出 .NET MAUI 中定義的視覺狀態:

類別 狀態 相關資訊
Button Pressed 按鈕視覺狀態
CarouselView DefaultItem、 、 CurrentItemPreviousItemNextItem CarouselView 視覺效果狀態
CheckBox IsChecked CheckBox 視覺效果狀態
CollectionView Selected CollectionView 視覺狀態
ImageButton Pressed ImageButton 視覺狀態
RadioButton Checked, Unchecked RadioButton 視覺狀態
Switch On, Off 切換視覺狀態
VisualElement Normal、 、 DisabledFocusedPointerOver 常見狀態

在多個元素上設定狀態

在先前的範例中,視覺狀態已附加至單一元素並操作。 不過,您也可以建立附加至單一元素的視覺狀態,但在相同範圍內設定其他元素的屬性。 這可避免在狀態運作的每個元素上重複視覺狀態。

Setter 類型具有 TargetName 類型的 string屬性,代表視覺狀態將操作的目標物件 SetterTargetName定義 屬性時,會將 SetterTargetName定義的 物件的 設定PropertyValue

<Setter TargetName="label"
        Property="Label.TextColor"
        Value="Red" />

在這裡範例中, Label 具名 label 的 屬性 TextColor 會設定為 Red。 設定 屬性時, TargetName 您必須在 中 Property指定 屬性的完整路徑。 因此,若要在 上LabelProperty設定 TextColor 屬性,會指定為 Label.TextColor

注意

對象所參考 Setter 的任何屬性都必須由可繫結屬性支援。

下列範例示範如何從單一視覺狀態群組設定多個物件的狀態:

<StackLayout>
    <Label Text="What is the capital of France?" />
    <Entry x:Name="entry"
           Placeholder="Enter answer" />
    <Button Text="Reveal answer">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup Name="CommonStates">
                <VisualState Name="Normal" />
                <VisualState Name="Pressed">
                    <VisualState.Setters>
                        <Setter Property="Scale"
                                Value="0.8" />
                        <Setter TargetName="entry"
                                Property="Entry.Text"
                                Value="Paris" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
    </Button>
</StackLayout>

在此範例中Normal,當 未按下 時Button,狀態為使用中,而且可以輸入回應。Entry Pressed按下 時Button狀態會變成使用中狀態,並指定其Scale屬性將從預設值 1 變更為 0.8。 此外, Entry 該名 entry 將將其 Text 屬性設定為巴黎。 因此,結果是按下 Button 時會重新調整為稍微小一點,而 Entry 顯示巴黎:

按鈕已按下狀態的螢幕快照。

然後,發行 時 Button ,它會重新調整為預設值 1,並 Entry 顯示任何先前輸入的文字。

重要

指定屬性的 元素TargetName不支援Setter屬性路徑。

定義自定義視覺狀態

您可以藉由定義自定義視覺狀態來實作,因為您可以定義通用狀態的視覺狀態,但使用您選擇的名稱,然後呼叫 VisualStateManager.GoToState 方法來啟動狀態。

下列範例示範如何使用 Visual State Manager 進行輸入驗證:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="VsmDemos.VsmValidationPage"
             Title="VSM Validation">
    <StackLayout x:Name="stackLayout"
                 Padding="10, 10">
            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup Name="ValidityStates">
                    <VisualState Name="Valid">
                        <VisualState.Setters>
                            <Setter TargetName="helpLabel"
                                    Property="Label.TextColor"
                                    Value="Transparent" />
                            <Setter TargetName="entry"
                                    Property="Entry.BackgroundColor"
                                    Value="Lime" />
                        </VisualState.Setters>
                    </VisualState>
                    <VisualState Name="Invalid">
                        <VisualState.Setters>
                            <Setter TargetName="entry"
                                    Property="Entry.BackgroundColor"
                                    Value="Pink" />
                            <Setter TargetName="submitButton"
                                    Property="Button.IsEnabled"
                                    Value="False" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        <Label Text="Enter a U.S. phone number:"
               FontSize="18" />
        <Entry x:Name="entry"
               Placeholder="555-555-5555"
               FontSize="18"
               Margin="30, 0, 0, 0"
               TextChanged="OnTextChanged" />
        <Label x:Name="helpLabel"
               Text="Phone number must be of the form 555-555-5555, and not begin with a 0 or 1" />
        <Button x:Name="submitButton"
                Text="Submit"
                FontSize="18"
                Margin="0, 20"
                VerticalOptions="Center"
                HorizontalOptions="Center" />
    </StackLayout>
</ContentPage>

在此範例中,視覺狀態會附加至 StackLayout,而且有兩個名為 和Invalid的互斥狀態ValidEntry如果 不包含有效的電話號碼,則目前的狀態為 Invalid,因此 Entry 具有粉紅色背景,則會顯示第二Label個,且 Button 已停用。 輸入有效的電話號碼時,目前的狀態會 Valid變成 。 會 Entry 取得石灰背景,第二個 Label 消失,而 Button 現在已啟用:

可視化狀態驗證範例的螢幕快照。

程序代碼後置檔案負責處理 TextChanged 中的 Entry事件。 處理程式會使用正則表示式來判斷輸入字串是否有效。 程式GoToState代碼後置檔案中的 方法會呼叫 物件上的StackLayout靜態VisualStateManager.GoToState方法:

public partial class VsmValidationPage : ContentPage
{
    public VsmValidationPage()
    {
        InitializeComponent();

        GoToState(false);
    }

    void OnTextChanged(object sender, TextChangedEventArgs args)
    {
        bool isValid = Regex.IsMatch(args.NewTextValue, @"^[2-9]\d{2}-\d{3}-\d{4}$");
        GoToState(isValid);
    }

    void GoToState(bool isValid)
    {
        string visualState = isValid ? "Valid" : "Invalid";
        VisualStateManager.GoToState(stackLayout, visualState);
    }
}

在此範例中,會 GoToState 從建構函式呼叫 方法,以初始化狀態。 一律應該有目前的狀態。 程式代碼後置檔案接著會在定義視覺狀態的 物件上,使用狀態名稱呼叫 VisualStateManager.GoToState

視覺狀態觸發程式

視覺狀態支持狀態觸發程式,這是一組特殊的觸發程式,可定義應套用 的條件 VisualState

狀態觸發程序會新增至 VisualStateStateTriggers 集合。 此集合可以包含單一狀態觸發程序或多個狀態觸發程序。 當集合中的任何狀態觸發程序為作用中時,將會套用 VisualState

使用狀態觸發程式來控制視覺狀態時,.NET MAUI 會使用下列優先順序規則來判斷哪一個觸發程式 (以及對應的 VisualState) 為作用中:

  1. 衍生自 StateTriggerBase 的任何觸發程序。
  2. 因為符合 MinWindowWidth 條件,所以已啟用 AdaptiveTrigger
  3. 因為符合 MinWindowHeight 條件,所以已啟用 AdaptiveTrigger

如果多個觸發程序同時處於作用中狀態 (例如,兩個自訂觸發程序),則會優先使用標記中宣告的第一個觸發程序。

如需狀態觸發程式的詳細資訊,請參閱 狀態觸發程式