C#,WPF,ソフトウェア開発

1次元配列であれば、DataGridにBindするだけで表示されるが、
クラスや構造体を用いた多次元配列をDataGridにBindする場合、
{Binding [プロパティ名]} が {Binding Path=[プロパティ名].[プロパティ名]} に変わる。

CSファイル

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // 生徒配列
        List<Student> student = new List<Student>();

        student.Add(new Student(1, "塩沢 美智子", new Test(50, 60, 40, 50)));
        student.Add(new Student(2, "志村 薫", new Test(75, 56, 97, 30)));
        student.Add(new Student(3, "石崎 なつみ", new Test(75, 21, 26, 34)));
        student.Add(new Student(3, "島袋 ジョージ", new Test(77, 96, 64, 37)));
        student.Add(new Student(3, "森田 サンタマリア", new Test(99, 79, 61, 74)));

        grdData.DataContext = student;
    }
}

/// <summary>
/// 生徒クラス
/// </summary>
public class Student
{
    // 出席番号
    public int no { get; }

    // 名前
    public string name { get; }

    // テスト結果
    public Test test { get; }

    public Student(int _no, string _name, Test _test)
    {
        this.no = _no;
        this.name = _name;
        this.test = _test;
    }
}

/// <summary>
/// テスト結果クラス
/// </summary>
public class Test
{
    // 国語
    public int kokugo { get; }

    // 算数
    public int sansu { get; }

    // 理科
    public int rika { get; }

    // 社会
    public int syakai { get; }

    public Test(int _kokugo, int _sansu, int _rika, int _syakai)
    {
        this.kokugo = _kokugo;
        this.sansu = _sansu;
        this.rika = _rika;
        this.syakai = _syakai;
    }
}

XAMLファイル

<Window x:Class="WpfApp1.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:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid x:Name="grdData" Margin="10" ItemsSource="{Binding}" AutoGenerateColumns="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="出席番号" Binding="{Binding no}" />
                <DataGridTextColumn Header="名前" Binding="{Binding name}" />
                <DataGridTextColumn Header="国語" Binding="{Binding Path=test.kokugo}" />
                <DataGridTextColumn Header="算数" Binding="{Binding Path=test.sansu}" />
                <DataGridTextColumn Header="理科" Binding="{Binding Path=test.rika}" />
                <DataGridTextColumn Header="社会" Binding="{Binding Path=test.syakai}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

C#,ソフトウェア開発

WebBrowserがサイトの読み込みを完了すると、DocumentCompletedイベントが発生するが、
これはサイトのデータを読み込み完了したタイミングである。

読み込み完了後に実行されるJavaScript等で、サイトが変化した後の内容を取得したい場合は
下記のように、内容を比較し、変化が無くなるまで待機する必要がある。
(常時どこかしらが更新されるようなサイトの場合、無限ループに陥るので注意。)

C#,ソフトウェア開発

Html Agility Pack を使用すると、HTMLタグがオブジェクトのように扱え、
簡単に解析を行うことができる。

C#,ソフトウェア開発

await/async等の別スレッドからコントロールのプロパティを変更しようとすると、
コントロールの描画を行っているスレッドと異なる為、エラーとなる。

InvokeRequired で異なるスレッドからの呼び出しかを判定し、
異なる場合は Invoke で再呼び出しをすることで解決する。

C#,ソフトウェア開発

Enum型をループで回すには、Enum.GetNames もしくは Enum.GetValues を用いる。
また、数値→Enum型はEnum型にキャストを、
Enum型→数値はInt型にキャストを、
Enum型→名前はToStringを用いる。

C#,ソフトウェア開発

Taskの配列を作成し、Task.WhenAllを呼ぶことで、
複数のTaskを同時に実行し、全てのTaskの処理が終了するまで待機する。

C#,ソフトウェア開発

FormBorderStyle.Noneのフォームは、枠がなくなると同時に、周囲の影も消えてしまう。

下記コードを適用することで、Aeroの影を復活することができる。

    public partial class Form1 : Form
    {
        #region Win32API

        [StructLayout(LayoutKind.Sequential)]
        private struct MARGINS
        {
            public int leftWidth;
            public int rightWidth;
            public int topHeight;
            public int bottomHeight;
        }

        [DllImport("dwmapi.dll")]
        private static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);

        private enum DWMWINDOWATTRIBUTE : uint
        {
            NCRenderingEnabled = 1,
            NCRenderingPolicy,
            TransitionsForceDisabled,
            AllowNCPaint,
            CaptionButtonBounds,
            NonClientRtlLayout,
            ForceIconicRepresentation,
            Flip3DPolicy,
            ExtendedFrameBounds,
            HasIconicBitmap,
            DisallowPeek,
            ExcludedFromPeek,
            Cloak,
            Cloaked,
            FreezeRepresentation
        }

        [DllImport("dwmapi.dll", PreserveSig = true)]
        private static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attr, ref int attrValue, int attrSize);

        [DllImport("dwmapi.dll")]
        private static extern int DwmIsCompositionEnabled(out bool enabled);

        const int WM_NCPAINT = 0x85;


        #endregion


        public Form1()
        {
            InitializeComponent();

            this.FormBorderStyle = FormBorderStyle.None;
        }

        [System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_NCPAINT:

                    // デザイン時は処理しない
                    if (!DesignMode)
                    {
                        // Vista以降のOSのみ
                        if (Environment.OSVersion.Version.Major >= 6)
                        {
                            // Aeroが使用可能か確認
                            bool bolAeroEnabled = false;
                            DwmIsCompositionEnabled(out bolAeroEnabled);

                            if (bolAeroEnabled == true)
                            {
                                int intAttrValue = 2;
                                int intAttrSize = sizeof(int);
                                DwmSetWindowAttribute(this.Handle, DWMWINDOWATTRIBUTE.NCRenderingPolicy, ref intAttrValue, intAttrSize);

                                MARGINS objMargins = new MARGINS()
                                {
                                    leftWidth = 1,
                                    rightWidth = 1,
                                    topHeight = 1,
                                    bottomHeight = 1
                                };

                                DwmExtendFrameIntoClientArea(this.Handle, ref objMargins);
                            }
                        }
                    }

                    break;
            }
            base.WndProc(ref m);
        }
    }

 

 

余談:WPFの場合は、GlowWindowを使うと綺麗にできる。

C#,ソフトウェア開発

MouseDown・MouseMove・MouseDown等のマウスイベントは、各コントロールで発生する為、
親フォームで一括して受信するには、明示的にイベントハンドラを設定する必要がある。

        public Form1()
        {
            InitializeComponent();

            // 親フォームのマウスイベントハンドラの割り当て
            SetEventHandler(this);
        }

        /// <summary>
        /// 子コントロールにMouseイベントハンドラを設定(再帰)
        /// </summary>
        private void SetEventHandler(Control objControl)
        {
            // イベントの設定
            // (親フォームにはすでにデザイナでマウスのイベントハンドラが割り当ててあるので除外)
            if(objControl != this)
            {
                objControl.MouseDown += (sender, e) => this.OnMouseDown(e);
                objControl.MouseMove += (sender, e) => this.OnMouseMove(e);
                objControl.MouseUp += (sender, e) => this.OnMouseUp(e);
            }

            // さらに子コントロールを検出する
            if(objControl.Controls.Count > 0)
            {
                foreach(Control objChildControl in objControl.Controls)
                {
                    SetEventHandler(objChildControl);
                }
            }
        }

        #region FormのMouseイベント受信部

        private void Form1_MouseDown(object sender, MouseEventArgs e)
        {
            // senderは常にFormだが、eの座標は各コントロールを基準とした座標が入る為、
            // スクリーン座標からクライアント座標を計算すること
            Point pntScreen = Control.MousePosition;
            Point pntForm = this.PointToClient(pntScreen);

            this.Text = "X=" + pntForm.X + " Y=" + pntForm.Y;
        }

        private void Form1_MouseMove(object sender, MouseEventArgs e)
        {
            Point pntScreen = Control.MousePosition;
            Point pntForm = this.PointToClient(pntScreen);

            this.Text = "X=" + pntForm.X + " Y=" + pntForm.Y;
        }

        private void Form1_MouseUp(object sender, MouseEventArgs e)
        {
            Point pntScreen = Control.MousePosition;
            Point pntForm = this.PointToClient(pntScreen);

            this.Text = "X=" + pntForm.X + " Y=" + pntForm.Y;
        }

        #endregion

 

キーボードのイベントも各コントロールで発生するが、
こちらは親フォームでイベントを発生させるプロパティが用意されている。

            // 子・孫問わず、すべてのコントロールで発生したキーイベントが親フォームで取得できる
            this.KeyPreview = true;

 

C#,ソフトウェア開発

VisualStudioのデザインモードを判定する方法として、DesignModeプロパティがあるが、
継承コントロールやユーザコントロールの場合、正しく値が取得できない。

EndInit後に全ての親コントロールのDesignModeを確認することで、
デザインモードの判定を行う。

internal class uctlControl : PictureBox, ISupportInitialize // ISupportInitializeを継承し、EndInitイベントを受信する
{
        /// <summary>
        /// Init後処理
        /// </summary>
        public void EndInit()
        {
            // 自身のデザインモードを取得
            bool bolDesignMode = base.DesignMode;

            // 親コントロールを再帰取得
            Control objParent = this.Parent;
            while (objParent != null)
            {
                ISite objSite = objParent.Site;
                if (objSite != null)
                    bolDesignMode |= objSite.DesignMode;

                objParent = objParent.Parent;
            }

            // デザインモードの場合は処理を抜ける
            if (bolDesignMode == true)
                return;

            // デザインモードではないときに行う初期化処理
        }
}