search bar in shell behavior

Eduardo Gomez Romero 1,015 Reputation points
2024-11-22T10:58:06.6833333+00:00

So, I need to modify, my behavior on desktop

I have the shell, with the Seach bar

<Window
    x:Class="METROWIND.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">

    <Window.TitleBar>
        <TitleBar
            Title="METROWIND"
            BackgroundColor="{x:AppThemeBinding Dark={x:StaticResource Black}, Light={x:StaticResource Primary}}"
            HeightRequest="60">
            <TitleBar.Content>
                <Border>
                    <SearchBar/>
                </Border>
            </TitleBar.Content>
            <TitleBar.TrailingContent>
                <ImageButton
                    Background="Transparent"
                    BorderWidth="0"
                    HeightRequest="36"
                    WidthRequest="36">
                    <ImageButton.Source>
                        <FontImageSource
                            FontFamily="SegoeMDL2"
                            Glyph="&#xE713;"
                            Size="16" />
                    </ImageButton.Source>
                </ImageButton>
            </TitleBar.TrailingContent>
        </TitleBar>
    </Window.TitleBar>

    public partial class App : Application
    {
        AppShell shell;

        public App(AppShell appShell)
        {

            SyncfusionLicenseProvider.RegisterLicense(AppConstants.SYNCFUSION_KEY);

            InitializeComponent();

            shell = appShell;
        }

        protected override Window CreateWindow(IActivationState? activationState)
        {
            Window window = new MainWindow(shell);

            return window;

        }


everything works fantastic, but what I want to do for desktop, is to make my collection of turbines globally available for you to search in the shell to open a new window WITH THE DETAIL

Also, I have a cahrgeStationPage, that display a combo box with the turbines to search, and mke the map go to that pin

    public partial class ChargingStationsMapPageViewModel(
        HttpService service, DeviceLanguageService deviceLanguage, TurbinesService turbinesService) :
        HomePageViewModel(service, deviceLanguage, turbinesService)
    {

        private Map? MapView;

        [ObservableProperty]
        bool isOptionsOpen;

        [ObservableProperty]
        bool isExpanded;

        [RelayCommand]
        private void Appearing(Map map)
        {

            MapView = map;
        }

        [RelayCommand]
        void ItemSelected(Turbine Turbine)
        {

            var mapSpan = MapSpan.FromCenterAndRadius(Turbine.Location,
                Distance.FromKilometers(0.4));

            MapView!.MoveToRegion(mapSpan);
            IsExpanded = false;
        }

        [RelayCommand]
        void OpenMenu()
        {

            IsOptionsOpen = true;
        }

        [RelayCommand]
        void ChangeMapType(int mapType)
        {
            MapView!.MapType = mapType switch
            {
                0 => MapType.Street,
                1 => MapType.Satellite,
                2 => MapType.Hybrid, // Example: Handle mapType 2
                _ => throw new ArgumentOutOfRangeException(nameof(mapType), mapType, "Invalid map type"),
            };
            IsOptionsOpen = false;
        }
    }
}

<ContentPage
    x:Class="METROWIND.Views.ChargingStationsMapPage"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:constant="clr-namespace:METROWIND.Constants"
    xmlns:controls="clr-namespace:METROWIND.Controls"
    xmlns:expander="clr-namespace:Syncfusion.Maui.Expander;assembly=Syncfusion.Maui.Expander"
    xmlns:maps="clr-namespace:Microsoft.Maui.Controls.Maps;assembly=Microsoft.Maui.Controls.Maps"
    xmlns:mct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
    xmlns:model="clr-namespace:METROWIND.Models"
    xmlns:popup="clr-namespace:Syncfusion.Maui.Popup;assembly=Syncfusion.Maui.Popup"
    xmlns:rex="clr-namespace:METROWIND.Resources"
    xmlns:vm="clr-namespace:METROWIND.ViewModel"
    x:Name="stationsMapPage"
    x:DataType="vm:ChargingStationsMapPageViewModel">

    <ContentPage.Behaviors>
        <mct:EventToCommandBehavior
            Command="{x:Binding AppearingCommand}"
            CommandParameter="{x:Reference ChargingStationMap}"
            EventName="Appearing" />

    </ContentPage.Behaviors>

    <Grid>
        <Border Style="{x:StaticResource ChargeStationBorderStyle}">
            <expander:SfExpander
                AnimationDuration="200"
                IsExpanded="{x:Binding IsExpanded}">
                <expander:SfExpander.Header>
                    <Grid RowDefinitions="48">
                        <Label Style="{x:StaticResource ChooseStationLabelStyle}" />
                    </Grid>
                </expander:SfExpander.Header>
                <expander:SfExpander.Content>
                    <Grid
                        Padding="10,0,0,0"
                        RowDefinitions="Auto">
                        <CollectionView ItemsSource="{x:Binding Turbines}">
                            <CollectionView.ItemTemplate>
                                <DataTemplate x:DataType="model:TurbinePin">
                                    <Border Stroke="Transparent">
                                        <HorizontalStackLayout Spacing="10">
                                            <Label Style="{x:StaticResource ElectricBoltLabelStyle}" />
                                            <Label
                                                Style="{x:StaticResource AddressLabelStyle}"
                                                Text="{x:Binding Turbine.Name}" />
                                        </HorizontalStackLayout>
                                        <Border.Behaviors>
                                            <mct:TouchBehavior
                                                DefaultBackgroundColor="White"
                                                PressedBackgroundColor="CornflowerBlue" />
                                        </Border.Behaviors>
                                        <Border.GestureRecognizers>
                                            <TapGestureRecognizer
                                                Command="{x:Binding Source={x:Reference stationsMapPage},
                                                                    Path=BindingContext.ItemSelectedCommand}"
                                                CommandParameter="{x:Binding Turbine}" />
                                        </Border.GestureRecognizers>

                                    </Border>
                                </DataTemplate>
                            </CollectionView.ItemTemplate>
                        </CollectionView>
                    </Grid>
                </expander:SfExpander.Content>
            </expander:SfExpander>
        </Border>

        <maps:Map
            x:Name="ChargingStationMap"
            ItemsSource="{x:Binding Turbines}"
            VerticalOptions="FillAndExpand">
            <maps:Map.ItemTemplate>
                <DataTemplate x:DataType="model:TurbinePin">
                    <controls:CustomMapPin
                        Address="{x:Binding Turbine.Address}"
                        Images="{x:Binding Turbine.ImagesURLs}"
                        Label="{x:Binding Turbine.Name}"
                        Location="{x:Binding Turbine.Location}"
                        MarkerClickedCommand="{x:Binding PinClickedCommand}"
                        MarkerClickedCommandParameter="{x:Binding Turbine}" />
                </DataTemplate>
            </maps:Map.ItemTemplate>
        </maps:Map>

        <Button
            x:Name="mapLayers"
            Command="{x:Binding OpenMenuCommand}"
            Style="{x:StaticResource MapButtonStyle}">
            <Button.ImageSource>
                <FontImageSource
                    FontFamily="ma"
                    Glyph="{x:Static constant:MaterialFonts.Layers}" />
            </Button.ImageSource>
        </Button>

        <popup:SfPopup
            IsOpen="{x:Binding IsOptionsOpen}"
            RelativeView="{x:Binding Source={x:Reference mapLayers}}"
            Style="{x:StaticResource ChargeStationPopupStyle}">
            <popup:SfPopup.PopupStyle>
                <popup:PopupStyle CornerRadius="8" />
            </popup:SfPopup.PopupStyle>
            <popup:SfPopup.ContentTemplate>
                <DataTemplate>
                    <VerticalStackLayout Spacing="10">
                        <controls:CustomImageButton
                            Caption="{x:Static rex:AppResource.Default}"
                            ClickCommand="{x:Binding ChangeMapTypeCommand}"
                            ImageName="default_layer.png"
                            Parameters="0" />
                        <controls:CustomImageButton
                            Caption="{x:Static rex:AppResource.Satelite}"
                            ClickCommand="{x:Binding ChangeMapTypeCommand}"
                            ImageName="satelite_layer.png"
                            Parameters="2" />
                    </VerticalStackLayout>
                </DataTemplate>
            </popup:SfPopup.ContentTemplate>
        </popup:SfPopup>
    </Grid>
</ContentPage>


On this particular page, the search box, should take care of this

How is working right now

I have a homePage

that grabs all the turbines from the service and put it in a collection view

        protected readonly TurbinesService _turbinesService;
        public ObservableCollection<TurbinePin> Turbines => _turbinesService.TurbinePins;
        public HomePageViewModel(HttpService service, DeviceLanguageService deviceLanguage, TurbinesService turbinesService)
        {
            deviceLanguageService = deviceLanguage;
            httpService = service;
            LoadNews();
            _turbinesService = turbinesService;
            OnPinMarkerClickedCommand = new Command<object>(OnPinMarkerClicked);
            _turbinesService.GetTurbinePinsForUI(OnPinMarkerClickedCommand);
        }
        public ObservableCollection<Article>? NewsList { get; set; } =
            [];
        async void LoadNews()
        {
            var language = deviceLanguageService.GetDeviceCultureInfo();
            var newsUrl = AppConstants.GetNewsUrl(language.TwoLetterISOLanguageName);
            NewsList!.Clear();
            var newsObj = await httpService.GetAsync<News>(newsUrl);
            foreach (var item in newsObj!.Articles!)
            {
                NewsList.Add(item);
            }
        }
        [RelayCommand]
        protected void ShowNewsDetail(Article article)
        {
            if (article != null)
            {
                Shell.Current.GoToAsync($"{nameof(ArticleDetailsPage)}",
                    true,
                    new Dictionary<string, object> {
                        { "articleObj", article }
                    });
            }
        }
        void OnPinMarkerClicked(object turbine)
        {
            if (turbine != null)
            {
                Shell.Current.GoToAsync($"{nameof(TurbineDetailPage)}",
                     true,
                     new Dictionary<string, object> {
                    { "SelectedTurbine", turbine }
                });
            };
        }
    }
}

and in my turbines collection page, I inherit from this page, in order to get the turbines

    public partial class TurbinesCollectionPageViewModel(HttpService service, DeviceLanguageService deviceLanguage,
        TurbinesService turbinesService) : HomePageViewModel(service, deviceLanguage, turbinesService)
    {

        CollectionView? TurbinesCollection;

        [ObservableProperty]
        Turbine? turbine;

        [RelayCommand]
        void PageEnter(CollectionView collectionView)
        {

            if (collectionView != null)
            {

                TurbinesCollection = collectionView;
            }
        }

        [RelayCommand]
        async Task SelectedItemChange(SfComboBox combo)
        {

            if (combo.SelectedIndex < 0)
            {
                return;
            }

            var item = Turbines.ElementAt(combo.SelectedIndex);
            TurbinesCollection?.ScrollTo(combo.SelectedIndex, -1, ScrollToPosition.Center);
            var inputView = combo.Children[1] as Entry;

#if ANDROID || IOS
            if (KeyboardExtensions.IsSoftKeyboardShowing(inputView!))
            {
                await Task.Delay(200);
                await inputView!.HideKeyboardAsync(default);
            }
#else
            await Task.CompletedTask;
#endif
        }
    }

and for I have a search box that can use to search

   <Grid
       Padding="20"
       RowDefinitions="Auto,*"
       RowSpacing="-30">
       <inputLayout:SfTextInputLayout Style="{x:StaticResource TurbineCollectionComboBoxStyle}">
           <editors:SfComboBox
               x:Name="combobox"
               DisplayMemberPath="Turbine.Name"
               ItemsSource="{x:Binding Turbines}"
               Style="{x:DynamicResource TurbinesCollectionComboBoxStyle}">
               <editors:SfComboBox.Behaviors>
                   <mct:EventToCommandBehavior
                       Command="{x:Binding SelectedItemChangeCommand}"
                       CommandParameter="{x:Binding Source={x:Reference combobox}}"
                       EventName="SelectionChanged" />
               </editors:SfComboBox.Behaviors>
           </editors:SfComboBox>
       </inputLayout:SfTextInputLayout>

       <CollectionView
           x:Name="TurbineCollection"
           Grid.Row="1"
           ItemsSource="{x:Binding Turbines}"
           SelectionMode="None"
           Style="{x:StaticResource TurbineCollectionViewStyle}">
           <CollectionView.ItemTemplate>
               <DataTemplate x:DataType="model:TurbinePin">
                   <Border BackgroundColor="#578D57">
                       <Border.StrokeShape>
                           <RoundRectangle CornerRadius="8" />
                       </Border.StrokeShape>
                       <VerticalStackLayout>
                           <Label
                               Style="{x:StaticResource TurbineDataLabelStyleStyle}"
                               Text="{x:Binding Turbine.Name}" />
                           <Label
                               Style="{x:StaticResource TurbineDataLabelStyleStyle}"
                               Text="{x:Binding Turbine.Address}" />
                           <Label
                               Style="{x:StaticResource C02LabelStyle}"
                               Text="{x:Binding Turbine.FinalCo2Removed,
                                                StringFormat='{0:F5} kg CO₂'}" />
                       </VerticalStackLayout>
                       <Border.GestureRecognizers>
                           <TapGestureRecognizer
                               Command="{x:Binding OnPinMarkerClickedCommand,
                                                   Source={x:RelativeSource AncestorType={x:Type vm:TurbinesCollectionPageViewModel}}}"
                               CommandParameter="{x:Binding}" />
                       </Border.GestureRecognizers>
                   </Border>
               </DataTemplate>
           </CollectionView.ItemTemplate>
       </CollectionView>
.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
3,696 questions
0 comments No comments
{count} votes

Accepted answer
  1. Yonglun Liu (Shanghai Wicresoft Co,.Ltd.) 45,181 Reputation points Microsoft Vendor
    2024-11-25T05:35:26.1766667+00:00

    Hello,

    In Maui, the Window supports MVVM data binding. Therefore, you can use data binding to perform Command search and navigation in the SearchBar, which is no different from implementing this function in a regular page.

    You can refer to the following code to learn how to bind ViewModel in Window.

    
    <Window.BindingContext>
    
    	<vm:MyViewModel/>
    
    </Window.BindingContext>
    
    <Window.TitleBar>
    
    	<TitleBar
    
            ...>
    
    		<SearchBar SearchCommand="{Binding SearchCommand}"/>
    
    		...
    
    	</TitleBar>
    
    </Window.TitleBar>
    
    

    Best Regards,

    Alec Liu.


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.