Xamarin.Forms - UWP でファイルドロップ
- Xamarin.Forms - UWPでファイルをドロップしたい
- カスタム レンダラー 利用
環境
- Visual Studio 2019(16.10.0)
- Windows 10
- Windows Template Studio(ドロップ部分のコードで利用)
準備
UWPのドラッグドロップ関連の処理は、下記から拝借。
https://docs.microsoft.com/ja-jp/windows/uwp/design/windows-template-studio/
- 1:Windows Template Studio で、UWPプロジェクト作成
- 2:機能で Drag & Drop を追加。(それ以外は不要)
- 3:出来たプロジェクトから、D&D関連のファイルだけコピーして、 XamarinのUWPプロジェクトに追加。(プラットフォームのプロジェクト)
↑ のファイルをコピペ ※ ネームスペースを適当に修正
Xamarin-UWP プロジェクトでドロップ
1. プロジェクト作成
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ドロップ用のファイルを追加しとく
カスタムレンダラー作成
- 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;
4. 感想
- 以前やった時よりも、シンプルにかけた(気がする)👍
- View(レンダラー)を通してデータをやりとりするのにモヤッとする
- x:Name の名前(文字列)で判定してる箇所にモヤッとする