[.NET MAUI] UIに独自の状態変化
Entry
から独自のBHEDIT
に置き換える。
...
<Border BindingContext="{x:Reference this}"
x:Name="ActivityBorder"
Stroke="Transparent"
StrokeThickness="4"
StrokeShape="RoundRectangle 8">
<Label Text="{Binding InputText}"
HeightRequest="40"
FontSize="18" TextColor="#000" BackgroundColor="#FFF"
Padding="4" />
</Border>
<ContentView.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="2" Tapped="DoubleTapped" />
</ContentView.GestureRecognizers>
</ContentView>
UIとして表示する文字列を独自のInputText
プロパティとして実装する。
また、UIをダブルタップして文字列を消去する処理もこちら側に実装しておく。
...
public partial class BHEDIT : ContentView
{
public static readonly BindableProperty InputTextProperty = BindableProperty.Create(nameof(InputText), typeof(string), typeof(BHEDIT), string.Empty);
public string InputText
{
get => (string)GetValue(InputTextProperty);
set => SetValue(InputTextProperty, value);
}
public BHEDIT()
{
InitializeComponent();
}
private void DoubleTapped(object sender, EventArgs e)
{
InputText = "";
}
}
独自コントロールを使用するMainPageはこのようになる。
<HorizontalStackLayout Margin="6">
<controls:BHPANEL LabelText="車種" VerticalOptions="Center" />
+ <controls:BHEDIT x:Name="CarTypeInput" VerticalOptions="Center"
+ WidthRequest="180">
+ <controls:BHEDIT.GestureRecognizers>
+ <TapGestureRecognizer NumberOfTapsRequired="1" Tapped="SingleTapped" />
+ </controls:BHEDIT.GestureRecognizers>
+ </controls:BHEDIT>
</HorizontalStackLayout>
また、この後BHEDIT
から擬似的に「フォーカスが外れた」ことを検知するためにContentPage > AbsoluteLayout > Grid
とかなり上の階層にGestureRecognizer
を追加しておく。
+ <Grid.GestureRecognizers>
+ <TapGestureRecognizer NumberOfTapsRequired="1" Tapped="SingleTapped" />
+ </Grid.GestureRecognizers>
独自の状態変化
やりたいことは…
- BHEDITクラスのUIをタップすると、活性状態になって枠が水色になる。
- BHEDITクラスの外側をタップすると、非活性状態になって枠が透明になる。
BHEDIT.xaml
に戻って、ルートのContentView
直下にVisualStateManager
を追加する。
活性/非活性状態をActivityStates
と名付けて、値がActive
の場合はBorder
を水色に変更、Inactive
の場合は透明に変更する。
+ <VisualStateManager.VisualStateGroups>
+ <VisualStateGroup Name="ActivityStates">
+ <VisualState Name="Active">
+ <VisualState.Setters>
+ <Setter TargetName="ActivityBorder" Property="Border.Stroke" Value="LightSkyBlue" />
+ </VisualState.Setters>
+ </VisualState>
+ <VisualState Name="Inactive">
+ <VisualState.Setters>
+ <Setter TargetName="ActivityBorder" Property="Border.Stroke" Value="White" />
+ </VisualState.Setters>
+ </VisualState>
+ </VisualStateGroup>
+ </VisualStateManager.VisualStateGroups>
コードビハインド側で、SetActive
という名前の状態変更用メソッドを実装する。引数がtrue
であればVisualStateManager.GoToState
でthisすなわちルートのContentView
の状態をActive
にする。
+ public void SetActive(bool isActive)
+ {
+ string state = isActive ? "Active" : "Inactive";
+ VisualStateManager.GoToState(this, state);
+ }
MainPage側はこのようになる。
async void SingleTapped(System.Object sender, Microsoft.Maui.Controls.TappedEventArgs e)
{
Console.WriteLine("SingleTapped: " + sender.ToString());
+ // 最初に全部のBHEDITの活性状態をリセットする
+ CarTypeInput.SetActive(false);
+ RegionFromInput.SetActive(false);
+ RegionToInput.SetActive(false);
+ if (sender == CarTypeInput)
+ {
+ CarTypeInput.SetActive(true);
+ }
+ else if (sender == RegionFromInput || sender == RegionToInput)
+ {
+ ((Controls.BHEDIT)sender).SetActive(true);
+ }
}
これでようやく、BHEDITクラスのUIの内側/外側をタップして枠の色を変えることに成功した!
ただし問題があり、Androidの場合はGridに対するTapGestureRecognizerが反応せず(厳密にはGridの上に被さっているHorizontalStackLayoutなどにTapGesutreRecognizerを設定する必要があり)、ちょっとめんどくさいので一旦このまま先へ進むことにした。
どうやらAndroidとiOSなどでイベントのバブリング的な動作が異なるっぽい。