From 365a042dd46b5f3f9a1cdf2397480c97cc8f9df4 Mon Sep 17 00:00:00 2001 From: Vignesh-SF3580 <102575140+Vignesh-SF3580@users.noreply.github.com> Date: Wed, 22 Oct 2025 13:39:08 +0530 Subject: [PATCH 1/7] moved changes from maui-samples to maui. --- .../src/templates/maui-mobile/AppShell.xaml | 4 +- .../templates/maui-mobile/AppShell.xaml.cs | 3 + .../Converter/DataLabelValueConverter.cs | 30 ----- .../maui-mobile/Data/TagRepository.cs | 33 +++++ .../src/templates/maui-mobile/MauiProgram.cs | 20 +++ .../maui-mobile/PageModels/MainPageModel.cs | 2 +- .../PageModels/ProjectDetailPageModel.cs | 61 +++++++--- .../PageModels/ProjectListPageModel.cs | 7 +- .../Pages/Controls/CategoryChart.xaml | 29 ++--- .../Pages/Controls/ChartDataLabelConverter.cs | 28 +++++ .../Pages/Controls/ProjectCardView.xaml | 6 +- .../maui-mobile/Pages/Controls/TaskView.xaml | 37 +++--- .../templates/maui-mobile/Pages/MainPage.xaml | 4 +- .../maui-mobile/Pages/ProjectDetailPage.xaml | 115 ++++++++---------- .../maui-mobile/Pages/ProjectListPage.xaml | 39 +++--- .../maui-mobile/Pages/ProjectListPage.xaml.cs | 1 + .../maui-mobile/Pages/TaskDetailPage.xaml | 4 +- .../Platforms/MacCatalyst/Info.plist | 2 + .../Resources/Styles/AppStyles.xaml | 4 +- 19 files changed, 257 insertions(+), 172 deletions(-) delete mode 100644 src/Templates/src/templates/maui-mobile/Converter/DataLabelValueConverter.cs create mode 100644 src/Templates/src/templates/maui-mobile/Pages/Controls/ChartDataLabelConverter.cs diff --git a/src/Templates/src/templates/maui-mobile/AppShell.xaml b/src/Templates/src/templates/maui-mobile/AppShell.xaml index 738a02e3c786..f25afd290c43 100644 --- a/src/Templates/src/templates/maui-mobile/AppShell.xaml +++ b/src/Templates/src/templates/maui-mobile/AppShell.xaml @@ -47,8 +47,8 @@ SegmentWidth="40" SegmentHeight="40"> - - + + diff --git a/src/Templates/src/templates/maui-mobile/AppShell.xaml.cs b/src/Templates/src/templates/maui-mobile/AppShell.xaml.cs index 58fb90dc18ef..e88d6057f64a 100644 --- a/src/Templates/src/templates/maui-mobile/AppShell.xaml.cs +++ b/src/Templates/src/templates/maui-mobile/AppShell.xaml.cs @@ -13,6 +13,9 @@ public AppShell() #if (IncludeSampleContent) var currentTheme = Application.Current!.RequestedTheme; ThemeSegmentedControl.SelectedIndex = currentTheme == AppTheme.Light ? 0 : 1; +#endif +#if ANDROID || WINDOWS + SemanticProperties.SetDescription(ThemeSegmentedControl, "Theme selection"); #endif } #if (IncludeSampleContent) diff --git a/src/Templates/src/templates/maui-mobile/Converter/DataLabelValueConverter.cs b/src/Templates/src/templates/maui-mobile/Converter/DataLabelValueConverter.cs deleted file mode 100644 index 3578fec9097d..000000000000 --- a/src/Templates/src/templates/maui-mobile/Converter/DataLabelValueConverter.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Globalization; -using Microsoft.Maui.Controls; -using MauiApp._1.Models; - -namespace MauiApp._1.Converter; - -public class DataLabelValueConverter : IValueConverter -{ - public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) - { - if (value is CategoryChartData categoryChartData) - { - switch (parameter?.ToString()) - { - case "Title": - return categoryChartData.Title; - - case "Count": - return categoryChartData.Count; - } - } - - return value; - } - - public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) - { - return value; - } -} diff --git a/src/Templates/src/templates/maui-mobile/Data/TagRepository.cs b/src/Templates/src/templates/maui-mobile/Data/TagRepository.cs index 7f2a5ed4a479..ee87d540ad15 100644 --- a/src/Templates/src/templates/maui-mobile/Data/TagRepository.cs +++ b/src/Templates/src/templates/maui-mobile/Data/TagRepository.cs @@ -200,6 +200,12 @@ public async Task SaveItemAsync(Tag item, int projectID) await Init(); await SaveItemAsync(item); + var isAssociated = await IsAssociated(item, projectID); + if (isAssociated) + { + return 0; // No need to save again if already associated + } + await using var connection = new SqliteConnection(Constants.DatabasePath); await connection.OpenAsync(); @@ -212,6 +218,33 @@ public async Task SaveItemAsync(Tag item, int projectID) return await saveCmd.ExecuteNonQueryAsync(); } + /// + /// Checks if a tag is already associated with a specific project. + /// + /// The tag to save. + /// The ID of the project. + /// If tag is already associated with this project + async Task IsAssociated(Tag item, int projectID) + { + await Init(); + await SaveItemAsync(item); + + await using var connection = new SqliteConnection(Constants.DatabasePath); + await connection.OpenAsync(); + + // First check if the association already exists + var checkCmd = connection.CreateCommand(); + checkCmd.CommandText = @" + SELECT COUNT(*) FROM ProjectsTags + WHERE ProjectID = @projectID AND TagID = @tagID"; + checkCmd.Parameters.AddWithValue("@projectID", projectID); + checkCmd.Parameters.AddWithValue("@tagID", item.ID); + + int existingCount = Convert.ToInt32(await checkCmd.ExecuteScalarAsync()); + + return existingCount != 0; + } + /// /// Deletes a tag from the database. /// diff --git a/src/Templates/src/templates/maui-mobile/MauiProgram.cs b/src/Templates/src/templates/maui-mobile/MauiProgram.cs index 32ebbe27ed96..081300e68081 100644 --- a/src/Templates/src/templates/maui-mobile/MauiProgram.cs +++ b/src/Templates/src/templates/maui-mobile/MauiProgram.cs @@ -19,6 +19,26 @@ public static MauiApp CreateMauiApp() .UseMauiCommunityToolkit() .ConfigureSyncfusionToolkit() #endif + .ConfigureMauiHandlers(handlers => + { +#if IOS || MACCATALYST + handlers.AddHandler(); +#endif +#if WINDOWS + Microsoft.Maui.Controls.Handlers.Items.CollectionViewHandler.Mapper.AppendToMapping("KeyboardAccessibleCollectionView", (handler, view) => + { + handler.PlatformView.SingleSelectionFollowsFocus = false; + }); + + Microsoft.Maui.Handlers.ContentViewHandler.Mapper.AppendToMapping(nameof(Pages.Controls.CategoryChart), (handler, view) => + { + if (view is Pages.Controls.CategoryChart && handler.PlatformView is ContentPanel contentPanel) + { + contentPanel.IsTabStop = true; + } + }); +#endif + }) .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); diff --git a/src/Templates/src/templates/maui-mobile/PageModels/MainPageModel.cs b/src/Templates/src/templates/maui-mobile/PageModels/MainPageModel.cs index c0c3d3a10156..d4c6f1ec07cf 100644 --- a/src/Templates/src/templates/maui-mobile/PageModels/MainPageModel.cs +++ b/src/Templates/src/templates/maui-mobile/PageModels/MainPageModel.cs @@ -37,7 +37,7 @@ public partial class MainPageModel : ObservableObject, IProjectTaskPageModel [ObservableProperty] private Project? selectedProject; - + public bool HasCompletedTasks => Tasks?.Any(t => t.IsCompleted) ?? false; diff --git a/src/Templates/src/templates/maui-mobile/PageModels/ProjectDetailPageModel.cs b/src/Templates/src/templates/maui-mobile/PageModels/ProjectDetailPageModel.cs index 0d51bd25204a..f87c2acde70a 100644 --- a/src/Templates/src/templates/maui-mobile/PageModels/ProjectDetailPageModel.cs +++ b/src/Templates/src/templates/maui-mobile/PageModels/ProjectDetailPageModel.cs @@ -1,6 +1,8 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using MauiApp._1.Models; +using System.Collections.ObjectModel; +using System.Windows.Input; namespace MauiApp._1.PageModels; @@ -34,6 +36,8 @@ public partial class ProjectDetailPageModel : ObservableObject, IQueryAttributab [ObservableProperty] private List _allTags = []; + public IList SelectedTags { get; set; } = new List(); + [ObservableProperty] private IconData _icon; @@ -147,6 +151,10 @@ private async Task LoadData(int id) foreach (var tag in allTags) { tag.IsSelected = _project.Tags.Any(t => t.ID == tag.ID); + if (tag.IsSelected) + { + SelectedTags.Add(tag); + } } AllTags = new(allTags); } @@ -169,7 +177,6 @@ private async Task TaskCompleted(ProjectTask task) OnPropertyChanged(nameof(HasCompletedTasks)); } - [RelayCommand] private async Task Save() { @@ -187,14 +194,11 @@ private async Task Save() _project.Icon = Icon.Icon ?? FluentUI.ribbon_24_regular; await _projectRepository.SaveItemAsync(_project); - if (_project.IsNullOrNew()) + foreach (var tag in AllTags) { - foreach (var tag in AllTags) + if (tag.IsSelected) { - if (tag.IsSelected) - { - await _tagRepository.SaveItemAsync(tag, _project.ID); - } + await _tagRepository.SaveItemAsync(tag, _project.ID); } } @@ -249,7 +253,7 @@ private Task NavigateToTask(ProjectTask task) => Shell.Current.GoToAsync($"task?id={task.ID}"); [RelayCommand] - private async Task ToggleTag(Tag tag) + internal async Task ToggleTag(Tag tag) { tag.IsSelected = !tag.IsSelected; @@ -258,20 +262,15 @@ private async Task ToggleTag(Tag tag) if (tag.IsSelected) { await _tagRepository.SaveItemAsync(tag, _project.ID); - AllTags = new(AllTags); - SemanticScreenReader.Announce($"{tag.Title} selected"); } else { await _tagRepository.DeleteItemAsync(tag, _project.ID); - AllTags = new(AllTags); - SemanticScreenReader.Announce($"{tag.Title} unselected"); } } - else - { - AllTags = new(AllTags); - } + + AllTags = new(AllTags); + SemanticScreenReader.Announce($"{tag.Title} {(tag.IsSelected ? "selected" : "unselected")}"); } [RelayCommand] @@ -294,4 +293,34 @@ private async Task CleanTasks() OnPropertyChanged(nameof(HasCompletedTasks)); await AppShell.DisplayToastAsync("All cleaned up!"); } + + [RelayCommand] + private async Task SelectionChanged(object parameter) + { + if (parameter is IEnumerable enumerableParameter) + { + var currentSelection = enumerableParameter.OfType().ToList(); + var previousSelection = AllTags.Where(t => t.IsSelected).ToList(); + + // Handle newly selected tags + foreach (var tag in currentSelection.Except(previousSelection)) + { + tag.IsSelected = true; + if (!_project.IsNullOrNew()) + { + await _tagRepository.SaveItemAsync(tag, _project.ID); + } + } + + // Handle deselected tags + foreach (var tag in previousSelection.Except(currentSelection)) + { + tag.IsSelected = false; + if (!_project.IsNullOrNew()) + { + await _tagRepository.DeleteItemAsync(tag, _project.ID); + } + } + } + } } diff --git a/src/Templates/src/templates/maui-mobile/PageModels/ProjectListPageModel.cs b/src/Templates/src/templates/maui-mobile/PageModels/ProjectListPageModel.cs index b0299b28e958..4b7f0cdcbf0f 100644 --- a/src/Templates/src/templates/maui-mobile/PageModels/ProjectListPageModel.cs +++ b/src/Templates/src/templates/maui-mobile/PageModels/ProjectListPageModel.cs @@ -14,6 +14,9 @@ public partial class ProjectListPageModel : ObservableObject [ObservableProperty] private List _projects = []; + [ObservableProperty] + private Project? selectedProject; + public ProjectListPageModel(ProjectRepository projectRepository) { _projectRepository = projectRepository; @@ -26,8 +29,8 @@ private async Task Appearing() } [RelayCommand] - Task NavigateToProject(Project project) - => Shell.Current.GoToAsync($"project?id={project.ID}"); + Task? NavigateToProject(Project project) + => project is null ? null : Shell.Current.GoToAsync($"project?id={project.ID}"); [RelayCommand] async Task AddProject() diff --git a/src/Templates/src/templates/maui-mobile/Pages/Controls/CategoryChart.xaml b/src/Templates/src/templates/maui-mobile/Pages/Controls/CategoryChart.xaml index df15a8dd91f9..4eddbc736421 100644 --- a/src/Templates/src/templates/maui-mobile/Pages/Controls/CategoryChart.xaml +++ b/src/Templates/src/templates/maui-mobile/Pages/Controls/CategoryChart.xaml @@ -6,7 +6,6 @@ xmlns:shimmer="clr-namespace:Syncfusion.Maui.Toolkit.Shimmer;assembly=Syncfusion.Maui.Toolkit" xmlns:pageModels="clr-namespace:MauiApp._1.PageModels" xmlns:models="clr-namespace:MauiApp._1.Models" - xmlns:converter="clr-namespace:MauiApp._1.Converter" x:Class="MauiApp._1.Pages.Controls.CategoryChart" x:DataType="pageModels:MainPageModel" HeightRequest="{OnIdiom 300, Phone=200}" @@ -16,7 +15,6 @@ AutomationProperties.IsInAccessibleTree="False" BackgroundColor="Transparent" VerticalOptions="Fill" - x:DataType="pageModels:MainPageModel" IsActive="{Binding IsBusy}"> @@ -28,12 +26,11 @@ + SemanticProperties.Description="Task Categories Chart"> - + - - + SmartLabelAlignment="Shift"> + StrokeWidth="3"> diff --git a/src/Templates/src/templates/maui-mobile/Pages/Controls/ChartDataLabelConverter.cs b/src/Templates/src/templates/maui-mobile/Pages/Controls/ChartDataLabelConverter.cs new file mode 100644 index 000000000000..f9aba252c78b --- /dev/null +++ b/src/Templates/src/templates/maui-mobile/Pages/Controls/ChartDataLabelConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Globalization; +using MauiApp._1.Models; + +namespace MauiApp._1.Pages.Controls; + +public class ChartDataLabelConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + if (value is CategoryChartData categoryData && parameter is string parameterValue) + { + return parameterValue?.ToLower() switch + { + "title" => categoryData.Title, + "count" => categoryData.Count.ToString(), + _ => value?.ToString() + }; + } + + return value?.ToString(); + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} diff --git a/src/Templates/src/templates/maui-mobile/Pages/Controls/ProjectCardView.xaml b/src/Templates/src/templates/maui-mobile/Pages/Controls/ProjectCardView.xaml index f38e5bf8a644..e4738f1887cd 100644 --- a/src/Templates/src/templates/maui-mobile/Pages/Controls/ProjectCardView.xaml +++ b/src/Templates/src/templates/maui-mobile/Pages/Controls/ProjectCardView.xaml @@ -9,10 +9,10 @@ x:Class="MauiApp._1.Pages.Controls.ProjectCardView" SemanticProperties.Description="{Binding AccessibilityDescription}" Style="{StaticResource CardStyle}" + MinimumHeightRequest="250" x:DataType="models:Project"> @@ -48,13 +48,13 @@ diff --git a/src/Templates/src/templates/maui-mobile/Pages/Controls/TaskView.xaml b/src/Templates/src/templates/maui-mobile/Pages/Controls/TaskView.xaml index 02c260051436..1890f42b2edd 100644 --- a/src/Templates/src/templates/maui-mobile/Pages/Controls/TaskView.xaml +++ b/src/Templates/src/templates/maui-mobile/Pages/Controls/TaskView.xaml @@ -11,12 +11,12 @@ Background="{AppThemeBinding Light={StaticResource LightSecondaryBackground}, Dark={StaticResource DarkSecondaryBackground}}" x:DataType="models:ProjectTask"> - - - - - - + + + + + - - diff --git a/src/Templates/src/templates/maui-mobile/Pages/MainPage.xaml b/src/Templates/src/templates/maui-mobile/Pages/MainPage.xaml index 484677096300..baaa071ce932 100644 --- a/src/Templates/src/templates/maui-mobile/Pages/MainPage.xaml +++ b/src/Templates/src/templates/maui-mobile/Pages/MainPage.xaml @@ -38,7 +38,7 @@ RefreshCommand="{Binding RefreshCommand}"> - + @@ -47,7 +47,7 @@ - +