MdHTMLer ver 0.1

markdown を HTMLに変換するアプリ MdHTMLer の ver 0.1 が Microsoft Store で公開されました。

簡単なアプリなので審査も公開も 爆速で 気づいたら公開されてました。

(審査は1時間くらい? で通った気がする)

www.microsoft.com

機能

  • MarkdownをHTMLに変換します
  • フォルダ指定します。(フォルダ以下をコピーして変換します)
  • 簡単な Index と目次ページを追加できます(オプション)

f:id:kawaishi2:20210617100237p:plain

f:id:kawaishi2:20210617100233p:plain

現状

まだ とりあえず動いた版。

大量ファイルは処理できず、途中で落ちてしまいます。。。

以上

非同期で再帰(UWP)

非同期で再帰(UWP)

自分用:メモ:

非同期で再帰(UWP)の挙動が安定しない…。

この辺か???

devlights.hatenablog.com

stackoverflow.com


自分でも書いてはみたが...

var buf = new BlockingCollection<IStorageItem>();
var copyDirTask = Task.Run<Task>(async () =>
{
    await this.CopyFolderAsync(s, d, buf);
    buf.CompleteAdding();
});

List<AbstractStorage> result1 = new List<AbstractStorage>();
var outputTask = Task.Run(() =>
{
    foreach (var item in buf.GetConsumingEnumerable())
    {
        Debug.WriteLine(item.Path);
        result1.Add(new AbstractStorage() { Path = item.Path, Storage = item });
    }
});

await Task.WhenAll(copyDirTask, outputTask);


// 再帰
private async Task CopyFolderAsync(StorageFolder sourceContainer, StorageFolder destContainer, BlockingCollection<IStorageItem> buf)
{
    var subDirBufs = new List<BlockingCollection<IStorageItem>>();

    StorageFolder destDir = await destContainer.CreateFolderAsync(sourceContainer.Name, CreationCollisionOption.OpenIfExists);

    buf.Add(destDir);

    // ファイル
    var files = await sourceContainer.GetFilesAsync();
    foreach (var file in files)
    {
        var copied = await file.CopyAsync(destDir, file.Name, NameCollisionOption.ReplaceExisting);
        buf.Add(copied);
    }

    // 非同期で再帰
    var tasks = new List<Task>();
    var folders = await sourceContainer.GetFoldersAsync();
    foreach (var folder in folders)
    {
        var bufSubDir = new BlockingCollection<IStorageItem>();

        tasks.Add(Task.Run(async () =>
        {
            Debug.WriteLine(folder.Path);
            await CopyFolderAsync(folder, destDir, bufSubDir);
            bufSubDir.CompleteAdding();
        }));

        subDirBufs.Add(bufSubDir);
    }

    await Task.WhenAll(tasks).ContinueWith(task =>
    {
        foreach (var subDirBuf in subDirBufs)
        {
            foreach (var item in subDirBuf.GetConsumingEnumerable())
            {
                Debug.WriteLine(item.Path);
                buf.Add(item);
            }
        }
    });

    return;
}

Microsoft パートナー センターで「パッケージ ファミリ名が無効です」が出たとき

Microsoft パートナー センターで「パッケージ ファミリ名が無効です」のエラーが出た

エラー内容:

Microsoft パートナー センター> アプリ > パッケージ:

===

提出する前に、すべてのパッケージ検証エラーを修正する必要があります。

  • AppName.UWP_1.9.0.0_x86_x64_arm_bundle.msixupload46.9 MB
  • パッケージ ファミリ名が無効です: 0000000AAAAAAA.AppName_AAAAAAAAA (期待値: 0000000AAAAAAA.AppName_BBBBBBBBB)
  • パッケージの発行元の名前が無効です: CN=MyComputer(期待値: CN=AAAAAAA-AAAA-AAAA-AAAA-AAAA)

===


修正

UWPプロジェクト>マニフェスト>パッケージ化>公開者>証明書の選択>その他

で、エラーメッセージにあった 期待値っぽい方を選択し、再ビルド。

f:id:kawaishi2:20210614101654j:plain

ストアから選択

f:id:kawaishi2:20210614101745j:plain

別のが選択されているので:

f:id:kawaishi2:20210614101748j:plain

正しそうな方を再選択

f:id:kawaishi2:20210614101751j:plain

解消された。


参考

qiita.com

WindowsTemplateStudio 4.0.2

WindowsTemplateStudio 4.0.2

4.0.2 がリリースされてました

github.com


App (WinUI 3 in Desktop)が追加されてる!

f:id:kawaishi2:20210611110412p:plain


機能拡張から更新

f:id:kawaishi2:20210611110511p:plain


プロジェクトを作成

App(WinUI 3 in Desktop) が追加されてる

f:id:kawaishi2:20210611110639p:plain


1:種類

blank は 必要最低限のシンプル構成っぽい。

Navigation Pane の方だと、Windows Community Toolkitが入ってくる。(左側のメニューも動的に変わる)

今回は、Navigation Paneですすめる。

f:id:kawaishi2:20210611110653p:plain

f:id:kawaishi2:20210611110702p:plain

2:Design pattern

1つだけ

f:id:kawaishi2:20210611110716p:plain


3:ページ

今使えるのは6つ

f:id:kawaishi2:20210611110728p:plain


4:機能

インストーラーとかテーマとか

f:id:kawaishi2:20210611110743p:plain


メインのプロジェクトと、Coreのプロジェクト、それとパッケージプロジェクト

f:id:kawaishi2:20210611110827p:plain

依存関係:

Reunionがいらっしゃる!

f:id:kawaishi2:20210611110840p:plain

基本はWPF(だよね??)

f:id:kawaishi2:20210611110855p:plain

フレームワークは .net5 + Windows バージョン指定してるやつだ。

TFM(Target Framework Moniker)

f:id:kawaishi2:20210611110907p:plain


ViewModel で Di 使ってたり、

f:id:kawaishi2:20210611110919p:plain

ページ遷移の為の処理等、おなじみの奴らが既にいる。

(自分にはちょっと重い)

f:id:kawaishi2:20210611110933p:plain


警告バーが一杯 出てた。

> これはVisualStudioを再起動したら消えた。

f:id:kawaishi2:20210611110950p:plain


実行! エラー! 無視して続行!

(スクショ 撮り逃した)


起動した!

f:id:kawaishi2:20210611111012p:plain

f:id:kawaishi2:20210611111016p:plain

f:id:kawaishi2:20210611111019p:plain

f:id:kawaishi2:20210611111022p:plain

以上

ファイル優先のファイル一覧に並び替えたい

ファイル優先のファイル一覧に並び替えたい

環境


現状

f:id:kawaishi2:20210610174300p:plain

こんな感じに階層になっているファイル一覧をとってきた。

なぜか「ファイル名」でソートされてしまっている...。

// temp に一覧データが入っている

 Debug.WriteLine(string.Join("\r\n", temp));

C:\root\300\1.md
C:\root\300\2.md
C:\root\200\200_10\200_1.md
C:\root\200\200_20\200_1.md
C:\root\200\200_10\200_2.md
C:\root\200\200_20\200_2.md
C:\root\200\200_20\200_3.md
C:\root\200\200_20\200_4.md
C:\root\README.md
C:\root\releasenotes.md
C:\root\400\Sample.md
C:\root\100\top.md

要望

  • 取得済の一覧を、パス順で並び替えた一覧に並び替えたい。

  • ただし、同一階層内では「フォルダ(の中)」よりも「ファイル」を優先とする。

  • (取得する方はコード変更できない)


対応

下記で希望した形になった。

  • GroupBy で、親フォルダ順に並べる
  • SelectManyで、展開

もう少しスマートにならないものか。

var t3 = temp.GroupBy(x => System.IO.Path.GetDirectoryName(x))
              .OrderBy(f => f.Key)
              .SelectMany(x => x.OrderBy(f => f));

Debug.WriteLine(string.Join("\r\n", t3));

C:\root\README.md
C:\root\releasenotes.md
C:\root\100\top.md
C:\root\200\200_10\200_1.md
C:\root\200\200_10\200_2.md
C:\root\200\200_20\200_1.md
C:\root\200\200_20\200_2.md
C:\root\200\200_20\200_3.md
C:\root\200\200_20\200_4.md
C:\root\300\1.md
C:\root\300\2.md
C:\root\400\Sample.md

失敗1:

パスでソート

これだと、ファイル(README.md)が末尾に並んでしまう…。

var t1 = temp.OrderBy(x => x);

Debug.WriteLine(string.Join("\r\n", t1));

C:\root\100\top.md
C:\root\200\200_10\200_1.md
C:\root\200\200_10\200_2.md
C:\root\200\200_20\200_1.md
C:\root\200\200_20\200_2.md
C:\root\200\200_20\200_3.md
C:\root\200\200_20\200_4.md
C:\root\300\1.md
C:\root\300\2.md
C:\root\400\Sample.md
C:\root\README.md
C:\root\releasenotes.md

失敗2:

第一ソートキーに 階層数(の代わりに区切り文字数)

第二ソートキーに パス

駄目でした。(そりゃそうか・・・)

var t2 = temp.OrderBy(x => (x.Where(c => c == '\\')).Count()).ThenBy(f => f);

Debug.WriteLine(string.Join("\r\n", t2));

C:\root\README.md
C:\root\releasenotes.md
C:\root\100\top.md
C:\root\300\1.md
C:\root\300\2.md
C:\root\400\Sample.md
C:\root\200\200_10\200_1.md
C:\root\200\200_10\200_2.md
C:\root\200\200_20\200_1.md
C:\root\200\200_20\200_2.md
C:\root\200\200_20\200_3.md
C:\root\200\200_20\200_4.md

以上

Xamarin.Forms - UWP でファイルドロップ

Xamarin.Forms - UWP でファイルドロップ

  • Xamarin.Forms - UWPでファイルをドロップしたい
  • カスタム レンダラー 利用

環境

準備

UWPのドラッグドロップ関連の処理は、下記から拝借。

https://docs.microsoft.com/ja-jp/windows/uwp/design/windows-template-studio/

  • 1:Windows Template Studio で、UWPプロジェクト作成

f:id:kawaishi2:20210608153837p:plain

  • 2:機能で Drag & Drop を追加。(それ以外は不要)

f:id:kawaishi2:20210608153927p:plain

  • 3:出来たプロジェクトから、D&D関連のファイルだけコピーして、 XamarinのUWPプロジェクトに追加。(プラットフォームのプロジェクト)

f:id:kawaishi2:20210608153938p:plain

↑ のファイルをコピペ ※ ネームスペースを適当に修正


Xamarin-UWP プロジェクトでドロップ

1. プロジェクト作成

f:id:kawaishi2:20210608153950p:plain

f:id:kawaishi2:20210608154002p:plain


2. 共通プロジェクトのViewにFrame 作成

  • 今回はFrame にドロップ(したい)
  • Frameの子要素の子要素に Entry を配置(ドロップしたファイルパスを表示)
  • ViewModel とバインド。ViewModelの String に EntryのTextをバインド
  • FrameやEntry コントロールに x:Name で名前付けとく(後で使う)
共通プロジェクトのView

<Frame  x:Name="TopFrame">

    <xct:DockLayout>

        <Entry
            x:Name="EntryTop"
            Text="{Binding Path=SourcePath, Mode=TwoWay}"  >
        </Entry>

    </xct:DockLayout>
</Frame>

・・・略
共通プロジェクトのViewModel

public class MainViewModel : BaseViewModel
{
    private string sourcePath = string.Empty;
    public string SourcePath
    {
        get { return this.sourcePath; }
        set { SetProperty(ref sourcePath, value); }
    }

    private string targetPath = string.Empty;
    public string TargetPath
    {
        get { return this.targetPath; }
        set { SetProperty(ref targetPath, value); }
    }
・・・略

ここまでは特に特別な箇所はない。


3. UWP プロジェクト側にカスタムレンダラー

通常だと、共通プロジェクトでFrameをサブクラス化したカスタムコントロールを作成する・・・んだけど、今回は横着して省略。

下準備

UWPドロップ用のファイルを追加しとく

f:id:kawaishi2:20210608154029p:plain

カスタムレンダラー作成

  • FrameだからFrameRenderer
  • (横着して)標準のFrameを対象
    • Xamarin.Forms.Frame を直で指定。普通は MyFrameとか作る
カスタムレンダラー クラス:

[assembly: ExportRenderer(typeof(Xamarin.Forms.Frame), typeof(CustomFrameRenderer))]

namespace MyFormPrj.UWP.CustomViews
{
    public class CustomFrameRenderer : FrameRenderer

・・・略

カスタムレンダラー:OnElementChanged

  • OnElementChanged で色々やっているけど、この辺はイマイチわかってない。
  • OnLoaded とかあれば安心なんだけが…。
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
    base.OnElementChanged(e);

    // ドロップ可
    this.Control.AllowDrop = true;

    // ドロップ処理を作成
    var fileDropCmd = new ReactiveCommand<IReadOnlyList<IStorageItem>>().WithSubscribe(x => this.OnGetStorageItem(x));

    // D&D を登録
    DragDropService.SetConfiguration(this.Control, new DropConfiguration() { DropStorageItemsCommand = fileDropCmd });

    return;
}
  • ドラックドドロップ処理をFrameに登録する
  • XAMLで書くようなことをコードで書いているだけ

カスタムレンダラーからフォームへ

public void OnGetStorageItem(IReadOnlyList<IStorageItem> items)
{
    foreach (var item in items)
    {

        switch (this.Element.StyleId)
        {
            case "TopFrame":
                {
                    Xamarin.Forms.Entry entryElement = this.Element.Content.FindByName("EntryTop") as Xamarin.Forms.Entry;

                    // コントロールに直接格納
                    entryElement.Text = item.Path;
                    break;
                }
            case "BottomFrame":
                {
                    Xamarin.Forms.Entry entryElement = this.Element.Content.FindByName("EntryBottom") as Xamarin.Forms.Entry;

                    // (フォーム側で)バインドしているモデルに格納
                    var model1 = entryElement.BindingContext as MyFormPrj.ViewModels.MainViewModel;
                    model1.TargetPath = item.Path;

                    break;
                }

            default:
                break;
        }

        // for文に意味はない
        break;
    }
}
  • items はドロップされたファイル/フォルダ
  • Frame全部にドロップ処理を入れているので、どのフレームにドロップされたかわからない。
  • ので、x:Name に付けた名前で判別(イケてない)

共通プロジェクトに値を渡す

  • 渡し方1:entryElement.Text(←共通プロジェクトのEntryのテキスト) = item.Path(UWPでドロップされたファイルパス)

  • バインドしているモデルに(無理くり?)アクセスしてもいい(のか?)

  • 渡し方2:var model1 = entryElement.BindingContext as MyFormPrj.ViewModels.MainViewModel;

f:id:kawaishi2:20210608154050g:plain


4. 感想

  • 以前やった時よりも、シンプルにかけた(気がする)👍
  • View(レンダラー)を通してデータをやりとりするのにモヤッとする
  • x:Name の名前(文字列)で判定してる箇所にモヤッとする