WPFのお勉強

WPFで何らかのシステムを組む際のサンプルがあまりないので自分でいろいろ勉強してみる。

とりあえずログイン画面を作ってみた。


正直、販売管理システムやワークフローシステムなどを作るのにWPFが適しているかいまだに疑問ではある。
個人的には作ってみたいが、Windowsフォームのほうが安定しているし、メンバーも集めやすいし、アニメーションにこだわる必要など皆無。カーソルの動きやフォーカスの当て方などをいろいろ用途によって変える必要があるのでMVVMの分離も一癖ある。
特殊な独自コントロールもほとんど必要ない。
趣味でやるのならこだわってみてもいいが、原価やコストなどを考えるとWPFでやるメリットを見いだせない。

とかいろいろ考えているとお勉強にならないのでとりあえず趣味の範囲で組んでみようと思う。

まずコントロールの配置のためのパネルをどうするかで悩んだ。
Gridで綺麗に分けることができればいいが、項目一つ一つにGridの列や行を定義するのはめんどくさい。項目が増えた時に変更箇所も多い。

ということで、Gridをベースとしてその上にCanvasコントロールを配置することにした。

Login.xaml

<Window x:Class="SystemSample.win00_0000"
        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:SystemSample"
        xmlns:control="clr-namespace:SystemSample.Controls"
        mc:Ignorable="d"
        Title="ログイン画面" Height="250" Width="450" Style="{StaticResource LoginWindowStyle}" WindowStartupLocation="CenterScreen">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="5"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="5"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="5"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="5"/>
        </Grid.RowDefinitions>

        <Canvas Grid.Column="1" Grid.Row="1">
            <Label x:Name="lblTitle" Content="販売管理システム" Canvas.Left="10" Canvas.Top="10" Width="412" FontSize="32" FontFamily="Arphic Kaisho4JIS" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" />
            <control:CsTextBox x:Name="txtUserID" Canvas.Left="167" Text="{Binding UserID}"   Canvas.Top="79"  Width="205" Style="{StaticResource NormalTextBox}"/>
            <control:CsTextBox x:Name="txtPass"   Canvas.Left="167" Text="{Binding PassWord}" Canvas.Top="108" Width="205" Style="{StaticResource NormalTextBox}"/>
            <Label x:Name="lblUserID" Content="ユーザーID" Canvas.Left="60" Canvas.Top="79" Width="105" Style="{StaticResource RequiredLabel}"/>
            <Label x:Name="lblPass"   Content="パスワード" Canvas.Left="60" Canvas.Top="108" Width="105" Style="{StaticResource RequiredLabel}" />
            <Button x:Name="btnLogin" Content="ログイン" Canvas.Left="280" Canvas.Top="159" Width="95" Style="{StaticResource NormalButton}" />
        </Canvas>

    </Grid>
</Window>

CsTextBoxはTextBoxを継承しただけの独自コントロール。
まだ何の制御も実装していない。
本当は数値入力のみとか半角英数字のみ入力できるようにするなど実装したいがそれはそのうちということで。

StaticResourceについてはプロジェクト内でスタイルを共通化したいので、App.xamlに定義を行っている。

・App.xaml

<Application x:Class="SystemSample.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:SystemSample"
             xmlns:controls="clr-namespace:SystemSample.Controls"
             StartupUri="win00_0000.xaml">
    <Application.Resources>


        <!--#region 色など-->

        <!-- Active状態の背景色 -->
        <SolidColorBrush x:Key="ActiveBackColor" Color="Yellow"></SolidColorBrush>
       
        <!-- テーマカラー -->
        <LinearGradientBrush x:Key="BlueGradientBrush" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#FFFFFFFF" Offset="0"/>
            <GradientStop Color="#FFC4DDFF" Offset="0.987"/>
            <GradientStop Color="#FFDEECFF" Offset="0.535"/>
        </LinearGradientBrush>
        <!--#endregion-->


        <!--#region 継承元のスタイル-->
        <!-- 継承元のフォームベースのスタイル -->
        <Style x:Key="WindowStyleBase" TargetType="Window">
            <Setter Property="FontFamily" Value="MS Gothic" />
            <Setter Property="FontSize" Value="15"/>
            <Setter Property="Background" Value="{StaticResource BlueGradientBrush}"></Setter>
            <Setter Property="WindowStyle" Value="SingleBorderWindow"></Setter>
        </Style>

        <Style x:Key="CsTextBoxStyleBase" TargetType="controls:CsTextBox">
            <Setter Property="FontFamily" Value="MS Gothic" />
            <Setter Property="FontSize" Value="15"/>
            <Setter Property="TextAlignment" Value="Left"/>
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Height" Value="25"/>


            <Style.Triggers>
               
                <!-- フォーカス時の背景色 -->
                <Trigger Property="IsFocused" Value="True">
                    <Setter Property="Background" Value="{StaticResource ActiveBackColor}"></Setter>
                </Trigger>
            </Style.Triggers>

        </Style>

        <Style x:Key="TextBlockStyleBase" TargetType="TextBlock">
            <Setter Property="FontFamily" Value="MS Gothic" />
            <Setter Property="FontSize" Value="15"/>
            <Setter Property="TextAlignment" Value="Left"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="Height" Value="25"/>
        </Style>

        <Style x:Key="LabelStyleBase" TargetType="Label">
            <Setter Property="FontFamily" Value="MS Gothic" />
            <Setter Property="FontSize" Value="15"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
            <Setter Property="BorderBrush" Value="Black"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Height" Value="25"/>
        </Style>

        <Style x:Key="ButtonStyleBase" TargetType="Button">
            <Setter Property="FontFamily" Value="MS Gothic" />
            <Setter Property="FontSize" Value="15"/>
            <Setter Property="BorderBrush" Value="Black"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Height" Value="25"/>
            <Setter Property="Background" >
                <Setter.Value>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="Gainsboro" Offset="0"/>
                    <GradientStop Color="#FF919191" Offset="1"/>
                    </LinearGradientBrush>
                </Setter.Value>
            </Setter>

           
            <Style.Triggers>

                <!-- フォーカス時の背景色 -->
                <Trigger Property="IsFocused" Value="True">
                    <Setter Property="Background" Value="{StaticResource ActiveBackColor}"></Setter>
                </Trigger>
            </Style.Triggers>

        </Style>

        <!--#endregion -->

        <!--#region 継承後のスタイル-->

        <Style x:Key="NormalLabel" TargetType="Label" BasedOn="{StaticResource LabelStyleBase}">
        </Style>
        <Style x:Key="RequiredLabel" TargetType="Label" BasedOn="{StaticResource LabelStyleBase}">
            <!--<Setter Property="Background" Value="#FFFF997B" />-->
            <Setter Property="Background" Value="#FF7BB7FF" />
        </Style>

        <Style x:Key="NormalTextBox" TargetType="controls:CsTextBox" BasedOn="{StaticResource CsTextBoxStyleBase}">
        </Style>
       
        <Style x:Key="NormalTextBlock" TargetType="TextBox" BasedOn="{StaticResource TextBlockStyleBase}">
        </Style>

        <Style x:Key="NormalButton" TargetType="Button" BasedOn="{StaticResource ButtonStyleBase}">
        </Style>

        <Style x:Key="LoginWindowStyle" TargetType="Window" BasedOn="{StaticResource WindowStyleBase}">
            <!--<Setter Property="Background">
                    <Setter.Value>
                        <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">
                            <LinearGradientBrush.GradientStops>
                                <GradientStop Offset="0.0" Color="#FFB0FFB0"/>
                                --><!--<GradientStop Offset="1.0" Color="#FFD6D6D6"/>--><!--
                                <GradientStop Offset="1.0" Color="#FFB6DAA2"/>
                            </LinearGradientBrush.GradientStops>
                        </LinearGradientBrush>
                    </Setter.Value>
            </Setter>-->
        </Style>

        <Style x:Key="MenuWindowStyle" TargetType="Window" BasedOn="{StaticResource WindowStyleBase}">
        </Style>

        <!--#endregion-->

    </Application.Resources>
</Application>

Styleを継承するにはBaseOnを指定することでできる。
そのため、各コントロールのBaseStyleを定義し、それらを継承したスタイルを個別に作成するようにしている。
また、コントロールにフォーカスがあるときにはコントロールの背景色を変更したいので、Triggerを定義している。

どこかのタイミングでプロジェクトを上げたいと思う。

ガントチャート作成日記

久しぶり更新。
前回の稼働率の計算式でやった結果うまくいったのでバージョンを更新。


<DownLoad>
https://sites.google.com/site/mmbloguserfile/wai-bufairu/ProjectManager_Beta_0.0.0.3.zip

いろいろ忙しくて、少し間が空いてしまったので少しづつ思い出しながら直していこうと思う。

ガントチャート作成日記

土日を挟んだ進捗率の計算のメモ。


下記のようなスケジュールの場合で、進捗率が7割の場合はこんな感じで進捗が求まる気がする。

 水|木|金|土|日|月|火
 □|■|■|■|■|■|□

  ・土日は稼働日から外す。
  ・稼働日3日
  ・休日2日
  ・進捗率7割
  ・タスクのWidth:100

  ① 稼働予定の日数:3日
    進捗率の7割をかけると、2.1日分の作業が進んでいる
    そのため、月曜日を過ぎたところを進捗線したい。

  ② タスクの開始日に+1日ずつしていき、休日判定を行う。
    平日の場合は、稼働日変数に+1
    休日の場合は、休日日数を+1

    稼働日日数が2.1日を超えたらストップ。
    その時の休日日数を2.1日に足しこむ
    進捗線
       (2.1日+休日日数)÷5日(稼働日+休日)×タスクのWidth

これで線が引けるのではないだろうか!
後は実際に試してみてうまくいくかな・・・・。

LINQ to Objectについて

今日はLINQ to Objectをクエリ形式で利用してGroupBy句を作成。
GroupByで集計単位を指定しinto句でGroupオブジェクトを生成。

SQLと違ってまとまったGroupオブジェクトをループで取得できるので帳票を作りるときや総合計を出しつつ、明細を表示する場合などに便利。

WPFのDataGridでGroupingしたりTreeListViewなどで表示するといい感じに見える。

販売管理システムとかだと請求書などで鏡の部分を作成しつつ、明細の部分も表示するようなときに使いやすい。

    Private Sub btnGroup_Click(sender As Object, e As EventArgs) Handles btnGroup.Click
        Dim dtUri As New DataTable
        Dim drUriSample As DataRow

        ' ▼ 売上データ
        dtUri.Columns.Add(New DataColumn("売上No", GetType(Integer)))
        dtUri.Columns.Add(New DataColumn("売上日", GetType(String)))
        dtUri.Columns.Add(New DataColumn("得意先CD", GetType(String)))
        dtUri.Columns.Add(New DataColumn("商品名", GetType(String)))
        dtUri.Columns.Add(New DataColumn("売上数量", GetType(Decimal)))
        dtUri.Columns.Add(New DataColumn("売上金額", GetType(Decimal)))

        '   テストデータ
        drUriSample = dtUri.NewRow
        drUriSample("売上No") = 1
        drUriSample("売上日") = "20171031"
        drUriSample("得意先CD") = "00001"
        drUriSample("商品名") = "パソコン"
        drUriSample("売上数量") = 1
        drUriSample("売上金額") = 150000
        dtUri.Rows.Add(drUriSample)

        drUriSample = dtUri.NewRow
        drUriSample("売上No") = 2
        drUriSample("売上日") = "20171031"
        drUriSample("得意先CD") = "00002"
        drUriSample("商品名") = "マウス"
        drUriSample("売上数量") = 1
        drUriSample("売上金額") = 3000
        dtUri.Rows.Add(drUriSample)

        drUriSample = dtUri.NewRow
        drUriSample("売上No") = 1
        drUriSample("売上日") = "20171031"
        drUriSample("得意先CD") = "00001"
        drUriSample("商品名") = "モニター"
        drUriSample("売上数量") = 2
        drUriSample("売上金額") = 30000
        dtUri.Rows.Add(drUriSample)

        Dim lnqGroup = From drUri In dtUri.AsEnumerable
                       Group By TokCD = drUri("得意先CD")
                           Into grp = Group
                       Select New With {
                             .得意先CD = TokCD,
                             .grp = grp
                             }



        Dim strMsg As String = ""

        For Each lnqdr In lnqGroup
            strMsg += "得意先CD:" & lnqdr.得意先CD & vbCrLf
            strMsg += "合計金額:" & lnqdr.grp.Sum(Function(drChild) drChild("売上金額")) & vbCrLf

            'まとまった単位
            For Each drGrpChild In lnqdr.grp
                strMsg += "商品名:" & drGrpChild("商品名") & " 数量:" & drGrpChild("売上数量") & " 金額:" & drGrpChild("売上金額") & vbCrLf
            Next

            MsgBox(strMsg)
            strMsg = ""

        Next

    End Sub


ガントチャート作成日記

ガントチャートがいい感じに出来上がってきた。
マウスカーソルのある個所に線を引いたり、タスクの分割処理を実装したりしてみた。

あとは速度が気になるのと、進捗率が土日を挟むと上手く計算できないのが目立つんで次はそのあたりを改善しようと思う。

プロジェクトごとに土日は進捗率の計算から除くとか任意の日付を除けるようにするとかできるようにしようと思う。

とりあえずベータ版を公開してみる。

https://sites.google.com/site/mmbloguserfile/wai-bufairu/ProjectManager_Beta_0.0.0.2.zip

ガントチャート作成日記

今日はガントチャートとツリーリストビューでそれぞれ選択行がわかるように枠線をつけたかったので、いろいろ頑張ってようやく実装できた。


ガントチャート側はGridコントロールで実装している。それぞれの行に専用のDataTemplateを切り替えるようにして交互色を設定している。土日の列の色を変えるためにもDataTemplateを設定している。Panel.Zindexを変更することで、行の上に列のテンプレートが表示されるようにしている。

選択行の枠線を変えるために背景色が透明の行を作成し、Panel.Zindexをほかの内容より大きくすることで一番上に行が表示されるようにして後はイベントを頑張って設定した。

Xaml側

        <!-- 行の背景を描画するDataTemplate -->
        <DataTemplate x:Key="RowLineTypeDataTemplate" >
            <Rectangle  Stroke="Black" StrokeThickness="0.25" Fill="Transparent"  />
            <!--StrokeDashArray="10 10" を設定すれば点線になる-->
        </DataTemplate>

        <!-- 交互行の背景を描画するDataTemplate -->
        <DataTemplate x:Key="AlternateRowLineTypeDataTemplate" >
            <!--<Rectangle Fill="#FFEFF9EC" />-->
            <Rectangle Fill="#FFF0F0F0" StrokeThickness="0.25" />
        </DataTemplate>

        <!-- 選択行の見た目を変更するためにZindexを列のZIndexより大きいものを作成 -->
        <DataTemplate x:Key="StyleRowLineDataTemplate" >
            <Rectangle Fill="Transparent" MouseEnter="rectBack_MouseEnter" MouseLeave="rectBack_MouseLeave"/>
        </DataTemplate>

Cs側

            // 行の背景色を辺くするためのタスクをインスタンス化して追加
            for (int i = 0; i < _alltask.Count; i++)
            {
                // タスクの位置を設定
                _alltask[i].Cell.RowIndex = i;

                if (i % 2 == 0)
                {
                    // 列罫線を描画するためのタスクを新たにインスタンス化して追加
                    _GanttGridCells.Add(new GanttCell(GanttCell.TemplateType.RowLine) { ZIndex = 0, ColumnIndex = 0, ColumnSpan = ColumnCount, RowIndex = i, RowSpan = 1 });
                }
                else {
                    // 列罫線を描画するためのタスクを新たにインスタンス化して追加
                    _GanttGridCells.Add(new GanttCell(GanttCell.TemplateType.AlternateRowLine) { ZIndex = 0, ColumnIndex = 0, ColumnSpan = ColumnCount, RowIndex = i, RowSpan = 1 });
                }

                // 選択行の見た目を変更するためにZindexを列のZIndexより大きいものを作成
                _GanttGridCells.Add(new GanttCell(GanttCell.TemplateType.StyleRowLine) { ZIndex = 30, ColumnIndex = 0, ColumnSpan = ColumnCount, RowIndex = i, RowSpan = 1 });

            }

            // 土日の列の背景色を変更するためのタスクを生成
            for (int i = 0; i < ColumnCount; i++)
            {
                // 土日それぞれに合わせてTemplateTypeプロパティに値を設定するとTemplateSelecterが各値によってDataTemplateのKye名を生成し、描画に利用するDataTemplateを選択する
                if (ProjectStartDay.AddDays(i).ToString("ddd") == "土")
                {
                    _GanttGridCells.Add(new GanttCell(GanttCell.TemplateType.ColumnLineBlue) { ZIndex = 10, ColumnIndex = i, ColumnSpan = 1, RowIndex = 0, RowSpan = this.RowCount });
                }
                else if (ProjectStartDay.AddDays(i).ToString("ddd") == "日")
                {
                    _GanttGridCells.Add(new GanttCell(GanttCell.TemplateType.ColumnLineOrange) { ZIndex = 10, ColumnIndex = i, ColumnSpan = 1, RowIndex = 0, RowSpan = this.RowCount });
                }
                else if (Common.Holiday(ProjectStartDay.AddDays(i)).holiday != Common.HolidayInfo.HOLIDAY.WEEKDAY)
                {
                    // 平日以外(祝日・振替休日)の場合は日と同じ色を設定
                    _GanttGridCells.Add(new GanttCell(GanttCell.TemplateType.ColumnLineOrange) { ZIndex = 10, ColumnIndex = i, ColumnSpan = 1, RowIndex = 0, RowSpan = this.RowCount });
                }
                else
                {
                    _GanttGridCells.Add(new GanttCell(GanttCell.TemplateType.ColumnLine) { ZIndex = 10, ColumnIndex = i, ColumnSpan = 1, RowIndex = 0, RowSpan = this.RowCount });
                }
            }

いずれはこの辺りをまとめて資料にしようと思うが、試行錯誤しながらだからあとで作成日記を見ながらまとめようかなーと思う。

あとは逆にTreeListView側で選択した行の枠線をガントチャート側に反映させようと思う。

PowerShellでEdgeを自動化(インストール不要。参考:郵便追跡サービス自動操作)

1.経緯について  RPAのソフトをインストールできないので、これまでVBSでCreateObjectでブラウザの自動操作をすることがたまにあった。 ※いざというときの手札として持っているだけで安心感が段違い  見た目上IEがインストールされていなくても、CreateObject...