20200823のC#に関する記事は8件です。

C#を使って秘密鍵と公開鍵で暗号・復号

題記の通り、C#を使用した暗号化・復号化の実装。

なるべく簡単に暗号化と復号化の機能を使いたいな、と思って実装してみた。

参考にしたのはMSの以下のサイト。
https://docs.microsoft.com/ja-jp/dotnet/standard/security/encrypting-data

環境はVS2019で .NET Core 3.1を使用。

イメージとしては、以下のようなやり取りを想定してコーディング。

(1) サーバーが秘密鍵と公開鍵を発行する。
(2) クライアントに文字列で公開鍵情報を渡す。
(3) クライアントは公開鍵情報を使ってデータを暗号化、サーバーに送付。
(4) サーバーは秘密鍵情報を使ってデータを復号。

証明書とか使えばいいのかもしれないが、そこら辺の知識はあまりないのでコードだけですべてを完結させたかった・・・・

ソースは以下。(クリックするとソースが表示されます)
namespace RsaConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            // 暗号化したい文字列をバイト配列に変換
            string encryptWord = "冷やし中華ァァ~";
            var encByte = Encoding.Unicode.GetBytes(encryptWord);

            // 秘密鍵と公開鍵を生成
            RSA rsa = RSA.Create();

            // 鍵情報を保存
            var publicKey = rsa.ToXmlString(false);
            var privateKey = rsa.ToXmlString(true);

            // 公開鍵情報の文字列からオブジェクトを復元
            byte[] encryptedByte;
            using (RSA encRsa = RSA.Create())
            {
                encRsa.FromXmlString(publicKey);
                encryptedByte = encRsa.Encrypt(encByte, RSAEncryptionPadding.Pkcs1);
            }

            // 暗号化したバイトデータを16進数文字列に変換
            var encByteToString = BitConverter.ToString(encryptedByte);
            Console.WriteLine($"Encrypt: {encByteToString}");

            // 
            // 上記の方法で生成された16進数文字列を外部から受信したと仮定して、
            // 秘密鍵を使って復元する
            //

            // 送信されてきた文字列をバイト配列に変換
            var encStrToBytes = encByteToString.Split('-').Select(r => Convert.ToByte(r, 16)).ToArray();
            byte[] decryptedByte;

            // 秘密鍵情報の文字列からオブジェクトを復元し、復号
            using (RSA decRsa = RSA.Create())
            {
                decRsa.FromXmlString(privateKey);
                decryptedByte = decRsa.Decrypt(encStrToBytes, RSAEncryptionPadding.Pkcs1);
            }

            var decryptedString = Encoding.Unicode.GetString(decryptedByte);

            Console.WriteLine($"Decrypt: {decryptedString}");

            // 試しにテキトーな鍵で復号してみる
            var invalidDec = string.Empty;
            try
            {
                using (RSA invalidRsa = RSA.Create())
                {
                    decryptedByte = invalidRsa.Decrypt(encStrToBytes, RSAEncryptionPadding.Pkcs1);
                }
            }
            catch
            {
                Console.WriteLine("Failed to decrypt.");
            }

            return;
        }
    }
}

出力はこんな感じ。

Encrypt: 6F-5B-B7-F8-3D-C9-00-23-BE-84-29-82-56-7E-BE-7B-79-E8-BB-5F-76-2B-03-A8-49-1C-76-FB-49-56-04-1E-8A-CA-AD-5C-57-F7-21-CC-17-FE-04-C8-FD-F9-D5-0D-7B-2B-2B-3A-34-A0-AE-5D-82-D3-1E-78-55-22-06-DB-C2-AA-99-A8-D1-52-6A-27-0F-A6-86-D9-CA-DA-78-6F-00-D2-6C-1C-51-F2-88-FA-CC-F8-CD-B0-0D-8E-D1-55-89-53-43-4A-92-25-DD-13-55-8E-F4-95-5D-6E-B3-95-89-3A-DE-6A-A1-A5-89-A9-1F-3F-30-6A-04-D0-B0-4E-CF-CA-6E-03-D9-D7-30-2C-48-31-8A-34-4F-0D-5E-1A-19-CF-61-52-A0-AB-20-C0-69-B6-DE-CE-C7-6C-59-C4-C0-64-2A-05-7B-07-96-A0-77-DD-F3-5E-D0-1C-E8-76-3D-A7-6B-51-8C-BC-50-A9-9D-03-31-1C-36-97-DB-F8-63-55-4F-2C-85-FA-C0-03-C6-C8-BE-93-11-DA-67-CD-55-3E-E0-D3-BC-13-FF-26-FD-A6-F2-87-5E-31-01-C0-4C-9D-6F-6A-45-8F-34-A5-07-6B-C3-A7-19-48-77-89-4E-9F-A5-9C-F2-61-83-CE-3E-66-59-47-56-10-F3-03
Decrypt: 冷やし中華ァァ~
Failed to decrypt.

ちゃんと復号できてるし、テキトーな鍵じゃ復号できてませんね(当たり前か)

あとは、メッセージのハッシュ送付とかも一緒にやれば、通信相手の検証もまとめてできるということか~。
そこまでやればまぁまぁ安全ですね~。

以上!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[メモ] モデルバインディング

<LABEL>-<MESSAGE>

はじめに

ASP.NET Core のモデルバイディングについて調べたときのメモです。

基本的な動作

PetsController.cs
using Microsoft.AspNetCore.Mvc;

namespace webapi01.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class PetsController : ControllerBase
    {
        public class Pet
        {
            public int Id { get; set; }

            public string Name { get; set; }
        }

        // パスパラメータやクエリパラメータの値はアクションメソッドに同名の引数を用意すると取得できる(HttpContextからでも取得できる)
        // パラメータの大文字、小文字は区別されない
        // http://localhost:5000/pets/2?DogsOnly=true
        [HttpGet("{id}")]
        public ActionResult<Pet> GetById(int id, bool dogsOnly) // id=2, dogsOnly=true
        {
            return new Pet()
            {
                Id = id,
                Name = $"huga{id}"
            };
        }

        // 複合型の引数の場合、各プロパティの値はリクエストボディから取得される
        // リクエストボディのフォーマットはConsumes属性で指定するかContent-Typeヘッダの値を使用する
        [HttpPost]
        public ActionResult<string> Post(Pet pet)
        {
            // 受信したデータをDBへ登録したり…

            // 201 Created を返す。レスポンスヘッダにLocationを追加しリソースのURLを返す。
            return CreatedAtAction(nameof(GetById), new { id = pet.Id }, pet);
        }
    }
}

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Entity Data Modelを用いて既存データベースに接続

この記事では、ASP.NET MVC5のプロジェクトにて、
Entity Data Modelを利用してデータベース(SQL Server)と接続。
最終的に以前、こちらの記事で作成したログイン画面の認証を固定値でなく、データベース接続して認証する方式に修正する方法をまとめてあります。

事前準備

使用ツール

・Visual Studio Community 2019
こちらでダウンロード可能です。(無料)

・SQL Server 2019 Developer エディション
こちらでダウンロード可能です。(無料)

使用プロジェクト

こちらの記事で作成したログイン機能のプロジェクトを使用して解説していきます。

データベース構築

今回は、Microsoft SQL Server でデータベースを作成して、ユーザーマスタ(M_USER)を作成します。
SQL Serverのインストールに関しては、ここでは割愛します。

SQL Server上で、以下のSQLを実行してください。

ユーザーマスタ(M_USER)

M_USR_create.sql
CREATE TABLE [dbo].[M_USER](
    [user_id] [nvarchar](50) NOT NULL,
    [password] [nvarchar](50) NOT NULL,
    [user_name] [nvarchar](100) NOT NULL,
    [authority] [tinyint] NOT NULL,
    [del_flag] [tinyint] NOT NULL,
    [create_date] [datetime] NOT NULL,
    [create_user] [nvarchar](50) NULL,
    [update_date] [datetime] NOT NULL,
    [update_user] [nvarchar](50) NULL,
 CONSTRAINT [PK_M_USER] PRIMARY KEY CLUSTERED 
(
    [user_id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

ユーザーマスタ(M_USER)のデータ

M_USER_初期データ.sql
INSERT INTO [dbo].[M_USER]
           ([user_id]
           ,[password]
           ,[user_name]
           ,[authority]
           ,[del_flag]
           ,[create_date]
           ,[create_user]
           ,[update_date]
           ,[update_user])
     VALUES
           ('test_user'
           ,'passwd'
           ,'テストユーザー'
           ,1
           ,0
           ,GETDATE()
           ,'admin'
           ,GETDATE()
           ,'admin')

Entity Data Modelの追加

データベースの環境構築ができたら、プロジェクト上でADO.NET Enitiy Data Modelを追加して、
データベースの情報をマッピングする為のEDMを作成します。

まずはEDMファイルを格納する為のフォルダをプロジェクト内に追加します。
ここではDALというフォルダを追加しました。

追加したフォルダを右クリック→「追加」⇒「新しい項目」を選択します。

edmx作成1.png

追加する項目を選択する画面が開くので、「データ」内にある「ADO.NET Entity Data Model」を選択します。

edmx作成2.png

続いてEntity Data Modelウィザードが起動されます。
まずは「モデルに含めるコンテンツ」の設定です。
Modelには「EF Designer」で作成する方法と、「Code First」で作成する方法の2種類ありますが、
今回は「EF Designer」の方法で「データベースからEF Designer」を選択します。

edmx作成3.png

次にデータ接続する設定になりますが、
先ほど環境構築で作成したデータベースを選択後、
作成するエンティティの名前も決められますが、ここではデフォルト通りに進みます。

edmx作成4.png

edmx作成5.png

edmx作成5_2.png

データベースの設定が完了すると、
マッピングする対象のモデル(テーブル、ビュー、ストアドプロシージャなど)を選択する画面が表示されます。
ここではユーザーマスタ(M_USER)を選択します。
モデルの名前空間も設定できますが、ここではデフォルトのままにします。

edmx作成6.png

EDMファイルでデザイナー上で編集すると以下のダイアログが表示される事がありますが、「OK」で問題ありません。
※何度も表示されると面倒なので、ここでは「今後このメッセージを表示しない」のをつけました。

edmx作成7.png

その後、EDMファイルが作成され、
EDMファイルを選択すると、以下のようなデザイナー画面が表示されます。

edmx作成8.png

また、作成したModel1.edmx内のModel1.tt内にM_USER.csが作成されている事を確認できるはずです。

M_USER.cs
//------------------------------------------------------------------------------
// <auto-generated>
//     このコードはテンプレートから生成されました。
//
//     このファイルを手動で変更すると、アプリケーションで予期しない動作が発生する可能性があります。
//     このファイルに対する手動の変更は、コードが再生成されると上書きされます。
// </auto-generated>
//------------------------------------------------------------------------------

namespace Mvc5LoginSample1.DAL
{
    using System;
    using System.Collections.Generic;

    public partial class M_USER
    {
        public string user_id { get; set; }
        public string password { get; set; }
        public string user_name { get; set; }
        public byte authority { get; set; }
        public byte del_flag { get; set; }
        public System.DateTime create_date { get; set; }
        public string create_user { get; set; }
        public System.DateTime update_date { get; set; }
        public string update_user { get; set; }
    }
}

プログラム修正

EDMファイルを作成したので、ログイン処理のプログラムを修正します。

Contollerの修正

AuthController.cs
using Mvc5LoginSample1.Models;
using Mvc5LoginSample1.DAL;
using System.Linq;
using System.Web.Mvc;
using System.Web.Security;

namespace Mvc5LoginSample1.Controllers
{
    public class AuthController : Controller
    {
        /// <summary>
        /// ログイン 表示
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult Login()
        {
            return View();
        }

        /// <summary>
        /// ログイン処理
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult Login(AuthModel model)
        {
            bool hasUser = false;
            using (var context = new SAMPLEDB01Entities())
            {
                hasUser = context.M_USER.AsNoTracking().Where(x => x.user_id == model.Id &&
                                                            x.password == model.Password &&
                                                            x.del_flag == 0).Any();
            }
            //if (model.Id == "test" && model.Password == "passwd")
            if(hasUser)
            {
                // ユーザー認証 成功
                FormsAuthentication.SetAuthCookie(model.Id, true);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                // ユーザー認証 失敗
                this.ModelState.AddModelError(string.Empty, "指定されたユーザー名またはパスワードが正しくありません。");
                return this.View(model);
            }
        }

/* 省略 */

まずはEDMファイルを格納したDALフォルダへ参照する為、2行目にusingを追加、
データベースからデータを取得する際の技術であるLINQを使用する為のusingを3行目で追加しています。

29行目から37行目でログイン処理を
前回のハードコーディングからユーザーマスタ(M_USER)の認証に修正しています。
LINQのAnyメソッドで、入力したユーザーID、パスワードに一致したデータが、ユーザーマスタ(M_USER)に存在するかチェック。
存在する場合はtrueが返します。

※元のソースコードを見たい方は、こちらの記事を参照ください。

まとめ

さぁ 早速実行してみましょう!

【実行結果】

image

image

ユーザーマスタ(M_USER)に追加したtest_userで無事にログインができました!

ということで
今回はEntity  Data Modelを利用してデータベース接続する方法をご紹介しました。

自分でデータベース接続するクラスを作成する方法もありますが、
この方法でデータベース接続すると、新規テーブル作成やテーブルのカラム追加時に
マッピングするモデルのソースを簡単に自動生成できるので、
初心者の方にはおすすめです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【C#】日時文字列をDateTime型へ変換する

概要

C#で文字列からDateTime型に変換するには「Parse」もしくは「TryParth」メソッドが使用できます。

Parseメソッド

// 日付・時刻あり
string strDateTime = "2020/10/22 15:01:11";
DateTime dateTime = DateTime.Parse(strDateTime);
Console.WriteLine(dateTime.ToString("yyyy/MM/dd HH:mm:ss"));

// 時刻省略
string strDate = "2020/10/22";
DateTime dateTimeFromDate = DateTime.Parse(strDate);
Console.WriteLine(dateTimeFromDate.ToString("yyyy/MM/dd HH:mm:ss"));

// 日付省略
string strTime = "15:01:11";
DateTime dateTimeFromTime = DateTime.Parse(strTime);
Console.WriteLine(dateTimeFromTime.ToString("yyyy/MM/dd HH:mm:ss"));
実行結果
2020/10/22 15:01:11
2020/10/22 00:00:00
2020/08/22 15:01:11

日付・時刻は省略できます。
時刻未入力の場合は 00:00:00 が入ります。
日付未入力の場合は 現在日付が入ります。

例外

渡す文字列がnullの場合は「ArgumentNullException」が発行されます。
渡す文字列が日時として判別不可能な場合は「FormatException」が発行されます。

TryParseメソッド

第1引数に変換する文字列を指定し、第2引数に指定したDateTime型に変換後の日時が格納されます。また変換に成功したかどうかを示す値を返します。

string strDateTime = "2020/10/22 15:01:11";
DateTime dateTime;

if (DateTime.TryParse(strDateTime, out dateTime))
{
Console.WriteLine("成功!");
Console.WriteLine(dateTime.ToString("yyyy/MM/dd HH:mm:ss"));
} else {
Console.WriteLine("失敗!");
Console.WriteLine(dateTime);
}
実行結果
成功!
2020/10/22 15:01:11

参考

https://docs.microsoft.com/ja-jp/dotnet/api/system.datetime.parse?view=netcore-3.1
https://docs.microsoft.com/ja-jp/dotnet/api/system.datetime.tryparse?view=netcore-3.1

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

submitボタンごとにActionメソッドを分ける方法

概要

入力フォームを作る際に、複数のsubmitボタンがあったので、その処理をつくったときのメモ。
今回、セレクター属性を作成して、アクションメソッドにその属性を設定しました。

環境

C# 7.3
ASP.NET MVC 5.2.7

参考

今回以下のページを参考にしました。
https://www.it-swarm.dev/ja/html/aspnet-mvc%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%A7%E8%A4%87%E6%95%B0%E3%81%AE%E9%80%81%E4%BF%A1%E3%83%9C%E3%82%BF%E3%83%B3%E3%82%92%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E5%87%A6%E7%90%86%E3%81%97%E3%81%BE%E3%81%99%E3%81%8B%E3%80%82/958193564/

方法

ModelsフォルダにMultipleButtonAttributeクラスを作成。

MultipleButtonAttributeクラス
using System;
using System.Web;
using System.Web.Mvc;
using System.Reflection;

namespace InputForm.Models
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class MultipleButtonAttribute : ActionNameSelectorAttribute
    {
        public string Name { get; set; }
        public string Argument { get; set; }

        public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
        {
            var isValidName = false;
            var keyValue = string.Format("{0}:{1}", Name, Argument);
            var value = controllerContext.Controller.ValueProvider.GetValue(keyValue);

            if (value != null)
            {
                controllerContext.Controller.ControllerContext.RouteData.Values[Name] = Argument;
                isValidName = true;
            }

            return isValidName;
        }
    }
}
CSHTML
@using (Html.BeginForm())
{
    <form> 
        <input type="submit" name="action:Cancel" value="キャンセル">
        <input type="submit" name="action:Save" value="保存">
    </form>
}
コントローラー
//ビュー表示用
public ActionResult Input(InputContents input)
{
    ViewBag.SelectOptions = new SelectListItem[]
    {
        new SelectListItem(){Value="1",Text="text1"},
        new SelectListItem(){Value="2",Text="text2"},
        new SelectListItem(){Value="3",Text="text3"}
    };

    return View(input);
}
//キャンセルボタンが押されたときに呼び出される
[HttpPost]
[MultipleButton(Name = "action", Argument = "Cancel")]
public ActionResult Cancel(InputContents input)
{
    return View("Input", input);
}
//保存ボタンが押されたときに呼び出される
[HttpPost]
[MultipleButton(Name = "action", Argument = "Save")]
public ActionResult Save(InputContents input)
{
    return View("Input", input);
}

コントローラーには上記のsubmitを受け取るアクションメソッド、Cancel、Saveとともに、Inputというビューを表示させるための、アクションメソッドがあります。

最後に

この記事の趣旨とは異なってしまうのですが、ASP.NETではアクションメソッドでViewを返す際、アクションメソッドを明示しない場合、そのアクションメソッドと同じ名前のcshtmlファイルを探すことをしらずに詰まってしまいました。この記事の記載内容は参考にしたページとほとんど同じなのですが、Viewに返すアクションメソッドを明示的に指定してあります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[c# / WPF] Microsoft Docsの「この記事の内容」っぽいサイドメニュー・ボタンを作る

はじめに

Microsoft Docs 右側のサイドメニュー?みたいなのをWPFで作らないといけなかったので、自分の実装方法を備忘録として書いておきます。

microsoft_docs_sidemenu.png

githubにソースコード置いています。
https://github.com/pisa-kun/SideBarSample

作ったサンプル

先にどんな感じのものを作ったかを公開しておきます。

image.png

Grid.Column をサイドメニューとメイン部分(選択されたサイドメニューで切り替わるコンテンツ)で二分割しています。
サイドメニューのボタンはとりあえず二つ用意し、メイン部分には今回何も割り当てていません。あくまでサイドメニューのデモということで。

image.png

マウスオーバーされるとボタンの背景を少しだけ白で透過(15%程度)させます。

image.png

マウスクリックされるとマウスオーバー時よりも白で透過(30%)させています。

image.png

マウスクリックされて選択中になると左側に白の長方形が表示され、ボタンの背景も白で透過させます。

ButtonAを選択中にButtonBをクリックすると、ButtonAの選択中を解除してButtonBを選択中にするところまでできています。

次からソースコードの解説になります。
プロジェクト構成は下記のようにしています。必要そうな箇所のみ説明します。

image.png

  • images フォルダ
    • Buttonに表示するアイコンをまとめているフォルダ。
  • MainWindow.xaml
    • スタートアップで起動されるウインドウ。先ほどまでお見せしていた画面。
  • SideBarControl.xaml
    • サイドメニューはUserControlとして作成。
  • ViewModel.cs
    • ButtonのCommandと選択状態のプロパティを記述

SideBarControl.xaml

ToggleButtonのテンプレートを編集して無理やりRectangle, Image, TextBlockをまとめて一つのそれっぽいコントロールを作っています。

<UserControl x:Class="sample.SideBarControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:sample"
             mc:Ignorable="d" 
             Name="SideBar"
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <!--Side Bar Image-->
            <!-- BackGroundのOpacityのみ変更 -->
            <!-- SolidColorBrushのアルファ値でOpacity操作 -->
            <Style x:Key="SideBarNewButtonStyle" TargetType="{x:Type ToggleButton}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ToggleButton}">
                            <Grid>
                                <StackPanel Orientation="Horizontal" Background="#00FFFFFF" x:Name="Panel">
                                    <Rectangle x:Name="rectangle" Width="10" Fill="White" Visibility="Hidden" HorizontalAlignment="Left" />
                                    <Image Source="{Binding Path=ImageFileName, ElementName=SideBar}" Width="20" Height="20" Margin="10,0,10,0" HorizontalAlignment="Center" x:Name="StyleImagePath" />
                                    <TextBlock Text="{Binding Path=ButtonText, ElementName=SideBar}" HorizontalAlignment="Right"  VerticalAlignment="Center" Foreground="White" FontSize="12" x:Name="StyleText" />
                                </StackPanel>
                            </Grid>

                            <ControlTemplate.Triggers>
                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsMouseOver" Value="true" />
                                        <Condition Property="IsChecked" Value="True" />
                                    </MultiTrigger.Conditions>
                                    <Setter Property="Background" TargetName="Panel" Value="#26FFFFFF"/>
                                    <Setter Property="Visibility" TargetName="rectangle" Value="Visible"/>
                                </MultiTrigger>

                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsMouseOver" Value="true" />
                                        <Condition Property="IsChecked" Value="false" />
                                    </MultiTrigger.Conditions>
                                    <Setter Property="Background" TargetName="Panel" Value="#26FFFFFF"/>
                                    <Setter Property="Visibility" TargetName="rectangle" Value="Hidden"/>
                                </MultiTrigger>

                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsPressed" Value="true" />
                                    </MultiTrigger.Conditions>
                                    <Setter Property="Background" TargetName="Panel" Value="#4CFFFFFF"/>
                                </MultiTrigger>

                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsChecked" Value="True" />
                                        <Condition Property="IsMouseOver" Value="False" />
                                    </MultiTrigger.Conditions>
                                    <MultiTrigger.Setters>
                                        <Setter Property="Visibility" TargetName="rectangle" Value="Visible"/>
                                        <Setter Property="Background" TargetName="Panel" Value="#39FFFFFF"/>
                                    </MultiTrigger.Setters>
                                </MultiTrigger>

                                <MultiTrigger>
                                    <MultiTrigger.Conditions>
                                        <Condition Property="IsChecked" Value="False" />
                                        <Condition Property="IsMouseOver" Value="False" />
                                    </MultiTrigger.Conditions>
                                    <MultiTrigger.Setters>
                                        <Setter Property="Visibility" TargetName="rectangle" Value="Hidden"/>
                                        <Setter Property="Background" TargetName="Panel" Value="#00FFFFFF"/>
                                    </MultiTrigger.Setters>
                                </MultiTrigger>

                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ResourceDictionary>
    </UserControl.Resources>

    <!--UserControlのNameとElementName を紐づけ-->
    <ToggleButton x:Name="Sidebar_button" Background="Transparent" BorderThickness="0" Style="{StaticResource SideBarNewButtonStyle}" 
                  IsChecked="{Binding Path=IsSelected, ElementName=SideBar}" Command="{Binding Path=ButtonCommand, ElementName=SideBar}"/>

</UserControl>
// SideBarControl.xaml.cs
using Prism.Commands;
using System.Windows;
using System.Windows.Controls;

namespace sample
{
    /// <summary>
    /// SideBarControl.xaml の相互作用ロジック
    /// </summary>
    public partial class SideBarControl : UserControl
    {
        public static readonly DependencyProperty ImageFileNameProperty =
            DependencyProperty.Register(nameof(ImageFileName), typeof(string), typeof(SideBarControl), new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnPropertyChanged)));

        public static readonly DependencyProperty ButtonTextProperty =
            DependencyProperty.Register(nameof(ButtonText), typeof(string), typeof(SideBarControl), new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(OnPropertyChanged)));

        public static readonly DependencyProperty ButtonCommandProperty =
            DependencyProperty.Register(nameof(ButtonCommand), typeof(DelegateCommand), typeof(SideBarControl), new UIPropertyMetadata(null, new PropertyChangedCallback(OnPropertyChanged)));

        public static readonly DependencyProperty IsSelectedProperty =
            DependencyProperty.Register(nameof(IsSelected), typeof(bool), typeof(SideBarControl), new UIPropertyMetadata(false, new PropertyChangedCallback(OnPropertyChanged)));

        public string ImageFileName
        {
            get { return (string)GetValue(ImageFileNameProperty); }
            set { SetValue(ImageFileNameProperty, value); }
        }

        public string ButtonText
        {
            get { return (string)GetValue(ButtonTextProperty); }
            set { SetValue(ButtonTextProperty, value); }
        }

        public bool IsSelected
        {
            get { return (bool)GetValue(IsSelectedProperty); }
            set
            {
                SetValue(IsSelectedProperty, value);
            }
        }

        public DelegateCommand ButtonCommand
        {
            get { return (DelegateCommand)GetValue(ButtonCommandProperty); }
            set { SetValue(ButtonCommandProperty, value); }
        }

        public SideBarControl()
        {
            InitializeComponent();
        }

        private static void OnPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
        }

ImageのSourceプロパティ、TextBlockのTextプロパティ、ToggleButtonのIsCheckedとCommandプロパティはDependencyPropertyを使ってMainWindow.xamlで設定できるようにしておきます。

また、ControlTemplate.Triggersでマウスオーバー、マウスプレス、クリック時などの条件でRectangleのVisibilityやPanelのBackgroundを調整します。

MainWindow.xaml

<Window x:Class="sample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:sample"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition x:Name="SideBar"  Width="100*"/>
            <ColumnDefinition x:Name="MainArea" Width="300*"/>
        </Grid.ColumnDefinitions>
        <!--サイドメニュー-->
        <Grid Grid.Column="0" FocusVisualStyle="{x:Null}">
            <Grid.Background>
                <LinearGradientBrush>
                    <GradientStop Color="#dc143c" Offset="0"/>
                    <GradientStop Color="#ff8c00" Offset="0.8"/>
                </LinearGradientBrush>
            </Grid.Background>
        </Grid>
        <Grid Grid.Column="0" FocusVisualStyle="{x:Null}">
            <Grid.RowDefinitions>
                <RowDefinition Height="30"/>
                <RowDefinition Height="30"/>
            </Grid.RowDefinitions>
            <!--サイドメニューのボタン-->
            <local:SideBarControl Grid.Row="0" ImageFileName="images\e713.png" ButtonText="Button1" ButtonCommand="{Binding ButtonCommandA}" IsSelected="{Binding IsButtonA, Mode=TwoWay}" />
            <local:SideBarControl Grid.Row="1" ImageFileName="images\e772.png" ButtonText="Button2" ButtonCommand="{Binding ButtonCommandB}" IsSelected="{Binding IsButtonB, Mode=TwoWay}" />
        </Grid>
    </Grid>
</Window>

コードビハインド(MainWindow.xaml.cs)についてはコンストラクタに this.DataContext = new ViewModel();を追加しているだけなので割愛します。

説明すべきは下記の部分でしょうか。

<!--サイドメニューのボタン-->
<local:SideBarControl Grid.Row="0" ImageFileName="images\e713.png" ButtonText="Button1"
 ButtonCommand="{Binding ButtonCommandA}" IsSelected="{Binding IsButtonA, Mode=TwoWay}" />

<local:SideBarControl Grid.Row="1" ImageFileName="images\e772.png" ButtonText="Button2"
 ButtonCommand="{Binding ButtonCommandB}" IsSelected="{Binding IsButtonB, Mode=TwoWay}" />

ImageFileNameにpng画像のファイルパスをセット。
ButtonCommandに押されたときのAction、IsSelectedに選択中かどうかのbool値をViewModelからBindingします。

ViewModel.cs

viewModelではbool型のButtonプロパティとDelegateCommandを記述します。

ButtonCommandについてはMessageBoxを表示してButtonのプロパティを変更することを行っています。
アプリとして完成させるならメイン画面のページ遷移などをここに記述しましょう。

public class ViewModel : BindableBase
    {
        private bool isButtonA = false;
        public bool IsButtonA
        {
            get { return this.isButtonA; }
            set { this.SetProperty(ref this.isButtonA, value); }
        }

        private bool isButtonB = false;
        public bool IsButtonB
        {
            get { return this.isButtonB; }
            set { this.SetProperty(ref this.isButtonB, value); }
        }

        public DelegateCommand ButtonCommandA { get; private set; }
        public DelegateCommand ButtonCommandB { get; private set; }

        public ViewModel()
        {
            ButtonCommandA = new DelegateCommand(() => {
                MessageBox.Show($"ButtonA is [{IsButtonA}] state");
                IsButtonA = true;
                IsButtonB = false;
            });

            ButtonCommandB = new DelegateCommand(() => {
                MessageBox.Show($"ButtonB is [{IsButtonB}] state");
                IsButtonA = false;
                IsButtonB = true;
            });
        }

まとめ

UWPだとすでにこういうコントロールあるんでしょうか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[WinForms] 3Dチャート

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;

using OpenCvSharp;

namespace ChartingApp
{
    public partial class Form1 : Form
    {
        Chart chart;
        public Form1()
        {
            InitializeComponent();

            var image = new Mat("data.bmp", ImreadModes.Grayscale);
            image = image.Resize(new OpenCvSharp.Size(image.Cols / 30, image.Rows / 30));
            image = image.Normalize(0, 255, NormTypes.MinMax);

            this.Width = 1000;
            this.Height = 800;

            TrackBar track = new TrackBar();
            track.Dock = DockStyle.Bottom;
            track.Minimum = -44;
            track.Maximum = 44;
            track.LargeChange = 50;
            track.SmallChange = 50;
            track.Value = 30;
            this.Controls.Add(track);

            TrackBar track2 = new TrackBar();
            track2.Dock = DockStyle.Top;
            track2.Minimum = -90;
            track2.Maximum = 90;
            track2.LargeChange = 1;
            track2.SmallChange = 1;
            track2.Value = 15;
            this.Controls.Add(track2);

            TrackBar track3 = new TrackBar();
            track3.Dock = DockStyle.Bottom;
            track3.Minimum = 0;
            track3.Maximum = 100;
            track3.LargeChange = 1;
            track3.SmallChange = 1;
            track3.Value = 30;
            this.Controls.Add(track3);

            ChartArea area = new ChartArea();

            area.AxisX.Title = "X";
            area.AxisX.Minimum = -10;
            area.AxisX.Maximum = image.Cols + 10;
            area.AxisX.Interval = 20;

            area.AxisY.Title = "Y";
            area.AxisY.Minimum = -10;
            area.AxisY.Maximum = 255 + 50;
            area.AxisY.Interval = 20;

            var style = area.Area3DStyle;
            style.Enable3D = true;
            style.IsRightAngleAxes = true;
            style.Perspective = 20;
            style.Rotation = track.Value;
            style.Inclination = track2.Value;
            style.Perspective = track3.Value;

            chart = new Chart();
            chart.ChartAreas.Add(area);

            Random rnd = new Random();
            for (int i = 0; i < image.Rows; i++)
            {
                var series = new Series();
                series.ChartType = SeriesChartType.Point;
                series.MarkerSize = 5;
                series.MarkerStyle = MarkerStyle.Circle;
                series.BorderColor = Color.Tomato;
                //series.Color = Color.Tomato;
                chart.Series.Add(series);

                for (int x = 0; x <= image.Cols; x++)
                {
                    var value = image.At<byte>(i, x);
                    series.Points.AddXY(x, value);
                }
            }

            area.Position = new ElementPosition(0f, 0f, 100f, 90f);
            chart.Dock = DockStyle.Fill;
            this.Controls.Add(chart);

            track.ValueChanged += (s, e) =>
            {
                this.chart.ChartAreas[0].Area3DStyle.Rotation = ((TrackBar)s).Value;
            };
            track2.ValueChanged += (s, e) =>
            {
                this.chart.ChartAreas[0].Area3DStyle.Inclination = ((TrackBar)s).Value;
            };
            track3.ValueChanged += (s, e) =>
            {
                this.chart.ChartAreas[0].Area3DStyle.Perspective = ((TrackBar)s).Value;
            };
        }
    }
}

image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# - SendInput(Windows API)によるキー入力のサンプル(32/64bit対応)

参考サイト

ソースコード

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

class MainForm : Form
{
    TextBox txt;

    MainForm()
    {
        Button btn = new Button(){
            Size = new Size(100,30),
            Text = "Test",
        };
        btn.Click += (s,e)=>{
            SendInputKeyPressAndRelease(Keys.Tab);
            SendInputKeyPressAndRelease(Keys.A);
        };
        Controls.Add(btn);


        txt = new TextBox(){
            Location = new Point(0, 30),
            Size = new Size(100,30),
            Text = "Test",
        };
        Controls.Add(txt);
    }

    private static class NativeMethods
    {
        [DllImport("user32.dll", SetLastError = true)]
        public extern static void SendInput(int nInputs, Input[] pInputs, int cbsize);

        [DllImport("user32.dll", EntryPoint = "MapVirtualKeyA")]
        public extern static int MapVirtualKey(int wCode, int wMapType);

        //[DllImport("user32.dll", SetLastError = true)]
        //public extern static IntPtr GetMessageExtraInfo();
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct MouseInput
    {
        public int X;
        public int Y;
        public int Data;
        public int Flags;
        public int Time;
        public IntPtr ExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct KeyboardInput
    {
        public short VirtualKey;
        public short ScanCode;
        public int Flags;
        public int Time;
        public IntPtr ExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct HardwareInput
    {
        public int uMsg;
        public short wParamL;
        public short wParamH;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct Input
    {
        public int Type;
        public InputUnion ui;
    }

    [StructLayout(LayoutKind.Explicit)]
    struct InputUnion
    {
        [FieldOffset(0)]
        public MouseInput Mouse;
        [FieldOffset(0)]
        public KeyboardInput Keyboard;
        [FieldOffset(0)]
        public HardwareInput Hardware;
    }

    private const int KEYEVENTF_EXTENDEDKEY = 0x0001;
    private const int KEYEVENTF_KEYUP = 0x0002;
    private const int KEYEVENTF_SCANCODE = 0x0008;
    private const int KEYEVENTF_UNICODE = 0x0004;

    private const int MAPVK_VK_TO_VSC = 0;
    // private const int MAPVK_VSC_TO_VK = 1;

    void SendKeyInput()
    {
        BeginInvoke(
            (MethodInvoker)delegate(){
                SendInputKeyPressAndRelease(Keys.Tab);
            }
        );
    }

    private static void SendInputKeyPressAndRelease(Keys key)
    {
        Input[] inputs = new Input[2];

        int vsc = NativeMethods.MapVirtualKey((int)key, MAPVK_VK_TO_VSC);

        inputs[0] = new Input();
        inputs[0].Type = 1; // KeyBoard = 1
        inputs[0].ui.Keyboard.VirtualKey = (short)key;
        inputs[0].ui.Keyboard.ScanCode = (short)vsc;
        inputs[0].ui.Keyboard.Flags = 0;
        inputs[0].ui.Keyboard.Time = 0;
        inputs[0].ui.Keyboard.ExtraInfo = IntPtr.Zero;

        inputs[1] = new Input();
        inputs[1].Type = 1; // KeyBoard = 1
        inputs[1].ui.Keyboard.VirtualKey = (short)key;
        inputs[1].ui.Keyboard.ScanCode = (short)vsc;
        inputs[1].ui.Keyboard.Flags = KEYEVENTF_KEYUP;
        inputs[1].ui.Keyboard.Time = 0;
        inputs[1].ui.Keyboard.ExtraInfo = IntPtr.Zero;

        NativeMethods.SendInput(inputs.Length, inputs, Marshal.SizeOf(inputs[0]));
    }


    [STAThread]
    static void Main(string[] args)
    {
        Application.Run(new MainForm());
    }
}

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む