Plataforma UNO - Criando um App ToDo - parte 4

Imagem de capa Plataforma UNO - Criando um App ToDo - parte 4

Plataforma UNO - Plataforma UNO - Part 4

Bem-vindo de volta à nossa série: Como criar um aplicativo de tarefas com a plataforma Uno. Como uma pequena recapitulação, criamos na última parte da série um belo formulário que também valida nosso modelo. Isso parecia chique, mas esse foi o fim do show. Vamos começar exatamente a partir daí e adicionar o elemento na raia direita.

Um pequeno teaser para onde vamos

Devolva o item

Nossa caixa de diálogo foi capaz de criar o item de tarefas e só permite ao usuário pressionar "Adicionar" quando o modelo estiver em um estado válido. Você pode se surpreender, mas terminamos com o diálogo em si. Como anexamos o viewmodel ao DataContext de nossa caixa de diálogo, podemos usar isso em nosso botão. Lembre-se de pressionar o botão vermelho abriu a caixa de diálogo. Então vamos a nossa próxima parada:

public  event EventHandler<Todo> TodoItemCreated;

private  void  OpenDialog(object sender, RoutedEventArgs args)
{
     var dialog = new AddTodoItemDialog();
     
     // We will hook into the PrimaryButtonClick here
     dialog.PrimaryButtonClick += (s, a) => NewTodoItemCreated((NewTodoItemViewModel)dialog.DataContext);
     dialog.ShowAsync();
}

private  void  NewTodoItemCreated(NewTodoItemViewModel viewModel)
{
     var todo = new Todo
     {
         Description = viewModel.Description,
         Title = viewModel.Title,
         DueDate = viewModel.DueDate.DateTime,
         KanbanState = KanbanState.New,
     };
     
     TodoItemCreated?.Invoke(this, todo);
}

A ideia é simples:

  • Anexar ao botão principal Clique que de fato é o seu botão Adicionar
  • Crie nosso objeto de domínio a partir do modelo de exibição e aumente o TodoItemCreated recém-criado com esse item

Agora o pai é responsável por se inscrever no evento TodoItemCreated e fazer algo com ele. O pai do nosso botão é a própria MainPage. Então será hora de fazer algum trabalho aqui.

MainPage.xaml

Ver modelo

Antes de fazermos qualquer coisa no evento, precisamos de algum tipo de armazenamento. Usaremos um modelo de exibição recém-criado para fazer isso. Nosso ViewModel é bastante simples:

MainPageViewModel.cs

using MvvmHelpers;
using TodoApp.Domain;

namespace  TodoApp
{ 
    public  class  MainPageViewModel : ObservableObject
    {
        private ObservableRangeCollection<Todo> todoItems = new ObservableRangeCollection<Todo>();
        
        public ObservableRangeCollection<Todo> TodoItems
        {
            get => todoItems;
            set
            {
                todoItems = value;
                OnPropertyChanged();
             }
         }
     }
}

Nós apenas temos um ObservableCollection que contém todos os nossos itens Todo. O ObservableCollection tem a vantagem de funcionar bem com a plataforma Uno (como no WPF ou UWP). Então, toda vez que adicionamos um elemento, todas as ligações são notificadas para que não tenhamos que fazer isso por conta própria. Agora que temos algum tipo de estado, podemos nos conectar ao evento do nosso botão e adicionar o elemento à lista.

Coloque o item em nosso modelo de visualização

Tal como acontece com todos os eventos C#, apenas nos anexamos no código por trás e adicionamos o item recém-criado ao nosso modelo de exibição:

MainPage.xaml.cs

public  sealed  partial  class  MainPage : Page
{ 
    public MainPage()
    {
        InitializeComponent();
        DataContext = new MainPageViewModel();
        addItemButton.TodoItemCreated += (o, item) => ((MainPageViewModel)DataContext).TodoItems.Add(item);
    }
}

Antes de continuarmos, um fato importante que vamos aproveitar a seguir: DataContext é herdado automaticamente por componentes filho se não for substituído. Isso significa que nossa Swimlane agora tem automaticamente todos os itens Todo, embora nunca tenhamos declarado isso explicitamente. Neste tutorial, usarei esse fato e não criarei um modelo de exibição separado para a Swimlane.

Adicionando-o à Swimlane

Conforme descrito acima, já adicionamos esse novo item indiretamente à nossa Swimlane, pois a Swimlane herda o DataContext da MainPage. Vamos apenas adicionar algum conteúdo à Swimlane. Já começamos um pouco, mas em teoria nada é mostrado. Então vamos estender nosso <ListView.ItemTemplate>. Também vamos definir o ListView.ItemContainerStyle para usar toda a largura do StackPanel.

Swimlane.xaml

<UserControl
    x:Class="TodoApp.Swimlane"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TodoApp"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

     <StackPanel  MinWidth="200"  MinHeight="400"  BorderBrush="DarkOliveGreen"  BorderThickness="2">
         <TextBlock Text="{x:Bind State}" HorizontalAlignment="Center"></TextBlock>
         <ListView x:Name="itemListView" HorizontalContentAlignment="Stretch">
             <ListView.ItemContainerStyle>
                 <Style  TargetType="ListViewItem">
                     <Setter  Property="HorizontalContentAlignment"  Value="Stretch"/>
                 </Style>
             </ListView.ItemContainerStyle>
             <ListView.ItemTemplate>
                 <DataTemplate>
                      <local:TodoItem></local:TodoItem>
                 </DataTemplate>
             </ListView.ItemTemplate>
        </ListView>
   </StackPanel>
</UserControl>

Hupps, agora está faltando algo: O controle de usuário TodoItem. Por enquanto vamos mantê-lo muito simples. Apenas mostramos todas as propriedades que temos em um StackPanel. Portanto, crie um novo Controle de Usuário da Plataforma Uno:

TodoItem.xaml

<UserControl
    x:Class="TodoApp.TodoItem"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:TodoApp"
    xmlns:domain="using:TodoApp.Domain"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DataContext="{d:DesignInstance Type=domain:Todo}"
    d:DesignHeight="300"
    d:DesignWidth="400">
    <StackPanel>
         <TextBlock Text="{Binding Path=Title}" TextAlignment="Center" FontWeight="Bold"></TextBlock>
         <TextBlock Text="{Binding Path=Description}" Margin="0,10,0,0"></TextBlock>
         <TextBlock Text="{Binding Path=DueDate}" Margin="0,10,0,0"></TextBlock>
    </StackPanel>
 </UserControl>

Também aqui usaremos o DataContext. O DataContext do ponto de vista do nosso TodoItem não é mais o ObservableCollection, mas um único TodoItem. Perfeito! A propósito, com: d:DataContext="{d:DesignInstance Type=domain:Todo}" você pode ajudar o IntelliSense a mostrar as propriedades corretas.

Parece que terminamos, ou? Vamos acessar o compilador e ver o que criamos:

Hmmm, não queremos ter esse único item Todo em todas as nossas pistas. Uma Swimlane é responsável por um KanbanState. A questão é que toda a nossa raia vê o mesmo conteúdo. Até agora não sabem filtrar nada.

Filtre as swimlanes

Para filtrar nossas Swimlanes de acordo com o estado definido anterior, vamos aproveitar um bom componente: AdvancedCollectionView. O AdvancedCollectionView faz parte do Community Toolkit.

Agora, o mais legal da plataforma Uno é que eles portaram o kit de ferramentas da comunidade. Se quiser saber mais acesse aqui. Vou resumir brevemente e orientá-lo com os pontos mais importantes.

Para o nosso caso, precisamos adicionar o pacote nuget Uno.Microsoft.Toolkit.Uwp.UI, mas:

  • Adicione apenas o Uno.Microsoft.Toolkit.Uwp.UI a todos os projetos de produção além do UWP ou do cabeçote WinUI3
  • Para cabeça UWP ou WinUI3, use Microsoft.Toolkit.Uwp.UI
  • Mantenha as versões alinhadas. No meu caso ambos os pacotes possuem a versão 7.1.1.
  • A plataforma Uno* está totalmente alinhada com o nome dos pacotes. Eles apenas prefixaram com Uno
  • Os namespaces também são os mesmos

Agora que adicionamos o(s) pacote(s). Podemos usar o AdvancedCollectionView. Para isso, vamos para Swimlane.xaml.cs:

public  Swimlane()
{
    InitializeComponent();
    DataContextChanged += SetFilter;
}

private  void  SetFilter(FrameworkElement sender, DataContextChangedEventArgs args)
{
    var view = new AdvancedCollectionView(((MainPageViewModel)this.DataContext).TodoItems, true);
    view.Filter = item => ((Todo)item).KanbanState == State;
    itemListView.ItemsSource = view;
}

Agora o que estamos fazendo aqui. Uma vez que o DataContext é carregado ou alterado (via DataContextChanged), aplicamos nosso método Filter. Já apresentamos o Estado em um episódio anterior da série (para usá-lo no cabeçalho). Por último, mas não menos importante, definimos o ItemsSource de nosso ListView para o AdvancedCollectionView recém-criado e filtrado. Muito direto!. Agora, se você compilar novamente e executar o aplicativo, verá a imagem mostrada no início:

Algo que me incomodou foi que a data "padrão" para o nosso NewTodoItemViewModel é basicamente padrão (DateTimeOffset), que é 1922. Podemos resolver isso facilmente via: private DateTimeOffset dueDate = DateTimeOffset.Now;.

E aí vai. Adicionamos nosso item de tarefas na coluna da direita e também fizemos o trabalho de base para ajustes adicionais.

Recursos

  • O repositório do github para este Todo-App: aqui
  • Site oficial da Plataforma UNO: aqui