ちおさん雑記帳

何の役にも立たない雑記から、誰かの役に立つ(かも知れない)メモなど・・

WPF ウィンドウ描画が画面に入り切っているかを判定する

アプリケーションのウィンドウ表示位置を記憶しておき、次回起動時に表示位置を復帰させたい。
ということがあるかも知れませ。しかし「但し、ウィンドウが画面からはみ出す場合には中央に表示させる」みたいな条件を考慮するにはどうしたら良いか、サンプルを作成してみました。

モニターが1台だけの場合はそれほど難しい話ではありませんが、複数モニターで

f:id:caffe1208:20200406094301p:plain

上記のような環境で、


f:id:caffe1208:20200406094348p:plain

このように、四隅が全てどこかのモニターに表示されている場合や


f:id:caffe1208:20200406094449p:plain

このように、右下が入りきっていない場合、


f:id:caffe1208:20200406094520p:plain

このように、左上が入りきっていない場合などに「はみ出している」ことを認識するには
「四隅何れかの座標が、どのモニターのデスクトップ領域にも含まれない」という判定が出来ればよいことになります。

この例における各座標を見てみましょう。


f:id:caffe1208:20200406094602p:plain

上記のような感じになります。

さて、この場合に「任意の座標がモニターの表示領域に入っている/入っていない」判定することになりますが、方法は幾つかあって
1つは、接続されている各モニターの座標情報を取得し、領域内にアプリケーションの座標が含まれているか判定する方法が考えられます。
接続されている複数(単一でも可)モニターの情報は、System.Windows.Forms名前空間のScreenクラス、AllScreensプロパティで取得可能で、例えば各モニターの情報を表示するには以下のようなコードになります。

            foreach (Screen scrn in Screen.AllScreens)
            {
                Console.WriteLine(scrn.DeviceName + " -> " + scrn.Bounds.ToString());
            }


実行結果
\\.\DISPLAY1 -> {X=0,Y=0,Width=1920,Height=1080}
\\.\DISPLAY2 -> {X=1920,Y=-150,Width=1920,Height=1080}


アプリケーションの座標4点について、AllScreensプロパティで取得した各モニターの領域に入っているかを判定することになりますが、モニターの枚数が増える(と言っても10枚単位とかはほぼあり得ないですが)と、その分処理時間が増える可能性が高いです。(特に一番右下側のモニターにアプリケーションを表示している場合)

何か良い方法はないか、とMSDNでScreenクラスについて見てみると、以下のようなメソッドが用意されていることが分かりました。

f:id:caffe1208:20200406094900p:plain


この中でも、FromPoint(Point)メソッドと、FromRectangle(Rectangle)が使えそうなので詳細を見てみると


f:id:caffe1208:20200406094937p:plain


つまり、引数で指定した座標(Point)位置に該当するモニターのScreenオブジェクトが取得できるようですが、1点
「ポイントを保持するディスプレイがない複数ディスプレイ環境では、指定したポイントに最も近いディスプレイが返されます。」

ということは即ち、


f:id:caffe1208:20200406095015p:plain


上記のような表示になっている際に、アプリケーションの左上の座標を指定して、FromPointメドッドを実行すると、最も近いディスプレイ、つまりモニター①のScreenオブジェクトが返却されます。
アプリケーションの各座標についてFromPointメソッドを実行した場合、
左上 → モニター①が返却される(実際にはモニター①の範囲ではない)
右上 → モニター②が返却される
右下 → モニター②が返却される
左下 → モニター①が返却される
となり、各座標について取得したScreenが実際にその範囲(X座標はX~X+wigthの範囲内か、Y座標はY+heightの範囲内か)を判定すればよいことになります。

というわけで前置きが長くなりましたが、サンプルを作成します。

ボタン押下時に、現在表示されているウィンドウ位置の各四隅がモニターに実際に表示されているかどうかを判定する処理を以下に示します。

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            int top = (int)this.Top;
            int left = (int)this.Left;
            int buttom = (int)(this.Top + this.Height);
            int right = (int)(this.Left + this.Width);

            // 左上
            if(IsLocateOut(left, top))
            {
                Console.WriteLine("左上がどの画面にも表示されていない");
                return;
            }
            // 右上
            if (IsLocateOut(right, top))
            {
                Console.WriteLine("右上がどの画面にも表示されていない");
                return;
            }
            // 右下
            if (IsLocateOut(right, buttom))
            {
                Console.WriteLine("右下がどの画面にも表示されていない");
                return;
            }
            // 左下
            if (IsLocateOut(left, buttom))
            {
                Console.WriteLine("左下がどの画面にも表示されていない");
                return;
            }
            Console.WriteLine("ウィンドウは画面に完全に表示されている");
        }

        // FromPoint()の戻り値は実際ははみ出していても直近のScreenが返却されてしまうので座標を判定する
        private bool IsLocateOut(int locateX, int locateY)
        {
            var pt = new System.Drawing.Point(locateX, locateY);
            var screen = Screen.FromPoint(pt);

            if (screen.Bounds.X > pt.X || (screen.Bounds.X + screen.Bounds.Width) < pt.X)
            {
                return true;
            }
            if (screen.Bounds.Y > pt.Y || (screen.Bounds.Y + screen.Bounds.Height) < pt.Y)
            {
                return true;
            }
            return false;
        }


コードの処理内容については、MSDNでScreenクラスのリファレンス
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.screen?view=netframework-4.8
を参照してもらえれば、難しいことはしていないので、説明は不要かなと思いますので割愛。

そんなわけで以上になります。

C# Task.Run()で時間の掛かる処理を別スレッドで実行させる

今更備忘録シリーズ第x段
ということで、Task.Run()で時間の掛かる処理を別スレッドで実行させるコード。
意外と分かりにくい(そうでもないかも)のは、別スレッドで実行させた処理が完了していなくても、
Task.Run()以降の行も(完了を待たずに)実行されるという話。
以下、簡単なサンプル

using System;
using System.Threading.Tasks;
using System.Threading;

namespace Sample1
{
    public class SampleClassA
    {
        public void ProcessA()
        {
            Task<bool> task = Task.Run(() => HeavyMethod()); // 別スレッドで重いメソッドを実行するが
            Console.WriteLine(task.Status.ToString());       // await指定やResultアクセスがない場合はすぐ次の行が実行される
                                                              // ここでは WaitingToRun
            bool b = task.Result; // TaskのResult(この場合bool値)を取得しようとすると、別スレッドの実行が終了するまでここで待つ
            Console.WriteLine(task.Status.ToString()); // ここでは RanToCompletion
        }
        private bool HeavyMethod()
        {
            Thread.Sleep(3000); // 重い処理
            return true;
        }
    }
}

LINQで行追加または更新する (忘備録)

超絶今更感が半端ないですが、個人的な忘備録メモなのでね・・

 

といことで超ざっくりサンプル

 

たとえば下記のようなDataTableがあったと仮定してですが、

 

f:id:caffe1208:20200103161718j:plain

以下サンプル

続きを読む

ドライブ喪失によりアンインストール出来なくなってしまったプログラムをアンインストールさせる方法

タイトル長っ!

 

ということでですね、題名の状態になってしまって半日ハマったので一応メモ書き。

 

経緯としては

 

・H:ドライブ(HDD)にVisual Studioをインストールしていた

 

・このHDDがもう4,5年もので、たまーに嫌な音がするので、

 チェックをかけたら寿命が近い警告状態なことが判明

 

・急いで代替えの容量大きめのHDDを買ってくる

 

・ついでだったので、容量が少なめで微妙なHDD数台を全て外して

 買ってきたHDD1個に集約

 

で、快適に運用してたものの、久しぶりにVisual Studio

起動しようとしたら、あら大変、起動できないってそりゃそうだ。

 

しょうがないので一旦アンインストールしてから入れなおすかー

と消そうとしたらアンインストールが失敗する!

 

どうやらH:\Program Files~がないとだめみたい。。

 

えー、もうHDD処分しちゃってH:ドライブ復活できないんですけど!

 

 

で、レジストリに書いてある「H:\なんちゃら」を全て「C:\なんちゃら」

に書き換えればいけるんじゃない!?

 

 

とやってみましたが、結果変わらず(アンインストール失敗)

 

 

悩んだ末、物理的にH:ドライブが存在する状態にして試してみたらできました!

 

というながーーい前置きですが、そんな感じですハイ。

 

 

f:id:caffe1208:20191230193428p:plain

 

こんな感じで、C:ドライブ以外のドライブを一時的にH:に変えてアンインストールを実行してみましたの図

 

 

 

CLR YUV422形式の画像データをRGB24に変換する処理のサンプル

処理書いたのでぺとぺと貼り付けておきますです。

(動作確認はしてあるので、2017.03.16現在では少なくともちゃんと動きます)

 

YUV422のYUY2およびUYVYに対応してあります。

第6引数で指定して読み込みbyte位置を切り替えています。

 

変換に使用している計算式は調べてみるとわかりますが、幾つかの値があり、どれを使えばいいの?となるかと思います。

今回私は、openCVのドキュメントを元に値を流用しました。

(ですので、恐らくopenCVで変換した結果と同じになると思います(試してません))

 

変換処理の内容が理解しやすいように書いたつもりです。

なので、若干というか、かなり速度的に重いコードになっているかとは自覚していますw

本当はビットシフト等を使えば早くなるような気もします。

(そういうサンプルが載っている記事も見かけますが、初心者には分かりにくいかも知れません)

 

ちなみに、この処理でも、Debugビルドしたもので動かしても、デコードおよび描画で60fps以上余裕で間に合います

 

という訳で、以下、ソース

続きを読む

NuGetでsqlite-net-pclがパッケージ追加できない対応

少しハマったので、単純ではありますが、メモとして記録。

(初心者しかこんなとこで躓かないのかも知れませんが。。些細な事でも記録として残すことにします)

 

あ、Xamarin Studioでの話なので、Visual Studioで同じことになるかは全く分かりません。

 

続きを読む