ディーバ Blog

大阪発 C#の会社、株式会社ディーバの Blog です。

Xamarin.iOS SfDataGrid 改行でセルの編集を確定する

Syncfusion SfDataGrid のセル編集をキーボードの改行(Enter/Return キー)で編集を確定する方法です。

※ この記事の投稿時点の SfDataGrid のバージョンは v15.2.0.46 です。

f:id:jz5_diva:20170721205901p:plain

Editing 機能で Excel のセルのように編集できます。改行をハンドリングするには、CurrentCellBeginEdit イベントで処理します。

// _dataGrid: SfDataGrid
_dataGrid.CurrentCellBeginEdit += DataGrid_CurrentCellBeginEdit;
private async void DataGrid_CurrentCellBeginEdit(object sender, GridCurrentCellBeginEditEventArgs args)
{
    await Task.Delay(100);

    if (!(args.Column is GridTextColumn)) return;

    var row = _dataGrid.GetRowGenerator().Items.FirstOrDefault(x => x.RowIndex == args.RowColumnIndex.RowIndex);
    var column = (row.GetType().GetRuntimeProperties().FirstOrDefault(x => x.Name == "VisibleColumns").GetValue(row) as List<DataColumnBase>).FirstOrDefault(x => x.ColumnIndex == args.RowColumnIndex.ColumnIndex) as IElement;

    if (column.Element.Subviews.Length > 0)
    {
        if (column.Element.Subviews[0] is UITextField)
        {
            ((UITextField)column.Element.Subviews[0]).ShouldReturn = textField =>
            {
                _dataGrid.EndEdit(); // 編集終了
                return true;
            };
        }
    }
}

5/23 MvvmCross 5.0 がリリース

クロスプラットフォーム MVVM フレームワーク「MvvmCross」がメジャーアップデートして MvvmCross 5.0 がリリースされています。MvvmCross 5.0 は、Xamarin.iOS, Xamarin.Android, Xamarin.Mac, Xamarin.Forms, Universal Windows Platform (UWP), Windows Presentation Framework (WPF) をターゲットとしています(Windows Phone 8.x, Windows 8.x が削除されました)。

f:id:jz5_diva:20170523023535p:plain

少人数で開発されていることもあり 4.x 系のアップデートはもうないと思われます。これから開発する場合 5.x 系を選ばない理由はないでしょう。当ブログも含めて 4.x 系以下の情報は古く 5.0 ではまったく異なる場合があるので最新の情報を確認しましょう。

所感としては、4.x 系と大きく異なっていますが(ようやく便利になった感じ)、そのままアップデートしてもある程度動くと思います。実際 iOS/Android のプロジェクトを先日から 5.0 beta にアップデートする作業をしていました。Navigation(画面遷移と画面間の値の受け渡し)などの作法は全く異なりますが、4.x 系の作法でも動くので少しずつ変更しています。

MvvmCross 5.0

詳しくは、MvvmCross 5.0 release! を参照してください。

以下、気になったところだけピックアップ。

Navigation

もう ShowViewModel は使いません(フレームワーク内部では使っています)。新しい NavigationService を使って、画面遷移します(ViewModel 間でやり取りします)。特に次の2点は大きいですね。

  • ViewModel へ渡すパラメーターが typesafe になりました。
  • ViewModel から戻り値も受け取れるようになりました。

詳しくは、Navigation を参照。

Lifecycle

ViewModel で、View のライフサイクル イベント起点で簡単に処理が記述できるようになっています。ViewModel に次のメソッドが追加されています。

void Appearing();
void Appeared();
void Disappearing();
void Disappeared();

iOS presenter

iOS 用の presenter が改善されています。これまで Modal 表示など View をどのように表示するかを presenter クラスにごりごり記述していましたが、デフォルトである程度使える presenter が実装されています。

例えば、View クラスに MvxModalPresentation 属性を付けるだけで Modal 表示になります。

活用するには、ドキュメントおよびサンプル、そして実装された Presenter のコードも参照する必要があると思います。

ちなみに Android の presenter 改善は MvvmCross 6.0 予定です。

MvvmCross 6.0

既に MvvmCross 6.0 のロードマップも公開されています。

  • Even better Xamarin.Forms support
  • A new default presenter for Android
  • Better handling of Fragments

さらに、.NET Standard 2.0 サポートも含まれています。プラグインの構造の見直しや、より async/await や C# 7 の使用も。

ロードマップの議論は MvvmCross roadmap · Issue #1415 · MvvmCross/MvvmCross でされています。

Xamarin.iOS で psd ファイルを表示

Visual Studio (Xamarin.iOS)の iOS アプリで Photoshop ファイル(.psd ファイル)を、できるだけお手軽に表示する方法です。

UIWebView

iOS 10 から UIWevView で psd ファイルが表示できるようになっています! JPEG 画像と同じように表示するだけで psd ファイルが描画されます。

webView.LoadRequest(new NSUrlRequest("file://***"));

PSD Plugin for Paint.NET

C# によるライブラリでは、Paint.NET で psd ファイルを読み込み・保存可能にするプラグイン「PSD Plugin for Paint.NET」を開発されている方がいます。

Xamarin.iOS プロジェクトに組み込んでみたところ、少しの修正で使えました。コードは割愛。

プラグインのソースコードは、CodePlex にあります(MIT ライセンス)。現在のバージョンは、Version 2.5.0 (Sep 11, 2016)。

特徴は、レイヤー情報も読み込めます。ただし、各レイヤーの合成は Paint.NET 本体の仕事です。合成しようとすると、また別に自力で処理が必要なため、使いどころは難しそうです。。

Magick.NET

最後は参考情報です。

C# 製の画像ライブラリ「Magick.NET」では、psd ファイルも扱えます。ただし Windows 専用です。

.NET Core 版も開発されていますが、現在(Version 7.0.5.502)は、Windows でのみしか動きません。今後、Xamarin.iOS 等と組み合わせて使えるようになるかもしれません。

例外 Foundation.You_Should_Not_Call_base_In_This_Method

Xamarin で開発していると「Foundation.You_Should_Not_Call_base_In_This_Method: Exception of type ‘Foundation.You_Should_Not_Call_base_In_This_Method’ was thrown.」という例外とメッセージが出ることがあります。

名前の通り、base クラスのメソッドを呼び出してはいけません。UITableViewSource など override して使っているメソッドで、エディターが自動挿入した base クラスへの呼び出しがないか確認してみてください。

Xamarin + MvvmCross などの環境の場合、ピンポイントで例外発生場所を示してくれないときがあります。TableViewSource の場合、CanEditRow メソッドなどが base クラスのメソッドをつかえません。

Android カメラ API でフロント/バックカメラ画像を同時に表示

Android のフロント/バックカメラを同時にアクセスする方法です。結論は Android 4 ではできたようで 5.0 (Lollipop) からできなくなっている(?)ようです。

Android の カメラ API は、新旧2種類あります。

Camera

旧 Camera API では、実際にされている方がいて、同時に両方のカメラ映像を表示するデモとソースコードが公開されています。

www.youtube.com

bitbucket.org

ただし、Android 5 で実行すると Camera.Open のところで Fail to connect to camera service の例外が出ます。Bitbucket のプロジェクトの Issue にそのような コメント もあります。

Camera2

新しい camera2 API で試してみると、CmeraManage.openCamera で2台目にアクセスすると MAX_CAMERAS_IN_USE エラーが発生します。

複数の Android 端末で試しましたが同じ結果。もしかすると機種によっては可能なものもあるかもしれませんが、Android OS で拒否されているのかも。

Galaxy シリーズには、デュアルカメラアプリがあり、標準カメラアプリではもちろん同時撮影できますが、試したコードは同様にエラーとなりました。

操作ガイド

MvvmCross + Xamarin.iOS で NavigationBar 付きの modal 画面を使う

MvvmCross で画面遷移は ViewModel 側で ShowViewModel<SecondViewModel>() のように ShowViewModel メソッドを使います。

github.com

このとき iOS アプリで modal 画面として表示する方法です。

IMvxModalIosView の実装

View 側のクラスは IMvxModalIosView を実装します。

public partial class SecondView : MvxViewController<SecondViewModel>, IMvxModalIosView<SecondViewModel>
{
    public SecondView(IntPtr handle) : base(handle)
    {
    }
}

MvxIosViewPresenter のカスタマイズ

デフォルトの Presenter クラスを override することで、iOS の画面遷移・表示をカスタマイズできます。modal 表示をサポートするクラスが用意されているので、これを使います。

AppDelegate.cs にある次の部分を修正します。

var setup = new Setup(this, Window);
setup.Initialize();

単に modal を使う場合は、MvxModalSupportIosViewPresenter クラスを使います。この場合は modal 画面の NavigationController は null になります。

var presenter = new MvxModalSupportIosViewPresenter(this, Window);
var setup = new Setup(this, presenter);
setup.Initialize();

NavigationBar を付けて modal 画面を表示したい場合は、MvxModalNavSupportIosViewPresenter クラスを使います。

var presenter = new MvxModalNavSupportIosViewPresenter(this, Window);
var setup = new Setup(this, presenter);
setup.Initialize();

何をしているかはソースコードを読むしかありません。

github.com

Xamarin.Android で interface を実装するとき Handle と Dispose の実装

Xamarin.Android で Listener などの interface を実装しようとしたとき、Handle プロパティと Dispose メソッドを実装する必要があります。

class SampleListener : AppBarLayout.IOnOffsetChangedListener
{
    public IntPtr Handle => throw new NotImplementedException();

    public void Dispose()
    {
        throw new NotImplementedException();
    }

    public void OnOffsetChanged(AppBarLayout appBarLayout, int verticalOffset)
    {
        throw new NotImplementedException();
    }
}

これは Java.Lang.Object を継承すれば解決します。

class SampleListener : Java.Lang.Object, AppBarLayout.IOnOffsetChangedListener
{
    public void OnOffsetChanged(AppBarLayout appBarLayout, int verticalOffset)
    {
        throw new NotImplementedException();
    }
}