WPFメモ　Epoxy

# ■概要

Epoxyを触ってみた。

# ■環境

* Windows 10
* Visual Studio Community 2019 Version 16.9.2
* .NET 5.0
* Epoxy.Wpf 0.15.0

# ■プロジェクト構成

![ep01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142879/1bfbd277-a0ab-1160-38b5-d09c4e9b16d7.png)

# ■ソース

## ◇EpoxySample1.csproj

```xml:EpoxySample1.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWPF>true</UseWPF>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Epoxy.Wpf" Version="0.15.0" />
  </ItemGroup>

</Project>
```

## ◇MainWindow.xaml

```xml:MainWindow.xaml
<Window
    x:Class="EpoxySample1.Views.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:conv="clr-namespace:EpoxySample1.Converters"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:ep="https://github.com/kekyo/Epoxy"
    xmlns:local="clr-namespace:EpoxySample1.Views"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:vm="clr-namespace:EpoxySample1.ViewModels"
    Title="MainWindow"
    Width="500"
    Height="300"
    ResizeMode="CanResizeWithGrip"
    mc:Ignorable="d">
    <Window.Resources>
        <conv:InverseBooleanConverter x:Key="InverseBool" />
    </Window.Resources>

    <Window.DataContext>
        <vm:MainWindowViewModel />
    </Window.DataContext>

    <ep:EventBinder.Events>
        <ep:Event Name="Loaded" Command="{Binding WindowLoaded}" />
    </ep:EventBinder.Events>

    <Grid Background="WhiteSmoke">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>

        <StackPanel IsEnabled="{Binding IsBusy, Converter={StaticResource InverseBool}}">
            <TextBlock Margin="8,0" Text="パス" />
            <!--  検索するパス  -->
            <TextBox Margin="8,0" Text="{Binding TargetPath, UpdateSourceTrigger=PropertyChanged}" />
            <!--  検索ボタン  -->
            <Button
                Width="200"
                Margin="8"
                Padding="0,8"
                Command="{Binding SearchCommand}"
                Content="検索" />
        </StackPanel>

        <!--  指定されたパス直下のフォルダを一覧表示  -->
        <ListBox
            Grid.Row="1"
            Margin="8,8,8,16"
            ItemsSource="{Binding PathList}" />
    </Grid>
</Window>
```

## ◇MainWindowViewModel.cs

```csharp:MainWindowViewModel.cs
using Epoxy;
using Epoxy.Synchronized;
using System.Collections.ObjectModel;
using System.Windows;

namespace EpoxySample1.ViewModels
{
    public sealed class MainWindowViewModel : ViewModel
    {
        /// <summary>
        /// 検索対象パス
        /// </summary>
        public string? TargetPath
        {
            get => GetValue();
            set
            { 
                SetValue(value);
                // 検索コマンドの実行可否 更新
                SearchCommand?.ChangeCanExecute();
            }
        }

        /// <summary>
        /// 処理中フラグ
        /// </summary>
        public bool IsBusy
        {
            get => GetValue();
            private set => SetValue(value);
        }

        /// <summary>
        /// 検索結果パス一覧
        /// </summary>
        public ObservableCollection<string>? PathList
        {
            get => GetValue<ObservableCollection<string>?>();
            private set => SetValue(value);
        }

        /// <summary>
        /// ウィンドウ起動時の処理
        /// </summary>
        public Command? WindowLoaded
        {
            get => GetValue();
            private set => SetValue(value);
        }

        /// <summary>
        /// 検索コマンド
        /// </summary>
        public Command? SearchCommand
        {
            get => GetValue();
            private set => SetValue(value);
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public MainWindowViewModel()
        {
            CreateCommands();
        }

        /// <summary>
        /// コマンド初期化
        /// </summary>
        private void CreateCommands()
        {
            // ウィンドウ起動
            WindowLoaded = Command.Factory.CreateSync<RoutedEventArgs>(e =>
            {
                IsBusy = false;
                TargetPath = @"C:\ProgramData";
                PathList = new ObservableCollection<string>();
            });

            // 検索
            SearchCommand = CommandFactory.Create(
                executeAsync: async () =>
                {
                    IsBusy = true;
                    PathList?.Clear();

                    var dir = new Models.Directory(TargetPath ?? "");
                    await foreach (var item in dir.GetItemsAsync())
                    {
                        PathList?.Add(item);
                    }

                    IsBusy = false;
                },
                canExecute: () =>
                {
                    // パスが空のときは無効
                    return !string.IsNullOrWhiteSpace(TargetPath);
                }
                );
        }
    }
}
```

## ◇Directory.cs

```csharp:Directory.cs
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace EpoxySample1.Models
{
    public class Directory
    {
        private readonly string _basePath;

        public Directory(string basePath)
        {
            _basePath = basePath;
        }

        public async IAsyncEnumerable<string> GetItemsAsync()
        {
            var dirs = System.IO.Directory.EnumerateDirectories(
                _basePath, "*", SearchOption.TopDirectoryOnly);
            foreach (var dir in dirs)
            {
                // すぐ終わらないよう少し待つ
                await Task.Delay(100);
                // 1つ返す
                yield return dir;
            }
        }
    }

}
```

## ◇InverseBooleanConverter.cs

```csharp:InverseBooleanConverter.cs
using Epoxy;

namespace EpoxySample1.Converters
{
    /// <summary>
    /// bool反転
    /// </summary>
    public class InverseBooleanConverter : ValueConverter<bool, bool>
    {
        public override bool TryConvert(bool from, out bool result)
        {
            result = !from;
            // 処理成功したらtrue
            return true;
        }
    }
}
```

# ■参考

* [Epoxy/README.ja.md at main · kekyo/Epoxy · GitHub](https://github.com/kekyo/Epoxy/blob/main/README.ja.md)