Skip to main content

Overview

FlowKanbanHome is a home screen control for listing and managing multiple Kanban boards. It provides a responsive grid of boards with thumbnails, search filtering, sorting options, and board actions (open, rename, duplicate, delete, export). Namespace: Flowery.Uno.Kanban.Controls
Assembly: Flowery.Uno.Kanban.dll

Key Features

  • Board Grid: Responsive card-based layout with board thumbnails
  • Search: Filter boards by title with real-time updates
  • Sort: Order by name (A-Z, Z-A) or modification date (newest/oldest)
  • Board Actions: Open, rename, duplicate, delete, export via context menu
  • Empty State: Shows welcome message when no boards exist
  • Welcome Banner: Configurable welcome message with title and subtitle
  • Localization: Fully localized UI strings

Basic Usage

Standalone Control

<kanban:FlowKanbanHome
    Boards="{x:Bind Boards}"
    OpenBoardCommand="{x:Bind OpenBoardCommand}"
    CreateBoardCommand="{x:Bind CreateBoardCommand}"
    RenameBoardHomeCommand="{x:Bind RenameBoardCommand}"
    DeleteBoardCommand="{x:Bind DeleteBoardCommand}"
    DuplicateBoardCommand="{x:Bind DuplicateBoardCommand}"
    ExportBoardCommand="{x:Bind ExportBoardCommand}"
    SearchText="{x:Bind SearchText, Mode=TwoWay}"
    SortMode="{x:Bind SortMode, Mode=TwoWay}"
    ShowWelcomeMessage="True"
    WelcomeMessageTitle="Welcome to Kanban"
    WelcomeMessageSubtitle="Create or open a board to get started" />

With FlowKanban

FlowKanban automatically creates and manages a FlowKanbanHome instance internally. Navigate to it using the ShowHomeCommand.
// Switch to Home view
Kanban.ShowHomeCommand.Execute(null);

// Access boards list
var boards = Kanban.Boards;

Properties

Board Management

Boards
ObservableCollection<FlowBoardMetadata>
Collection of available boards. Add/remove boards to update the grid.FlowBoardMetadata Properties:
  • Id: string - Unique board identifier
  • Title: string - Board title
  • LastModified: DateTime - Last modification timestamp
  • TaskCount: int - Total task count
  • ColumnCount: int - Total column count
FilteredBoards
ObservableCollection<FlowBoardMetadata>
Filtered and sorted boards based on SearchText and SortMode. Automatically updated when search or sort changes.
HasBoards
bool
True when FilteredBoards is not empty (after search/sort).
HasAnyBoards
bool
True when Boards is not empty (before search/sort).
IsEmptyStateVisible
bool
True when no boards match the current search/sort. Shows empty state message.

Search & Sort

SearchText
string
default:""
Search text used to filter boards by title (case-insensitive contains match).
SortMode
FlowKanbanHomeSortMode
default:"RecentlyModified"
Current sort mode. Values:
  • RecentlyModified - Newest first (default)
  • RecentlyModifiedAscending - Oldest first
  • NameAscending - A-Z
  • NameDescending - Z-A
SortModeIndex
int
default:"0"
Index of current sort mode (for ComboBox binding). Synced with SortMode:
  • 0 = RecentlyModified
  • 1 = NameAscending
  • 2 = NameDescending
  • 3 = RecentlyModifiedAscending

Welcome Message

ShowWelcomeMessage
bool
default:"true"
Controls visibility of the welcome banner.
WelcomeMessageTitle
string
default:""
Custom title for the welcome banner. Leave empty to hide title row.
WelcomeMessageSubtitle
string
default:""
Custom subtitle for the welcome banner. Leave empty to hide subtitle row.
IsWelcomeMessageVisible
bool
True when welcome banner should be shown (based on ShowWelcomeMessage and non-empty title/subtitle).
WelcomeTitleDisplay
string
Computed welcome title for display (uses custom title or empty string).
WelcomeSubtitleDisplay
string
Computed welcome subtitle for display (uses custom subtitle or empty string).

Localization

Localization
FloweryLocalization
default:"FloweryLocalization.Instance"
Localization instance for UI strings. Automatically updates when culture changes.

Commands

Board Actions

OpenBoardCommand
ICommand
Parameter: FlowBoardMetadata
Opens the specified board. Typically navigates to FlowKanban board view.
OpenBoardCommand = new RelayCommand<FlowBoardMetadata>(board =>
{
    LoadBoard(board.Id);
    CurrentView = FlowKanbanView.Board;
});
CreateBoardCommand
ICommand
Creates a new blank board.
CreateBoardCommand = new RelayCommand(() =>
{
    var newBoard = new FlowKanbanData { Title = "New Board" };
    SaveBoard(newBoard);
    RefreshBoards();
});
CreateDemoBoardCommand
ICommand
Creates a demo board with sample data.
CreateDemoBoardCommand = new RelayCommand(() =>
{
    var demo = CreateDemoBoard();
    SaveBoard(demo);
    RefreshBoards();
});
RenameBoardHomeCommand
ICommand
Parameter: FlowBoardMetadata
Opens rename dialog for the specified board.
RenameBoardHomeCommand = new RelayCommand<FlowBoardMetadata>(async board =>
{
    var newTitle = await ShowRenameBoardDialog(board.Title);
    if (!string.IsNullOrWhiteSpace(newTitle))
    {
        RenameBoard(board.Id, newTitle);
        RefreshBoards();
    }
});
DeleteBoardCommand
ICommand
Parameter: FlowBoardMetadata
Deletes the specified board (with confirmation).
DeleteBoardCommand = new RelayCommand<FlowBoardMetadata>(async board =>
{
    var confirmed = await ShowConfirmDeleteDialog(board.Title);
    if (confirmed)
    {
        DeleteBoard(board.Id);
        RefreshBoards();
    }
});
DuplicateBoardCommand
ICommand
Parameter: FlowBoardMetadata
Duplicates the specified board.
DuplicateBoardCommand = new RelayCommand<FlowBoardMetadata>(board =>
{
    DuplicateBoard(board.Id, out var newId);
    RefreshBoards();
});
ExportBoardCommand
ICommand
Parameter: FlowBoardMetadata
Exports the specified board to JSON file (Windows only).
ExportBoardCommand = new RelayCommand<FlowBoardMetadata>(async board =>
{
    var json = ExportBoardToJson(board.Id);
    await SaveToFile(json, $"{board.Title}.json");
});

Board Card Layout

Each board card in the grid displays:
  • Title: Board name (truncated with ellipsis if too long)
  • Last Modified: Relative timestamp (“2 hours ago”, “Yesterday”, etc.)
  • Task Count: Total tasks in board
  • Column Count: Total columns in board
  • Menu Button: Context menu with actions (Open, Rename, Duplicate, Export, Delete)

Card Actions

  • Tap card surface: Opens board (same as Open menu action)
  • Tap menu button: Shows context menu with all actions

Search & Sort Behavior

  • Case-insensitive: “kanban” matches “Kanban”, “KANBAN”, “kanBAN”
  • Contains match: “sprint” matches “Sprint Planning”, “Sprint 2”
  • Real-time: Updates as you type (no submit button)
  • Empty search: Shows all boards

Sort

Recently Modified (default)
Newest modified boards first. Useful for resuming recent work.
Recently Modified Ascending
Oldest modified boards first. Useful for finding neglected boards.
Name Ascending (A-Z)
Alphabetical order. Useful for large board collections.
Name Descending (Z-A)
Reverse alphabetical order.

Combined Search + Sort

Search filters boards first, then sort is applied to filtered results. Example: Search “sprint” + Sort “Name Ascending” shows all boards with “sprint” in title, sorted A-Z.

Empty States

No Boards Exist

Shows when Boards collection is empty:
No boards yet
Create your first board to get started
[Create Board] [Create Demo]

No Search Results

Shows when search has no matches:
No boards found
Try a different search term

Welcome Banner

Configurable welcome message shown at top of Home screen:
<kanban:FlowKanbanHome
    ShowWelcomeMessage="True"
    WelcomeMessageTitle="Welcome to Kanban"
    WelcomeMessageSubtitle="Organize your work visually" />
To hide welcome banner:
<kanban:FlowKanbanHome ShowWelcomeMessage="False" />
Or leave title/subtitle empty:
<kanban:FlowKanbanHome
    ShowWelcomeMessage="True"
    WelcomeMessageTitle=""
    WelcomeMessageSubtitle="" />

Complete Example

XAML

<Page
    x:Class="MyApp.HomePage"
    xmlns:kanban="using:Flowery.Uno.Kanban.Controls">
    
    <Grid>
        <kanban:FlowKanbanHome
            Boards="{x:Bind ViewModel.Boards, Mode=OneWay}"
            OpenBoardCommand="{x:Bind ViewModel.OpenBoardCommand}"
            CreateBoardCommand="{x:Bind ViewModel.CreateBoardCommand}"
            CreateDemoBoardCommand="{x:Bind ViewModel.CreateDemoBoardCommand}"
            RenameBoardHomeCommand="{x:Bind ViewModel.RenameBoardCommand}"
            DeleteBoardCommand="{x:Bind ViewModel.DeleteBoardCommand}"
            DuplicateBoardCommand="{x:Bind ViewModel.DuplicateBoardCommand}"
            ExportBoardCommand="{x:Bind ViewModel.ExportBoardCommand}"
            SearchText="{x:Bind ViewModel.SearchText, Mode=TwoWay}"
            SortMode="{x:Bind ViewModel.SortMode, Mode=TwoWay}"
            ShowWelcomeMessage="True"
            WelcomeMessageTitle="Welcome to Project Manager"
            WelcomeMessageSubtitle="Organize your projects with Kanban boards" />
    </Grid>
</Page>

ViewModel

using Flowery.Uno.Kanban.Controls;
using System.Collections.ObjectModel;
using System.Windows.Input;

public class HomeViewModel : ViewModelBase
{
    private string _searchText = string.Empty;
    private FlowKanbanHomeSortMode _sortMode = FlowKanbanHomeSortMode.RecentlyModified;
    
    public ObservableCollection<FlowBoardMetadata> Boards { get; } = new();
    
    public string SearchText
    {
        get => _searchText;
        set => SetProperty(ref _searchText, value);
    }
    
    public FlowKanbanHomeSortMode SortMode
    {
        get => _sortMode;
        set => SetProperty(ref _sortMode, value);
    }
    
    public ICommand OpenBoardCommand { get; }
    public ICommand CreateBoardCommand { get; }
    public ICommand CreateDemoBoardCommand { get; }
    public ICommand RenameBoardCommand { get; }
    public ICommand DeleteBoardCommand { get; }
    public ICommand DuplicateBoardCommand { get; }
    public ICommand ExportBoardCommand { get; }
    
    public HomeViewModel()
    {
        OpenBoardCommand = new RelayCommand<FlowBoardMetadata>(OpenBoard);
        CreateBoardCommand = new RelayCommand(CreateBoard);
        CreateDemoBoardCommand = new RelayCommand(CreateDemoBoard);
        RenameBoardCommand = new RelayCommand<FlowBoardMetadata>(RenameBoard);
        DeleteBoardCommand = new RelayCommand<FlowBoardMetadata>(DeleteBoard);
        DuplicateBoardCommand = new RelayCommand<FlowBoardMetadata>(DuplicateBoard);
        ExportBoardCommand = new RelayCommand<FlowBoardMetadata>(ExportBoard);
        
        LoadBoards();
    }
    
    private void LoadBoards()
    {
        Boards.Clear();
        var manager = new FlowKanbanManager(kanbanControl);
        foreach (var board in manager.ListAvailableBoards())
        {
            Boards.Add(board);
        }
    }
    
    private void OpenBoard(FlowBoardMetadata board)
    {
        var manager = new FlowKanbanManager(kanbanControl);
        manager.LoadBoard(board.Id);
        // Navigate to board view
    }
    
    private void CreateBoard()
    {
        var newBoard = new FlowKanbanData { Title = "New Board" };
        var manager = new FlowKanbanManager(kanbanControl);
        manager.SetBoardData(newBoard);
        manager.SaveBoard();
        LoadBoards();
    }
    
    private void CreateDemoBoard()
    {
        var manager = new FlowKanbanManager(kanbanControl);
        manager.CreateDefaultBoard();
        // Add sample tasks
        var backlog = manager.FindColumnByTitle("Backlog");
        manager.AddTask(backlog, "Sample Task 1");
        manager.AddTask(backlog, "Sample Task 2");
        manager.SaveBoard();
        LoadBoards();
    }
    
    private async void RenameBoard(FlowBoardMetadata board)
    {
        var dialog = new ContentDialog
        {
            Title = "Rename Board",
            PrimaryButtonText = "Rename",
            CloseButtonText = "Cancel",
            Content = new TextBox { Text = board.Title }
        };
        
        if (await dialog.ShowAsync() == ContentDialogResult.Primary)
        {
            var newTitle = (dialog.Content as TextBox)?.Text;
            if (!string.IsNullOrWhiteSpace(newTitle))
            {
                var manager = new FlowKanbanManager(kanbanControl);
                manager.RenameBoard(board.Id, newTitle);
                LoadBoards();
            }
        }
    }
    
    private async void DeleteBoard(FlowBoardMetadata board)
    {
        var dialog = new ContentDialog
        {
            Title = "Delete Board",
            Content = $"Are you sure you want to delete '{board.Title}'?",
            PrimaryButtonText = "Delete",
            CloseButtonText = "Cancel"
        };
        
        if (await dialog.ShowAsync() == ContentDialogResult.Primary)
        {
            var manager = new FlowKanbanManager(kanbanControl);
            manager.DeleteBoard(board.Id);
            LoadBoards();
        }
    }
    
    private void DuplicateBoard(FlowBoardMetadata board)
    {
        var manager = new FlowKanbanManager(kanbanControl);
        manager.DuplicateBoard(board.Id, out var newId);
        LoadBoards();
    }
    
    private async void ExportBoard(FlowBoardMetadata board)
    {
        var manager = new FlowKanbanManager(kanbanControl);
        var json = manager.ExportBoardToJson(board.Id);
        
        // Save to file using file picker
        var picker = new FileSavePicker();
        picker.FileTypeChoices.Add("JSON", new[] { ".json" });
        picker.SuggestedFileName = $"{board.Title}.json";
        
        var file = await picker.PickSaveFileAsync();
        if (file != null)
        {
            await FileIO.WriteTextAsync(file, json);
        }
    }
}

Best Practices

Performance

  • Lazy load thumbnails: Don’t render full board previews in list view
  • Virtualization: Use ItemsRepeater with virtualization for 100+ boards
  • Debounce search: Already built-in, but consider additional debouncing for expensive filters

UX Guidelines

  • Show recent boards prominently: Default to “Recently Modified” sort
  • Confirm destructive actions: Always confirm board deletion
  • Provide feedback: Show toast/snackbar after board actions
  • Enable keyboard navigation: Support arrow keys and Enter to open boards

Accessibility

  • All board cards expose AutomationProperties.Name for screen readers
  • Menu buttons include accessible labels
  • Search box has proper label association
  • Sort ComboBox announces current selection
  • FlowKanban - Interactive Kanban board control
  • FlowKanbanManager - Programmatic API
  • FlowBoardMetadata - Lightweight board metadata model
  • FlowKanbanView - Enum for switching between Board/Home views

Build docs developers (and LLMs) love