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. 24
      MyFirstMauiApp/NotesNet8/Views/AllNotesPage.xaml.cs
  10. 12
      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 Text { 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>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)" />
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="$(MauiVersion)" />
<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"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:NotesNet8.Models"
xmlns:viewModels="clr-namespace:NotesNet8.ViewModels"
Title="AboutPage">
<ContentPage.BindingContext>
<models:About />
<viewModels:AboutViewModel />
</ContentPage.BindingContext>
<VerticalStackLayout Margin="10" Spacing="10">
@ -20,6 +20,6 @@
</HorizontalStackLayout>
<Label Text="{Binding Message}" />
<Button Clicked="LearnMore_Clicked" Text="Learn more..." />
<Button Command="{Binding ShowMoreInfoCommand}" Text="Learn more..." />
</VerticalStackLayout>
</ContentPage>

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

@ -5,30 +5,10 @@ public partial class AllNotesPage : ContentPage
public AllNotesPage()
{
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,21 +2,23 @@
<ContentPage x:Class="NotesNet8.Views.NotePage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:models="clr-namespace:NotesNet8.Models"
xmlns:viewModels="clr-namespace:NotesNet8.ViewModels"
Title="NotePage">
<ContentPage.BindingContext>
<models:Note />
<viewModels:NoteViewModel />
</ContentPage.BindingContext>
<VerticalStackLayout Margin="5" Spacing="10">
<Editor x:Name="TextEditor"
HeightRequest="100" Placeholder="Enter your note"
Text="{Binding Text}"/>
Text="{Binding Text}" />
<Grid ColumnDefinitions="*,*" ColumnSpacing="4">
<Button Grid.Column="0"
Clicked="SaveButton_Clicked" Text="Save" />
Command="{Binding SaveCommand}"
Text="Save" />
<Button Grid.Column="1"
Clicked="DeleteButton_Clicked" Text="Delete" />
Command="{Binding DeleteCommand}"
Text="Delete" />
</Grid>
</VerticalStackLayout>
</ContentPage>

@ -1,50 +1,9 @@
namespace NotesNet8.Views;
[QueryProperty(nameof(ItemId), nameof(ItemId))]
public partial class NotePage : ContentPage
{
public string ItemId { set { LoadNote(value); } }
public NotePage()
{
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