Skip to content

Instantly share code, notes, and snippets.

@JordanMarr
Created May 12, 2021 23:45
Show Gist options
  • Save JordanMarr/dfe34745a9617234a9dfb2294c382508 to your computer and use it in GitHub Desktop.
Save JordanMarr/dfe34745a9617234a9dfb2294c382508 to your computer and use it in GitHub Desktop.
Xamarin TODO C# MVU
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
Shell.NavBarIsVisible="False"
Shell.TabBarIsVisible="False"
x:Name="xTodoPage"
x:Class="XamarinStore.Views.TodoPage">
<ContentPage.Resources>
<ResourceDictionary>
<xct:InvertedBoolConverter x:Key="InvertedBoolConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout Margin="5">
<Label Text="TODO" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" FontAttributes="Bold" FontSize="42" />
<CollectionView ItemsSource="{Binding Todos}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<StackLayout Orientation="Horizontal" Spacing="10" Margin="0,5,0,0">
<Button Text="Toggle" Command="{Binding BindingContext.ToggleDoneCmd, Source={x:Reference xTodoPage}}" CommandParameter="{Binding}" />
<Label Text="{Binding Description}" TextDecorations="Strikethrough" IsVisible="{Binding IsDone}" FontSize="28" />
<Label Text="{Binding Description}" TextDecorations="None" IsVisible="{Binding IsDone, Converter={StaticResource InvertedBoolConverter}}" FontSize="28" />
</StackLayout>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<StackLayout>
<Label Text="New Todo:" VerticalTextAlignment="Center" />
<Entry Text="{Binding NewDescription, Mode=TwoWay}" />
</StackLayout>
<Button Text="Add Todo" Command="{Binding AddTodoCmd}" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
namespace XamarinStore.Models
{
public record Todo(
bool IsDone,
string Description
);
public record TodoStoreModel(
string NewDescription,
Todo[] Todos
) {
public static TodoStoreModel Init => new TodoStoreModel("", Array.Empty<Todo>());
}
public class TodoStore : Store<TodoStoreModel>
{
public TodoStore() : base(TodoStoreModel.Init) { }
public record SetNewDescription(string Text) : Msg;
public record AddTodo() : Msg;
public record ToggleDone(Todo Todo) : Msg;
public override TodoStoreModel Update(TodoStoreModel model, Msg message)
{
switch (message)
{
case SetNewDescription msg:
return model with { NewDescription = msg.Text };
case AddTodo _:
return model with {
Todos = model.Todos.Union(new[] { new Todo(false, model.NewDescription) }).ToArray(),
NewDescription = ""
};
case ToggleDone msg:
return model with
{ Todos =
model.Todos
.Select(t => t.Description == msg.Todo.Description ? t with { IsDone = !t.IsDone } : t)
.ToArray()
};
default:
throw new Exception($"Unhandled Update Msg: {message.GetType().Name}");
}
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;
using XamarinStore.Models;
namespace XamarinStore.ViewModels
{
public class TodoViewModel : BaseViewModel
{
TodoStore _store;
public TodoViewModel(TodoStore store)
{
_store = store;
_store.Notify(m => m.NewDescription, OnPropertyChanged, () => NewDescription);
_store.Notify(m => m.Todos, OnPropertyChanged, () => Todos);
}
public string NewDescription
{
get => _store.Model.NewDescription;
set => _store.Dispatch(new TodoStore.SetNewDescription(value));
}
public Todo[] Todos => _store.Model.Todos;
public ICommand AddTodoCmd => _store.DispatchCommand(new TodoStore.AddTodo());
public ICommand ToggleDoneCmd => _store.DispatchCommand<Todo>(todo => new TodoStore.ToggleDone(todo));
}
}
@JordanMarr
Copy link
Author

Very nice -- I love it!
I already did install the Laconic template when I was experimenting with it the other week.
The only reason I'm not using it is because of hot reload. Don Syme create a hot reload mechanism for Fabulous that recompiles your changes to the code on-the-fly; without a hot reload mechanism, and no preview, it would be laborious.

Sadly, even the xaml hot reload breaks pretty easily which forces many reloads.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment