rksoftware

Visual Studio とか C# とかが好きです

NuGet のキャッシュを削除する

NuGet からパッケージを導入すると、パッケージがローカルマシンにキャッシュされます。
NuGet から取得して使うパッケージは大体決まっていてプロジェクトを新規作成するなどするたびに毎回導入するので、キャッシュしてくれることはありがたいことです。
しかし、そのキャッシュを削除したい場合もあると思います。
私は今回、NuGet パッケージ自体を作る際にローカルでデバッグしたキャッシュを削除したくなりました。

■ キャッシュの場所

Windows の場合
[ 参考: https://docs.microsoft.com/en-us/nuget/consume-packages/managing-the-nuget-cache ]

http-cache: C:\Users\user\AppData\Local\NuGet\v3-cache #NuGet 3.x+ cache
packages-cache: C:\Users\user\AppData\Local\NuGet\Cache #NuGet 2.x cache
global-packages: C:\Users\user.nuget\packages\ #Global packages folder
temp: C:\Users\user\AppData\Local\Temp\NuGetScratch #Temp folder

上記フォルダから削除したいパッケージ(バージョン)をエクスプローラーで削除して意図通りクリアされました。
ただ、本当は nuget locals コマンドで削除するようなので、自己責任でお願いします。
nuget locals コマンドについては上記の参考サイトを確認してください。

Mac の場合
軽く検索しても docs.microsoft.com で情報を見つけられなかったのですが、Windows と同じく。

/Users/user/.nuget/packages

の中にあったパッケージを削除することでクリアされました。

ひとまずちゃんと動いています。

別アセンブリのクラスをリフレクションで取得する

Type をクラス名の文字列で取得するには、reflection を使用し、Type.GetType(文字列) メソッドを使用します。
例えば、System.String の Type は

var s = Type.GetType("System.String");

のようになります。

■ 取得できる Type

上記の方法で取得できるのは、自身のアセンブリ(dll / プロジェクト)のクラスと、Mscorlib.dll のクラスだけです。
[ 参考: https://msdn.microsoft.com/ja-jp/library/w3f99sx1(v=vs.110).aspx ]

■ 別アセンブリのクラスを取得するには

引数に設定する文字列を、完全限定名で指定します。
例えば、System.String の Type は

var s = Type.GetType("System.String,mscorlib");

のようになります。
ここで、, の後ろの mscorlib はアセンブリ(dll)名です。

■ アセンブリ名を調べる

Visual Studio であれば、場合によっては簡単に調べることができます。「定義へ移動」をした際に先頭に

#region Assembly mscorlib, Version=4......

のような箇所があります。
この mscorlib の部分をアセンブリ名に設定すれば OK です。

Xamarin Forms でバックグラウンド処理内で画面の値を変更する

Xamarin.Forms に限らず多くのプラットフォームでそうなのですが、UI の更新は UI スレッド(メインスレッド)でしか行えません。
例えば、長い時間のかかる処理をスレッドを立ててバックグラウンドで行いつつ、進捗を UI に表示する場合などに少し手間をかけないとエラーになってしまいます。

■ 結論

Xamarin.Forms.Device.BeginInvokeOnMainThread メソッドを使用します。

■ 使用例

次のコードはボタンをクリックすると 10,000 ミリ秒間バックグラウンドのスレッドが実行されます。
その中で、100 ミリ秒ごとに UI の値を変更しています。
※お急ぎの方はコメント「// UI スレッドでラベルの値を更新」の部分だけ見ていただければ OK です。

public partial class MainPage : ContentPage
{
    Label label;
    public MainPage()
    {
        InitializeComponent();

        // UI の構築。ここは本題とは関係ない
        var sl = (StackLayout)(this.Content = new StackLayout());
        var button = new Button() { Text = "button" };
        label = new Label() { Text = "0" };
        sl.Children.Add(button);
        sl.Children.Add(label);
        
        // ボタンクリックで、別スレッドで処理
        button.Clicked += (sender, e) => new System.Threading.Thread(
            new System.Threading.ThreadStart(delegate ()
            {
                for (var i = 0; i < 100; ++i)
                {
                    System.Threading.Thread.Sleep(100);

                    // UI スレッドでラベルの値を更新
                    Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
                            this.label.Text = DateTime.Now.ToString());
                }
            })
        ).Start();
    }
}

秋葉原 C# もくもく会 #20 勉強会を開催しました。

■ C# もくもく会

C# もくもく会 #20 を開催しました。

C# もくもく会 は東京の秋葉原で毎週木曜日に開催している .NET 系の勉強会です。
もくもく自習を基本とし、分からないことを教えあったり情報共有したりしている会です。

今回は 10 名という非常に多くの方に来ていただけました。
定期開催していますので、お時間のある時に遊びに来ていただければと思います。
ちょっと詰まった時、ネット上で聞くのははずかしいなぁ、という課題のできた時などにも思い出していただけると嬉しいです。

■ 次回予定

次回は 2018/01/11 に開催予定です。

.NET に関心のある方、是非遊びに来てください。

Xamarin Forms で画面をスリープしないようにする

Xamarin Forms で画面を消灯しないようにするには、良い Plugin がないようです。
Xam.Plugins.ManageSleep という Plugin がありましたが、残念ながら Android の画面が消灯してしまいます。

■ 概要

iOS と Android で画面を消灯しないようにする機能を実装し、DependencyService で共通コードから呼び出します。

■ コード

早速コードです。今回は XFKeepScreen というプロジェクト名で作っています。
iOS
UIApplication.SharedApplication.IdleTimerDisabled の値を設定しているだけのシンプルなコードです。

using UIKit;
using XFKeepScreen.iOS;

[assembly: Xamarin.Forms.Dependency(typeof(KeppScreenOn))]
namespace XFKeepScreen.iOS
{
    public class KeppScreenOn : IKeepScreenOn
    {
        public void Set(bool keepOn) => UIApplication.SharedApplication.InvokeOnMainThread(() =>
                                UIApplication.SharedApplication.IdleTimerDisabled = keepOn);
    }
}

Android
こちらは少し複雑になっています。WindowManagerFlags.KeepScreenOn フラグを設定するだけの簡単な処理なのですが、厄介なのは設定する際に Activity のインスタンスが必要な点です。
今回は MainActivityOnCreate の中で static フィールドに確保するようにしています。
実は Activity のインスタンスは Xamarin.Forms.Forms.Context プロパティで簡単に取得することもできるのですが、残念なことにこのプロパティは Obsolete とされています。 動作はしますが警告になってしまうため今回は使用していません。

using Android.App;
using Android.Content.PM;
using Android.Views;
using Android.OS;
using XFKeepScreen.Droid;

[assembly: Xamarin.Forms.Dependency(typeof(KeppScreenOn))]
namespace XFKeepScreen.Droid
{
    [Activity(Label = "XFKeepScreen", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true,
                 ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);

            // ここで Activity を static フィールドに確保している
            KeppScreenOn.SetActivity(this);

            LoadApplication(new App());
        }
    }

    public class KeppScreenOn : IKeepScreenOn
    {
        // Activity を static フィールドに確保
        private static Activity _mainActivity;
        public static void SetActivity(Activity activity) => _mainActivity = activity;

        // KeepScreenOn を設定
        public void Set(bool keepOn) => _mainActivity.RunOnUiThread(() =>
        {
            if (keepOn)
                _mainActivity.Window.AddFlags(WindowManagerFlags.KeepScreenOn);
            else
                _mainActivity.Window.ClearFlags(WindowManagerFlags.KeepScreenOn);
        });
    }
}

共通コード
DependencyService で IKeepScreenOn の実装を取り出し、Set メソッドで ON/OFF を設定します。

using System.Threading.Tasks;
using Xamarin.Forms;

namespace XFKeepScreen
{
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            // UI の構築。KeepScreenOn の使い方とは直接関係ない
            var sl = (StackLayout)(this.Content = new StackLayout());
            var button = new Button() { Text = "button" };
            sl.Children.Add(button);

            // この処理内が KeepScreenOn の使い方
            button.Clicked += async (sender, e) =>
            {
                var keepScreenOn = DependencyService.Get<IKeepScreenOn>();
                keepScreenOn.Set(true);

                // ...ここに時間のかかる処理...

                keepScreenOn.Set(false);
            };
        }
    }

    public interface IKeepScreenOn
    {
        void Set(bool keepOn);
    }
}

■ Dependency Service について

今回利用している Dependency Service については次を参考にしてください。

マイクロソフト MVP を受賞しました

マイクロソフト MVP を受賞しました。受賞カテゴリーは Visual Studio and Development Technologies です。
これも、勉強会に参加してくださった皆様、登壇の機会を与えてくださった方々、アドバイスをくださった方々のおかげです。
正直、まだ実感はありませんが、より一層頑張って活動して行きます。今後もよろしくお願いいたします。

■ 主な活動

城東.NET Users Group
毎月第3水曜日開催の .NET 全般の勉強会です。
この会では毎月必ずその時の最新に近い内容で LT をするようにしています。
C# 勉強会
毎週木曜日開催の C# もくもく会です。
もくもく勉強や作業だけでなく、技術的な相談や情報交換も積極的に行っています。

Japan Xamarin User Group
JXUG 主催のイベントに高頻度で参加させていただいています。
また、JXUG 内で Xamarin もくもく会を開催させていただいています。
もくもく会は不定期開催ですが、よければ遊びに来てください。
その他
単発でテーマを決めた会を開催したり、いろいろな勉強会へ参加させていただいたりしています。
勉強会支援サイトなどで気にかけてくださるとうれしいです。

今後もよろしくお願いいたします。

Xamarin Froms で写真を撮影する

Xamarin.Froms で写真を撮影するには MediaPlugin を使うのが簡単です。
[ MediaPlugin : https://github.com/jamesmontemagno/MediaPlugin ]

■ NuGet パッケージのインストール

・Forms プロジェクトを .NET Standard で作成します。
・NuGet から「xam.plugin.media」を共通プロジェクトとプラットフォーム毎のプロジェクト全てにインストールします。
※Android、と iOS のみで確認しています。

非常にシンプルに使えるので、上記リポジトリの readme に書かれたコードをコピペすればすぐに使えてしまいます。
ただ、折角なので日本語でコメントを入れてみましたので、良かったら参考にしてください。

■ 独自の試み

1 点独自の試みとして、撮影時に保存されたファイルをすぐに消すようにしています。
デバッグしているとどんどんファイルが増えていってしまうのが気になって。

■ コード

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        // UI の構築。 Plugin の使い方とは直接関係ない
        var sl = (StackLayout)(this.Content = new StackLayout() { VerticalOptions = LayoutOptions.Fill });
        var button1 = new Button() { Text = "Take Photo" };
        var image1 = new Image() { HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill };
        sl.Children.Add(button1);
        sl.Children.Add(image1);

        // ボタンクリックイベント。 この中身が Plugin の使い方
        button1.Clicked += async (sender, e) =>
        {
            // Plugin の初期化。おまじない
            await Plugin.Media.CrossMedia.Current.Initialize();

            // カメラが使用可能で、写真が撮影可能かを判定。
            if (!Plugin.Media.CrossMedia.Current.IsCameraAvailable || !Plugin.Media.CrossMedia.Current.IsTakePhotoSupported)
            {
                // カメラが使用不可または写真が撮影不可の場合、終了
                return;
            }

            // カメラが起動し写真を撮影する。撮影した写真はストレージに保存され、ファイルの情報が return される
            var file = await Plugin.Media.CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
            {
                // ストレージに保存するファイル情報
                // すでに同名ファイルがある場合は、temp_1.jpg などの様に連番がつけられ名前の衝突が回避される
                Directory = "TempPhotos",
                Name = "temp.jpg"
            });

            // 端末のカメラ機能でユーザーが「キャンセル」した場合は、file が null となる
            if (file == null)
                return;

            // 今回独自に試してみた部分
            // ストレージに保存された社員ファイルの中身をメモリに読み込み、ファイルは削除してしまう
            var bytes = new Queue<byte>();
            using (var s = file.GetStream())
            {
                var length = s.Length;
                int b;
                while ((b = s.ReadByte()) != -1)
                    bytes.Enqueue((byte)b);
            }
            System.IO.File.Delete(file.Path);
            file.Dispose();

            // 写真を画面上の image 要素に表示する
            image1.Source = ImageSource.FromStream(() =>
            {
                // 元のサンプルはファイルから読み込んでいたが、
                // 今回独自にメモリからの表示を試している
                return new System.IO.MemoryStream(bytes.ToArray());
            });
        };
    }
}

たったこれだけで写真を撮影して画面上に表示できます。
素晴らしいですね。