implant mvvm

main
syneffort 1 year ago
parent 7ef194a4cb
commit f92f5f2a79
  1. 41
      MyFirstMauiApp/NotesNet8/Models/Note.cs
  2. 1
      MyFirstMauiApp/NotesNet8/NotesNet8.csproj
  3. 26
      MyFirstMauiApp/NotesNet8/ViewModels/AboutViewModel.cs
  4. 86
      MyFirstMauiApp/NotesNet8/ViewModels/NoteViewModel.cs
  5. 60
      MyFirstMauiApp/NotesNet8/ViewModels/NotesViewModel.cs
  6. 6
      MyFirstMauiApp/NotesNet8/Views/AboutPage.xaml
  7. 8
      MyFirstMauiApp/NotesNet8/Views/AboutPage.xaml.cs
  8. 20
      MyFirstMauiApp/NotesNet8/Views/AllNotesPage.xaml
  9. 22
      MyFirstMauiApp/NotesNet8/Views/AllNotesPage.xaml.cs
  10. 10
      MyFirstMauiApp/NotesNet8/Views/NotePage.xaml
  11. 41
      MyFirstMauiApp/NotesNet8/Views/NotePage.xaml.cs

@ -11,5 +11,46 @@ namespace NotesNet8.Models
public string Filename { get; set; } public string Filename { get; set; }
public string Text { get; set; } public string Text { get; set; }
public DateTime Date { get; set; } public DateTime Date { get; set; }
public Note()
{
Filename = $"{Path.GetRandomFileName()}.notes.txt";
Date = DateTime.Now;
Text = "";
}
public static Note Load(string filename)
{
filename = Path.Combine(FileSystem.AppDataDirectory, filename);
if (!File.Exists(filename))
throw new FileNotFoundException("Unable to find file on local storage.", filename);
return new Note()
{
Filename = Path.GetFileName(filename),
Text = File.ReadAllText(filename),
Date = File.GetLastWriteTime(filename)
};
}
public static IEnumerable<Note> LoadAll()
{
string appDataPath = FileSystem.AppDataDirectory;
return Directory
.EnumerateFiles(appDataPath, "*.notes.txt")
.Select(filename => Note.Load(Path.GetFileName(filename)))
.OrderByDescending(note => note.Date);
}
public void Save()
{
File.WriteAllText(Path.Combine(FileSystem.AppDataDirectory, Filename), Text);
}
public void Delete()
{
File.Delete(Path.Combine(FileSystem.AppDataDirectory, Filename));
}
} }
} }

@ -62,6 +62,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" /> <PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" /> <PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />

@ -0,0 +1,26 @@
using CommunityToolkit.Mvvm.Input;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace NotesNet8.ViewModels
{
internal class AboutViewModel
{
public string Title => AppInfo.Name;
public string Version => AppInfo.VersionString;
public string MoreInfoUrl => "https://aka.ms/maui";
public string Message => "This app is written in XAML and C# with .NET MAUI";
public ICommand ShowMoreInfoCommand { get; }
public AboutViewModel()
{
ShowMoreInfoCommand = new AsyncRelayCommand(ShowMoreInfo);
}
async Task ShowMoreInfo() => await Launcher.Default.OpenAsync(MoreInfoUrl);
}
}

@ -0,0 +1,86 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using NotesNet8.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace NotesNet8.ViewModels
{
internal class NoteViewModel : ObservableObject, IQueryAttributable
{
private Note _note;
public NoteViewModel()
{
_note = new Note();
SaveCommand = new AsyncRelayCommand(Save);
DeleteCommand = new AsyncRelayCommand(Delete);
}
public NoteViewModel(Note note)
{
_note = note;
SaveCommand = new AsyncRelayCommand(Save);
DeleteCommand = new AsyncRelayCommand(Delete);
}
public string Text
{
get => _note.Text;
set
{
if (_note.Text != value)
{
_note.Text = value;
OnPropertyChanged();
}
}
}
public ICommand SaveCommand { get; private set; }
public ICommand DeleteCommand { get; private set; }
public DateTime Date => _note.Date;
public string Identifier => _note.Filename;
private async Task Save()
{
_note.Date = DateTime.Now;
_note.Save();
await Shell.Current.GoToAsync($"..?saved={_note.Filename}");
}
private async Task Delete()
{
_note.Delete();
await Shell.Current.GoToAsync($"..?deleted={_note.Filename}");
}
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
if (query.ContainsKey("load"))
{
_note = Note.Load(query["load"].ToString());
RefreshProperties();
}
}
public void Reload()
{
_note = Note.Load(_note.Filename);
RefreshProperties();
}
private void RefreshProperties()
{
OnPropertyChanged(nameof(Text));
OnPropertyChanged(nameof(Date));
}
}
}

@ -0,0 +1,60 @@
using CommunityToolkit.Mvvm.Input;
using NotesNet8.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace NotesNet8.ViewModels
{
internal class NotesViewModel : IQueryAttributable
{
public ObservableCollection<NoteViewModel> AllNotes { get; }
public ICommand NewCommand { get; }
public ICommand SelectNoteCommand { get; }
public NotesViewModel()
{
AllNotes = new ObservableCollection<NoteViewModel>(Note.LoadAll().Select(n => new NoteViewModel(n)));
NewCommand = new AsyncRelayCommand(NewNoteAsync);
SelectNoteCommand = new AsyncRelayCommand<NoteViewModel>(SelectNoteAsync);
}
public void ApplyQueryAttributes(IDictionary<string, object> query)
{
if (query.ContainsKey("deleted"))
{
string noteId = query["deleted"].ToString();
NoteViewModel matchedNote = AllNotes.Where(n => n.Identifier == noteId).FirstOrDefault();
if (matchedNote != null)
AllNotes.Remove(matchedNote);
}
else if (query.ContainsKey("saved"))
{
string noteId = query["saved"].ToString();
NoteViewModel matchedNote = AllNotes.Where(n => n.Identifier == noteId).FirstOrDefault();
if (matchedNote != null)
{
matchedNote.Reload();
AllNotes.Move(AllNotes.IndexOf(matchedNote), 0);
}
else
AllNotes.Insert(0, new NoteViewModel(Note.Load(noteId)));
}
}
private async Task NewNoteAsync()
{
await Shell.Current.GoToAsync(nameof(Views.NotePage));
}
private async Task SelectNoteAsync(ViewModels.NoteViewModel note)
{
if (note != null)
await Shell.Current.GoToAsync($"{nameof(Views.NotePage)}?load={note.Identifier}");
}
}
}

@ -2,10 +2,10 @@
<ContentPage x:Class="NotesNet8.Views.AboutPage" <ContentPage x:Class="NotesNet8.Views.AboutPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:NotesNet8.Models" xmlns:viewModels="clr-namespace:NotesNet8.ViewModels"
Title="AboutPage"> Title="AboutPage">
<ContentPage.BindingContext> <ContentPage.BindingContext>
<models:About /> <viewModels:AboutViewModel />
</ContentPage.BindingContext> </ContentPage.BindingContext>
<VerticalStackLayout Margin="10" Spacing="10"> <VerticalStackLayout Margin="10" Spacing="10">
@ -20,6 +20,6 @@
</HorizontalStackLayout> </HorizontalStackLayout>
<Label Text="{Binding Message}" /> <Label Text="{Binding Message}" />
<Button Clicked="LearnMore_Clicked" Text="Learn more..." /> <Button Command="{Binding ShowMoreInfoCommand}" Text="Learn more..." />
</VerticalStackLayout> </VerticalStackLayout>
</ContentPage> </ContentPage>

@ -6,12 +6,4 @@ public partial class AboutPage : ContentPage
{ {
InitializeComponent(); InitializeComponent();
} }
private async void LearnMore_Clicked(object sender, EventArgs e)
{
if (BindingContext is Models.About about)
{
await Launcher.Default.OpenAsync(about.MoreInfoUrl);
}
}
} }

@ -2,23 +2,27 @@
<ContentPage x:Class="NotesNet8.Views.AllNotesPage" <ContentPage x:Class="NotesNet8.Views.AllNotesPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:NotesNet8.Models" xmlns:viewModels="clr-namespace:NotesNet8.ViewModels"
Title="AllNotesPage"> Title="AllNotesPage"
NavigatedTo="ContentPage_NavigatedTo">
<ContentPage.BindingContext>
<viewModels:NotesViewModel />
</ContentPage.BindingContext>
<ContentPage.ToolbarItems> <ContentPage.ToolbarItems>
<ToolbarItem Clicked="Add_Clicked" <ToolbarItem Command="{Binding NewCommand}"
IconImageSource="{FontImage Glyph='+', IconImageSource="{FontImage Glyph='+',
Color=Black, Color=Black,
Size=22}" Size=22}"
Text="Add" /> Text="Add" />
</ContentPage.ToolbarItems> </ContentPage.ToolbarItems>
<ContentPage.BindingContext>
<models:AllNotes />
</ContentPage.BindingContext>
<CollectionView x:Name="notesCollection" <CollectionView x:Name="notesCollection"
Margin="20" Margin="20"
ItemsSource="{Binding Notes}" ItemsSource="{Binding AllNotes}"
SelectionChanged="notesCollection_SelectionChanged" SelectionMode="Single"> SelectionMode="Single"
SelectionChangedCommand="{Binding SelectNoteCommand}"
SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItem}">
<CollectionView.ItemsLayout> <CollectionView.ItemsLayout>
<LinearItemsLayout ItemSpacing="10" Orientation="Vertical" /> <LinearItemsLayout ItemSpacing="10" Orientation="Vertical" />
</CollectionView.ItemsLayout> </CollectionView.ItemsLayout>

@ -5,30 +5,10 @@ public partial class AllNotesPage : ContentPage
public AllNotesPage() public AllNotesPage()
{ {
InitializeComponent(); InitializeComponent();
BindingContext = new Models.AllNotes();
}
protected override void OnAppearing()
{
base.OnAppearing();
((Models.AllNotes)BindingContext).LoadNotes();
} }
private async void Add_Clicked(object sender, EventArgs e) private void ContentPage_NavigatedTo(object sender, NavigatedToEventArgs e)
{ {
await Shell.Current.GoToAsync(nameof(NotePage));
}
private async void notesCollection_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.Count == 0)
return;
var note = (Models.Note)e.CurrentSelection[0];
await Shell.Current.GoToAsync($"{nameof(NotePage)}?{nameof(NotePage.ItemId)}={note.Filename}");
notesCollection.SelectedItem = null; notesCollection.SelectedItem = null;
} }
} }

@ -2,10 +2,10 @@
<ContentPage x:Class="NotesNet8.Views.NotePage" <ContentPage x:Class="NotesNet8.Views.NotePage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:NotesNet8.Models" xmlns:viewModels="clr-namespace:NotesNet8.ViewModels"
Title="NotePage"> Title="NotePage">
<ContentPage.BindingContext> <ContentPage.BindingContext>
<models:Note /> <viewModels:NoteViewModel />
</ContentPage.BindingContext> </ContentPage.BindingContext>
<VerticalStackLayout Margin="5" Spacing="10"> <VerticalStackLayout Margin="5" Spacing="10">
@ -14,9 +14,11 @@
Text="{Binding Text}" /> Text="{Binding Text}" />
<Grid ColumnDefinitions="*,*" ColumnSpacing="4"> <Grid ColumnDefinitions="*,*" ColumnSpacing="4">
<Button Grid.Column="0" <Button Grid.Column="0"
Clicked="SaveButton_Clicked" Text="Save" /> Command="{Binding SaveCommand}"
Text="Save" />
<Button Grid.Column="1" <Button Grid.Column="1"
Clicked="DeleteButton_Clicked" Text="Delete" /> Command="{Binding DeleteCommand}"
Text="Delete" />
</Grid> </Grid>
</VerticalStackLayout> </VerticalStackLayout>
</ContentPage> </ContentPage>

@ -1,50 +1,9 @@
namespace NotesNet8.Views; namespace NotesNet8.Views;
[QueryProperty(nameof(ItemId), nameof(ItemId))]
public partial class NotePage : ContentPage public partial class NotePage : ContentPage
{ {
public string ItemId { set { LoadNote(value); } }
public NotePage() public NotePage()
{ {
InitializeComponent(); InitializeComponent();
string appDataPath = FileSystem.AppDataDirectory;
string randomFileName = $"{Path.GetRandomFileName()}.notes.txt";
LoadNote(Path.Combine(appDataPath, randomFileName));
}
private void LoadNote(string fileName)
{
Models.Note noteModel = new Models.Note();
noteModel.Filename = fileName;
if (File.Exists(fileName))
{
noteModel.Date = File.GetCreationTime(fileName);
noteModel.Text = File.ReadAllText(fileName);
}
BindingContext = noteModel;
}
private async void SaveButton_Clicked(object sender, EventArgs e)
{
if (BindingContext is Models.Note note)
File.WriteAllText(note.Filename, TextEditor.Text);
await Shell.Current.GoToAsync("..");
}
private async void DeleteButton_Clicked(object sender, EventArgs e)
{
if (BindingContext is Models.Note note)
{
if (File.Exists(note.Filename))
File.Delete(note.Filename);
}
await Shell.Current.GoToAsync("..");
} }
} }
Loading…
Cancel
Save