~haowenl/vorg-windows

67420475e40bb4421594623d4c83b44433444f7c — Haowen Liu 1 year, 6 months ago 7583a3d
Implement filtering in gallery
M libvorg => libvorg +1 -1
@@ 1,1 1,1 @@
Subproject commit 053ddccf53ac9c64d6ba113da03ad4ecbac688fa
Subproject commit 2e44b594d503948cc7a23f9c46472ce2b095df8d

M vorg-windows/App.xaml.cpp => vorg-windows/App.xaml.cpp +5 -0
@@ 102,6 102,11 @@ void App::CancelImport()
    mRepo->cancelImport();
}

void App::SetFilter(const Vorg::VorgFilter &filter)
{
    mFilter = filter;
}

void App::PopulateGalleryData(
    Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> &resultData) const
{

M vorg-windows/App.xaml.h => vorg-windows/App.xaml.h +1 -0
@@ 23,6 23,7 @@ struct App : AppT<App>
                    const Windows::Foundation::Collections::IObservableVector<hstring> &tags);
    bool Skip();
    void CancelImport();
    void SetFilter(const Vorg::VorgFilter &filter);
    void PopulateGalleryData(
        Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> &resultData) const;
    void GetVideoDetail(const hstring &hash, vorg_windows::VideoDetailData &detailData,

M vorg-windows/BrowseGalleryPage.xaml => vorg-windows/BrowseGalleryPage.xaml +7 -2
@@ 44,10 44,15 @@
                x:Name="FilterButton"
                Grid.Column="1"
                HorizontalAlignment="Right"
                Click="FilterButton_Click"
                Content="&#xe71c;"
                FontFamily="Segoe Fluent Icons"
                IsTabStop="False" />
                IsTabStop="False">
                <Button.Flyout>
                    <Flyout Placement="Bottom">
                        <local:FilterEditor FilterApplied="FilterEditor_FilterApplied" />
                    </Flyout>
                </Button.Flyout>
            </Button>
        </Grid>

        <GridView

M vorg-windows/BrowseGalleryPage.xaml.cpp => vorg-windows/BrowseGalleryPage.xaml.cpp +9 -4
@@ 13,6 13,10 @@ namespace winrt::vorg_windows::implementation
BrowseGalleryPage::BrowseGalleryPage()
{
    InitializeComponent();

    // Initialize app ptr
    mApp.copy_from(get_self<vorg_windows::implementation::App>(
        Application::Current().as<default_interface<vorg_windows::implementation::App>>()));
}

Windows::Foundation::Collections::IObservableVector<Windows::Foundation::IInspectable> BrowseGalleryPage::GalleryData()


@@ 41,14 45,15 @@ void BrowseGalleryPage::ShowVideoDetail(const event_token &token)
void BrowseGalleryPage::Thumbnail_DoubleTapped(Windows::Foundation::IInspectable const &sender,
                                               Microsoft::UI::Xaml::Input::DoubleTappedRoutedEventArgs const &)
{
    Microsoft::UI::Xaml::Controls::Image image = sender.as<Microsoft::UI::Xaml::Controls::Image>();
    vorg_windows::HoverThumbnailDisplay image = sender.as<vorg_windows::HoverThumbnailDisplay>();
    Windows::Foundation::IInspectable dataContext = image.DataContext();
    vorg_windows::VideoGalleryData videoData = dataContext.as<vorg_windows::VideoGalleryData>();
    mShowVideoDetailEvent(*this, videoData.Hash());
}
} // namespace winrt::vorg_windows::implementation

void winrt::vorg_windows::implementation::BrowseGalleryPage::FilterButton_Click(
    winrt::Windows::Foundation::IInspectable const &sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const &e)
void BrowseGalleryPage::FilterEditor_FilterApplied(const vorg_windows::FilterEditor &,
                                                   const Windows::Foundation::IInspectable &)
{
    mApp->PopulateGalleryData(mGalleryData);
}
} // namespace winrt::vorg_windows::implementation

M vorg-windows/BrowseGalleryPage.xaml.h => vorg-windows/BrowseGalleryPage.xaml.h +6 -4
@@ 2,6 2,8 @@

#include "BrowseGalleryPage.g.h"

#include "App.xaml.h"

namespace winrt::vorg_windows::implementation
{
struct BrowseGalleryPage : BrowseGalleryPageT<BrowseGalleryPage>


@@ 20,7 22,8 @@ struct BrowseGalleryPage : BrowseGalleryPageT<BrowseGalleryPage>

    // Handlers
    void Thumbnail_DoubleTapped(Windows::Foundation::IInspectable const &sender,
                                           Microsoft::UI::Xaml::Input::DoubleTappedRoutedEventArgs const &);
                                Microsoft::UI::Xaml::Input::DoubleTappedRoutedEventArgs const &);
    void FilterEditor_FilterApplied(const vorg_windows::FilterEditor &, const Windows::Foundation::IInspectable &);

  private:
    // Properties


@@ 29,9 32,8 @@ struct BrowseGalleryPage : BrowseGalleryPageT<BrowseGalleryPage>
    // Events
    event<Windows::Foundation::TypedEventHandler<vorg_windows::BrowseGalleryPage, hstring>> mShowVideoDetailEvent;

  public:
    void FilterButton_Click(winrt::Windows::Foundation::IInspectable const &sender,
                            winrt::Microsoft::UI::Xaml::RoutedEventArgs const &e);
    // Members
    com_ptr<vorg_windows::implementation::App> mApp;
};
} // namespace winrt::vorg_windows::implementation


A vorg-windows/FilterEditor.idl => vorg-windows/FilterEditor.idl +14 -0
@@ 0,0 1,14 @@
namespace vorg_windows
{
[default_interface] runtimeclass FilterEditor : Microsoft.UI.Xaml.Controls.StackPanel
{
    FilterEditor();

    Windows.Foundation.Collections.IObservableVector<String> FilterDescriptions
    {
        get;
    };

    event Windows.Foundation.TypedEventHandler<FilterEditor, Object> FilterApplied;
}
} // namespace vorg_windows

A vorg-windows/FilterEditor.xaml => vorg-windows/FilterEditor.xaml +86 -0
@@ 0,0 1,86 @@
<StackPanel
    x:Class="vorg_windows.FilterEditor"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:vorg_windows"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Orientation="Vertical"
    mc:Ignorable="d">

    <UserControl x:Name="MainControl">
        <StackPanel>
            <Grid x:Name="InputGrid" ColumnSpacing="8">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="146" />
                    <ColumnDefinition Width="156" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>
                <DropDownButton
                    x:Name="ModeDropDown"
                    Grid.Column="0"
                    HorizontalAlignment="Stretch"
                    Content="Title (contains)">
                    <DropDownButton.Flyout>
                        <MenuFlyout Placement="Bottom">
                            <MenuFlyoutItem
                                x:Name="TitleDropDownButton"
                                Click="TitleDropDownButton_Click"
                                Text="Title (contains)" />
                            <MenuFlyoutItem
                                x:Name="StudioDropDownButton"
                                Click="StudioDropDownButton_Click"
                                Text="Studio (is)" />
                            <MenuFlyoutItem
                                x:Name="ActorDropDownButton"
                                Click="ActorDropDownButton_Click"
                                Text="Actor (includes)" />
                            <MenuFlyoutItem
                                x:Name="TagDropDownButton"
                                Click="TagDropDownButton_Click"
                                Text="Tag (includes)" />
                        </MenuFlyout>
                    </DropDownButton.Flyout>
                </DropDownButton>
                <AutoSuggestBox
                    x:Name="FilterInput"
                    Grid.Column="1"
                    KeyUp="FilterInput_KeyUp"
                    TextChanged="FilterInput_TextChanged" />
                <Button
                    x:Name="SubmitButton"
                    Grid.Column="2"
                    VerticalAlignment="Center"
                    Click="SubmitButton_Click"
                    Content="&#xE710;"
                    FontFamily="Segoe Fluent Icons"
                    IsTabStop="False" />
            </Grid>

            <local:ListBoxWithDelete
                x:Name="FilterList"
                DeleteItem="FilterList_DeleteItem"
                ItemsSource="{x:Bind FilterDescriptions, Mode=OneWay}" />

            <Button
                x:Name="ApplyButton"
                Margin="0,12,0,0"
                HorizontalAlignment="Right"
                Click="ApplyButton_Click"
                Style="{StaticResource AccentButtonStyle}">
                Apply Filter
            </Button>

            <VisualStateManager.VisualStateGroups>
                <VisualStateGroup>
                    <VisualState x:Name="ListEmpty" />
                    <VisualState x:Name="ListNotEmpty">
                        <VisualState.Setters>
                            <Setter Target="InputGrid.Margin" Value="0,0,0,16" />
                        </VisualState.Setters>
                    </VisualState>
                </VisualStateGroup>
            </VisualStateManager.VisualStateGroups>
        </StackPanel>
    </UserControl>
</StackPanel>

A vorg-windows/FilterEditor.xaml.cpp => vorg-windows/FilterEditor.xaml.cpp +200 -0
@@ 0,0 1,200 @@
#include "pch.h"

#include "FilterEditor.xaml.h"
#if __has_include("FilterEditor.g.cpp")
#include "FilterEditor.g.cpp"
#endif

using namespace winrt;
using namespace Microsoft::UI::Xaml;

namespace winrt::vorg_windows::implementation
{
FilterEditor::FilterEditor() : mFilterDescriptions(single_threaded_observable_vector<hstring>()), mFilterState(Title)
{
    InitializeComponent();

    // Initialize app ptr
    mApp.copy_from(get_self<vorg_windows::implementation::App>(
        Application::Current().as<default_interface<vorg_windows::implementation::App>>()));

    // Subscribe to vector changes for delete
    mVectorChangedToken = mFilterDescriptions.VectorChanged({this, &FilterEditor::FilterDescriptions_VectorChanged});
}

FilterEditor::~FilterEditor()
{
    mFilterDescriptions.VectorChanged(mVectorChangedToken);
}

Windows::Foundation::Collections::IObservableVector<hstring> FilterEditor::FilterDescriptions() const
{
    return mFilterDescriptions;
}

event_token FilterEditor::FilterApplied(
    const Windows::Foundation::TypedEventHandler<vorg_windows::FilterEditor, Windows::Foundation::IInspectable>
        &handler)
{
    return mFilterAppliedEvent.add(handler);
}

void FilterEditor::FilterApplied(const event_token &token)
{
    mFilterAppliedEvent.remove(token);
}

void FilterEditor::FilterDescriptions_VectorChanged(
    const Windows::Foundation::Collections::IObservableVector<hstring> &sender,
    const Windows::Foundation::Collections::IVectorChangedEventArgs &)
{
    if (sender.Size() == 0)
    {
        VisualStateManager::GoToState(MainControl(), L"ListEmpty", false);
    }
    else
    {
        VisualStateManager::GoToState(MainControl(), L"ListNotEmpty", false);
    }
}

void FilterEditor::SubmitButton_Click(const Windows::Foundation::IInspectable &,
                                      const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    AddNewConstraint();
}

void FilterEditor::FilterInput_KeyUp(const Windows::Foundation::IInspectable &,
                                     const Microsoft::UI::Xaml::Input::KeyRoutedEventArgs &e)
{
    if (e.Key() == Windows::System::VirtualKey::Enter)
    {
        AddNewConstraint();
    }
}

void FilterEditor::TitleDropDownButton_Click(const Windows::Foundation::IInspectable &,
                                             const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    mFilterState = Title;
    ModeDropDown().Content(box_value(L"Title (contains)"));
    FilterInput().ItemsSource(single_threaded_observable_vector<hstring>());
}

void FilterEditor::StudioDropDownButton_Click(const Windows::Foundation::IInspectable &,
                                              const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    mFilterState = Studio;
    ModeDropDown().Content(box_value(L"Studio (is)"));
    FilterInput().ItemsSource(mApp->CompleteStudios(FilterInput().Text()));
}

void FilterEditor::ActorDropDownButton_Click(const Windows::Foundation::IInspectable &,
                                             const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    mFilterState = Actor;
    ModeDropDown().Content(box_value(L"Actor (includes)"));
    FilterInput().ItemsSource(mApp->CompleteActors(FilterInput().Text()));
}

void FilterEditor::TagDropDownButton_Click(const Windows::Foundation::IInspectable &,
                                           const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    mFilterState = Tag;
    ModeDropDown().Content(box_value(L"Tag (includes)"));
    FilterInput().ItemsSource(mApp->CompleteTags(FilterInput().Text()));
}

void FilterEditor::FilterList_DeleteItem(vorg_windows::ListBoxWithDelete const &, hstring const &args)
{
    std::wstring_view arg_view = args;
    if (arg_view.starts_with(L"Title"))
    {
        mFilter.setTitle("");
    }
    if (arg_view.starts_with(L"Studio"))
    {
        mFilter.setStudio("");
    }
    if (arg_view.starts_with(L"Actor"))
    {
        mFilter.removeActor(to_string(arg_view.substr(15)));
    }
    if (arg_view.starts_with(L"Tag"))
    {
        mFilter.removeTag(to_string(arg_view.substr(13)));
    }
    ConvertFilterToDescriptions();
}

void FilterEditor::ApplyButton_Click(const Windows::Foundation::IInspectable &,
                                     const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    AddNewConstraint();
    mApp->SetFilter(mFilter);
    mFilterAppliedEvent(*this, nullptr);
}

void FilterEditor::FilterInput_TextChanged(const Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
                                           const Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &)
{
    switch (mFilterState)
    {
    case Title:
        sender.ItemsSource(single_threaded_observable_vector<hstring>());
        break;
    case Studio:
        sender.ItemsSource(mApp->CompleteStudios(sender.Text()));
        break;
    case Actor:
        sender.ItemsSource(mApp->CompleteActors(sender.Text()));
        break;
    case Tag:
        sender.ItemsSource(mApp->CompleteTags(sender.Text()));
        break;
    }
}

void FilterEditor::AddNewConstraint()
{
    hstring filterDescription;
    switch (mFilterState)
    {
    case Title:
        mFilter.setTitle(to_string(FilterInput().Text()));
        break;
    case Studio:
        mFilter.setStudio(to_string(FilterInput().Text()));
        break;
    case Actor:
        mFilter.appendActor(to_string(FilterInput().Text()));
        break;
    case Tag:
        mFilter.appendTag(to_string(FilterInput().Text()));
        break;
    }
    FilterInput().Text(L"");
    ConvertFilterToDescriptions();
}

void FilterEditor::ConvertFilterToDescriptions()
{
    mFilterDescriptions.Clear();
    if (!mFilter.getTitle().empty())
    {
        mFilterDescriptions.Append(L"Title contains " + to_hstring(mFilter.getTitle()));
    }
    if (!mFilter.getStudio().empty())
    {
        mFilterDescriptions.Append(L"Studio is " + to_hstring(mFilter.getStudio()));
    }
    for (auto &actor : mFilter.getRequiredActors())
    {
        mFilterDescriptions.Append(L"Actors contain " + to_hstring(actor));
    }
    for (auto &tag : mFilter.getRequiredTags())
    {
        mFilterDescriptions.Append(L"Tags contain " + to_hstring(tag));
    }
}
} // namespace winrt::vorg_windows::implementation

A vorg-windows/FilterEditor.xaml.h => vorg-windows/FilterEditor.xaml.h +80 -0
@@ 0,0 1,80 @@
#pragma once

#include "FilterEditor.g.h"

#include "App.xaml.h"
#include <libvorg/libvorg.h>

namespace winrt::vorg_windows::implementation
{
struct FilterEditor : FilterEditorT<FilterEditor>
{
    FilterEditor();
    ~FilterEditor();

    // Properties
    Windows::Foundation::Collections::IObservableVector<hstring> FilterDescriptions() const;

    // Events
    event_token FilterApplied(const Windows::Foundation::TypedEventHandler<vorg_windows::FilterEditor,
                                                                           Windows::Foundation::IInspectable> &handler);
    void FilterApplied(const event_token &token);

    // Handlers
    void FilterDescriptions_VectorChanged(const Windows::Foundation::Collections::IObservableVector<hstring> &sender,
                                          const Windows::Foundation::Collections::IVectorChangedEventArgs &);

    void SubmitButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);
    void FilterInput_KeyUp(const Windows::Foundation::IInspectable &,
                           const Microsoft::UI::Xaml::Input::KeyRoutedEventArgs &e);

    void TitleDropDownButton_Click(const Windows::Foundation::IInspectable &,
                                   const Microsoft::UI::Xaml::RoutedEventArgs &);
    void StudioDropDownButton_Click(const Windows::Foundation::IInspectable &,
                                    const Microsoft::UI::Xaml::RoutedEventArgs &);
    void ActorDropDownButton_Click(const Windows::Foundation::IInspectable &,
                                   const Microsoft::UI::Xaml::RoutedEventArgs &);
    void TagDropDownButton_Click(const Windows::Foundation::IInspectable &,
                                 const Microsoft::UI::Xaml::RoutedEventArgs &);

    void FilterList_DeleteItem(const vorg_windows::ListBoxWithDelete &, const hstring &args);

    void ApplyButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);

    void FilterInput_TextChanged(const Microsoft::UI::Xaml::Controls::AutoSuggestBox &sender,
                                 const Microsoft::UI::Xaml::Controls::AutoSuggestBoxTextChangedEventArgs &);

  private:
    // Properties
    Windows::Foundation::Collections::IObservableVector<hstring> mFilterDescriptions;

    // Events
    event<Windows::Foundation::TypedEventHandler<vorg_windows::FilterEditor, Windows::Foundation::IInspectable>>
        mFilterAppliedEvent;

    // Members
    enum FilterState
    {
        Title,
        Studio,
        Actor,
        Tag
    };

    Vorg::VorgFilter mFilter;
    event_token mVectorChangedToken;
    FilterState mFilterState;
    com_ptr<vorg_windows::implementation::App> mApp;

    // Helper functions
    void AddNewConstraint();
    void ConvertFilterToDescriptions();
};
} // namespace winrt::vorg_windows::implementation

namespace winrt::vorg_windows::factory_implementation
{
struct FilterEditor : FilterEditorT<FilterEditor, implementation::FilterEditor>
{
};
} // namespace winrt::vorg_windows::factory_implementation

A vorg-windows/ListBoxWithDelete.idl => vorg-windows/ListBoxWithDelete.idl +11 -0
@@ 0,0 1,11 @@
namespace vorg_windows
{
[default_interface] runtimeclass ListBoxWithDelete : Microsoft.UI.Xaml.Controls.Grid
{
    ListBoxWithDelete();

    Windows.Foundation.Collections.IObservableVector<String> ItemsSource;

    event Windows.Foundation.TypedEventHandler<ListBoxWithDelete, String> DeleteItem;
}
} // namespace vorg_windows

A vorg-windows/ListBoxWithDelete.xaml => vorg-windows/ListBoxWithDelete.xaml +53 -0
@@ 0,0 1,53 @@
<Grid
    x:Class="vorg_windows.ListBoxWithDelete"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:vorg_windows"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <ListView
        Grid.Row="2"
        Grid.Column="0"
        ItemsSource="{x:Bind Path=ItemsSource, Mode=OneWay}"
        SelectionMode="Single">
        <ItemsControl.ItemTemplate>
            <DataTemplate x:DataType="x:String">
                <UserControl PointerEntered="UserControl_PointerEntered" PointerExited="UserControl_PointerExited">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <TextBlock
                            Grid.Column="0"
                            VerticalAlignment="Center"
                            Text="{x:Bind}" />
                        <Button
                            x:Name="DeleteButton"
                            Grid.Column="1"
                            Click="DeleteButton_Click"
                            Content="&#xE711;"
                            FontFamily="Segoe Fluent Icons"
                            Visibility="Collapsed" />
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup>
                                <VisualState x:Name="HoverButtonHidden" />
                                <VisualState x:Name="HoverButtonShown">
                                    <VisualState.Setters>
                                        <Setter Target="DeleteButton.Visibility" Value="Visible" />
                                    </VisualState.Setters>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                    </Grid>
                </UserControl>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemContainerStyle>
            <Style BasedOn="{StaticResource DefaultListViewItemStyle}" TargetType="ListViewItem">
                <Setter Property="IsTabStop" Value="False" />
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ListView>
</Grid>

A vorg-windows/ListBoxWithDelete.xaml.cpp => vorg-windows/ListBoxWithDelete.xaml.cpp +61 -0
@@ 0,0 1,61 @@
#include "pch.h"

#include "ListBoxWithDelete.xaml.h"
#if __has_include("ListBoxWithDelete.g.cpp")
#include "ListBoxWithDelete.g.cpp"
#endif

using namespace winrt;
using namespace Microsoft::UI::Xaml;

namespace winrt::vorg_windows::implementation
{
ListBoxWithDelete::ListBoxWithDelete()
{
    InitializeComponent();
}

Windows::Foundation::Collections::IObservableVector<hstring> ListBoxWithDelete::ItemsSource() const
{
    return mItemsSource;
}

void ListBoxWithDelete::ItemsSource(const Windows::Foundation::Collections::IObservableVector<hstring> &itemsSource)
{
    mItemsSource = itemsSource;
}

event_token ListBoxWithDelete::DeleteItem(
    const Windows::Foundation::TypedEventHandler<vorg_windows::ListBoxWithDelete, hstring> &handler)
{
    return mDeleteItemEvent.add(handler);
}

void ListBoxWithDelete::DeleteItem(const event_token &token)
{
    mDeleteItemEvent.remove(token);
}

void ListBoxWithDelete::UserControl_PointerEntered(const Windows::Foundation::IInspectable &sender,
                                                   const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::UserControl userControl = sender.as<Microsoft::UI::Xaml::Controls::UserControl>();
    VisualStateManager::GoToState(userControl, L"HoverButtonShown", false);
}

void ListBoxWithDelete::UserControl_PointerExited(const Windows::Foundation::IInspectable &sender,
                                                  const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::UserControl userControl = sender.as<Microsoft::UI::Xaml::Controls::UserControl>();
    VisualStateManager::GoToState(userControl, L"HoverButtonHidden", false);
}

void ListBoxWithDelete::DeleteButton_Click(const Windows::Foundation::IInspectable &sender,
                                           const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    Microsoft::UI::Xaml::Controls::Button button = sender.as<Microsoft::UI::Xaml::Controls::Button>();
    Windows::Foundation::IInspectable dataContext = button.DataContext();
    hstring itemName = dataContext.as<hstring>();
    mDeleteItemEvent(*this, itemName);
}
} // namespace winrt::vorg_windows::implementation

A vorg-windows/ListBoxWithDelete.xaml.h => vorg-windows/ListBoxWithDelete.xaml.h +46 -0
@@ 0,0 1,46 @@
// Copyright (c) Microsoft Corporation and Contributors.
// Licensed under the MIT License.

#pragma once

#include "ListBoxWithDelete.g.h"

namespace winrt::vorg_windows::implementation
{
struct ListBoxWithDelete : ListBoxWithDeleteT<ListBoxWithDelete>
{
    ListBoxWithDelete();

    // Properties
    Windows::Foundation::Collections::IObservableVector<hstring> ItemsSource() const;
    void ItemsSource(const Windows::Foundation::Collections::IObservableVector<hstring> &itemsSource);

    // Events
    event_token DeleteItem(
        const Windows::Foundation::TypedEventHandler<vorg_windows::ListBoxWithDelete, hstring> &handler);
    void DeleteItem(const event_token &token);

    // Handlers
    void UserControl_PointerEntered(const Windows::Foundation::IInspectable &sender,
                                    const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &);
    void UserControl_PointerExited(const Windows::Foundation::IInspectable &sender,
                                   const Microsoft::UI::Xaml::Input::PointerRoutedEventArgs &);

    void DeleteButton_Click(const Windows::Foundation::IInspectable &sender,
                            const Microsoft::UI::Xaml::RoutedEventArgs &);

  private:
    // Properties
    Windows::Foundation::Collections::IObservableVector<hstring> mItemsSource;

    // Events
    event<Windows::Foundation::TypedEventHandler<vorg_windows::ListBoxWithDelete, hstring>> mDeleteItemEvent;
};
} // namespace winrt::vorg_windows::implementation

namespace winrt::vorg_windows::factory_implementation
{
struct ListBoxWithDelete : ListBoxWithDeleteT<ListBoxWithDelete, implementation::ListBoxWithDelete>
{
};
} // namespace winrt::vorg_windows::factory_implementation

M vorg-windows/ManagePage.xaml.cpp => vorg-windows/ManagePage.xaml.cpp +2 -2
@@ 36,8 36,8 @@ void ManagePage::ValidateButton_Click(const Windows::Foundation::IInspectable &,
    }
}

void ManagePage::ThumbnailButton_Click(const Windows::Foundation::IInspectable &sender,
                                       const Microsoft::UI::Xaml::RoutedEventArgs &e)
void ManagePage::ThumbnailButton_Click(const Windows::Foundation::IInspectable &,
                                       const Microsoft::UI::Xaml::RoutedEventArgs &)
{
    mApp->RegenerateAllThumbnails();
}

M vorg-windows/ManagePage.xaml.h => vorg-windows/ManagePage.xaml.h +3 -5
@@ 13,11 13,9 @@ struct ManagePage : ManagePageT<ManagePage>
{
    ManagePage();

    void ValidateButton_Click(const Windows::Foundation::IInspectable &sender,
                              const Microsoft::UI::Xaml::RoutedEventArgs &args);

    void ThumbnailButton_Click(const Windows::Foundation::IInspectable &sender,
                               const Microsoft::UI::Xaml::RoutedEventArgs &e);
    // Handlers
    void ValidateButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);
    void ThumbnailButton_Click(const Windows::Foundation::IInspectable &, const Microsoft::UI::Xaml::RoutedEventArgs &);

  private:
    // App ptr

M vorg-windows/vorg-windows.vcxproj => vorg-windows/vorg-windows.vcxproj +30 -0
@@ 139,10 139,18 @@
      <DependentUpon>BrowsePage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClInclude>
    <ClInclude Include="FilterEditor.xaml.h">
      <DependentUpon>FilterEditor.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClInclude>
    <ClInclude Include="HoverThumbnailDisplay.xaml.h">
      <DependentUpon>HoverThumbnailDisplay.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClInclude>
    <ClInclude Include="ListBoxWithDelete.xaml.h">
      <DependentUpon>ListBoxWithDelete.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClInclude>
    <ClInclude Include="VideoGalleryData.h" />
    <ClInclude Include="ErrorContentDialog.xaml.h">
      <DependentUpon>ErrorContentDialog.xaml</DependentUpon>


@@ 196,6 204,9 @@
    <Page Include="ErrorContentDialog.xaml">
      <SubType>Designer</SubType>
    </Page>
    <Page Include="FilterEditor.xaml">
      <SubType>Designer</SubType>
    </Page>
    <Page Include="HoverThumbnailDisplay.xaml">
      <SubType>Designer</SubType>
    </Page>


@@ 211,6 222,9 @@
    <Page Include="LandingPage.xaml">
      <SubType>Designer</SubType>
    </Page>
    <Page Include="ListBoxWithDelete.xaml">
      <SubType>Designer</SubType>
    </Page>
    <Page Include="MainWindow.xaml" />
    <Page Include="ManagePage.xaml">
      <SubType>Designer</SubType>


@@ 232,10 246,18 @@
      <DependentUpon>BrowsePage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClCompile>
    <ClCompile Include="FilterEditor.xaml.cpp">
      <DependentUpon>FilterEditor.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClCompile>
    <ClCompile Include="HoverThumbnailDisplay.xaml.cpp">
      <DependentUpon>HoverThumbnailDisplay.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClCompile>
    <ClCompile Include="ListBoxWithDelete.xaml.cpp">
      <DependentUpon>ListBoxWithDelete.xaml</DependentUpon>
      <SubType>Code</SubType>
    </ClCompile>
    <ClCompile Include="VideoGalleryData.cpp" />
    <ClCompile Include="ErrorContentDialog.xaml.cpp">
      <DependentUpon>ErrorContentDialog.xaml</DependentUpon>


@@ 295,10 317,18 @@
      <DependentUpon>BrowsePage.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Midl>
    <Midl Include="FilterEditor.idl">
      <DependentUpon>FilterEditor.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Midl>
    <Midl Include="HoverThumbnailDisplay.idl">
      <DependentUpon>HoverThumbnailDisplay.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Midl>
    <Midl Include="ListBoxWithDelete.idl">
      <DependentUpon>ListBoxWithDelete.xaml</DependentUpon>
      <SubType>Code</SubType>
    </Midl>
    <Midl Include="VideoGalleryData.idl" />
    <Midl Include="ErrorContentDialog.idl">
      <DependentUpon>ErrorContentDialog.xaml</DependentUpon>

M vorg-windows/vorg-windows.vcxproj.filters => vorg-windows/vorg-windows.vcxproj.filters +2 -0
@@ 28,6 28,8 @@
    </Page>
    <Page Include="VideoDataEditor.xaml" />
    <Page Include="HoverThumbnailDisplay.xaml" />
    <Page Include="FilterEditor.xaml" />
    <Page Include="ListBoxWithDelete.xaml" />
  </ItemGroup>
  <ItemGroup>
    <Midl Include="App.idl" />