ラベル ProjectManager の投稿を表示しています。 すべての投稿を表示
ラベル ProjectManager の投稿を表示しています。 すべての投稿を表示

WPFのコンボボックスの背景色について

TreeListView上でコンボボックスを利用するときにフォーカスのあるコンボボックスの色をイベントで変えようとしたけど上手くいかなかった。

色々と調べているとXaml側のテンプレートをいじる必要があるみたいだったので、いろいろとやってみた。

とりあえず、コンボボックスをデザイナに配置し、コンボボックスを選択した状態で「右クリック」->「テンプレートの編集」ー>「コピーして編集」を選択する。


リソースの名前は後でも変更できるのと、自動で作成されるXAMLの場所も移動できるので一旦は初期のままにして「OK」を押す。


ものすごく長いリソースがWindow.Resourceの中に作成される


このリソースの中でBackgroundを管理している箇所のStyleを変更すれば変えることができる。具体的には下記の画像の部分。
※編集可能なコンボボックスを使っているときは「ComboBox.Static.Editable.Background」を変更すればよいと思われる。




今回の場合は、フォーカスの移動によって背景色を変更したかったので、「ComboBox.Static.Background」、「ComboBox.MouseOver.Background」をデザイナ上は「Transparent」に設定しておいて、プログラムからGotFocusイベントとLostFocusイベントを利用して変更している。

=== ソース抜粋1 ===
            // 選択中の背景色を設定する
            this.GotFocus += SetSelectionBackgroundColor;
            // 選択中の背景色を初期値にもどす
            this.LostFocus += ClearSelectionBackgroundColor;
==============

=== ソース抜粋2 ===

        /// <summary>
        /// フォーカス取得時の背景色
        /// </summary>
        public Brush SelectionBackgroundColor = new SolidColorBrush(Color.FromArgb(255, 250, 250, 0));

        /// <summary>
        /// 選択中のコンボボックスの背景色を設定
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SetSelectionBackgroundColor(object sender, RoutedEventArgs e)
        {
            var grid = this.Template.FindName("templateRoot", this) as Grid;
            if (grid != null)
                grid.Background = SelectionBackgroundColor;
        }

        /// <summary>
        /// 選択中のコンボボックスの背景色を初期化
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ClearSelectionBackgroundColor(object sender, RoutedEventArgs e)
        {
            var grid = this.Template.FindName("templateRoot", this) as Grid;
            if (grid != null)
                grid.Background = null;
        }

=============
※templateRootは先ほどデザイナから自動生成されたXAMLの中で「ComboBoxTemplate」というControlTemplateがあるが、その中のGridのこと。
 なのでコンボボックスを構成する各コントロールを配置している元のGridの背景色を変更しているだけ。逆に言うとコンボボックスをどうやって作っているのかがControlTemplateを見ればわかる。


後はこの作成したリソースをコンボボックスのStyleに適用すればよい。

=== ソース抜粋3 ===
<ComboBox  ItemsSource="{Binding PriorityList, Mode=TwoWay}"
DisplayMemberPath="Name" SelectedValuePath="PriorityNo"
SelectedValue="{Binding SelectedPriorityNo}" Style="{StaticResource ComboBoxStyle1}">

=============

Window.Resourceに配置すると他のWindowでは利用できなくなるので、App.Xamlに配置すれば他のWindowから利用できる。
App.Xamlが長くなりすぎるのであれば、別のリソースディクショナリに作成して、App.Xamlは「ResourceDictionary.MergedDictionaries」でリソースの指定をすればよい。

<適用した例>


ガントチャート作成日記

久しぶりに更新。スマホのゲームにはまってしまって、なかなかこっちに時間が割けなかった。
とりあえず、DataGridではなく、独自で一覧を作成する方向で作り続けてみようと思う。

チャートの部分と同様にGridコントロールに独自クラスをバインドする形式にして、DataTemplateSelecterでDataTemplateを切り替えることでテキストボックスや行部分を表示している。
Thumbコントロールを使ってリサイズも可能なようにしておいて、セルのマージができないのを補ってみた。

工夫したところとしては処理速度を補うために、バインド用のリストとテキストボックス用のリストと行を表現するためのリストを分けた。

<ソース抜粋(GanntGridViewModel.cs)>
   // 左部のタスク一覧を表現するためのセルを格納する各リスト
   this.GanttGridTaskViewPartsRowCells = new ObservableCollection<GanttCell>();
   this.GanttGridTaskViewPartsTaskCells = new ObservableCollection<GanttCell>();
   this.GanttGridTaskViewCells = new ObservableCollection<GanttCell>();

GanttGridTaskViewPartsRowCellsとGanttGridTaskViewPartsTaskCellsにGanntCellクラスをインスタンス化してそれぞれに追加したのちに、this.GanttGridTaskViewCellsに両方のリストの内容をAddしてバインディングに利用する。
そうすることでタスクだけを変更したい時にはGanttGridTaskViewCellsをループするのではなく、GanttGridTaskViewPartsTaskCellsをループすることで対応できる。

問題としては、タスクを追加したときにリストへの追加ミスがないようにしないといけないところと、その際に他の問題が起きないか、というところ・・・。

これは試してみないとわからないので、次回やってみようと思う。

<DownLoad>

ガントチャート作成日記

行や列を見やすくするために別のDataTemplateを表現するようにした。



バインドする全体のGanttCellを格納するGanttGridCellsと列や行を分けて格納しておく
GanttGridRowCells やGanttGridColumnCells を新たに追加した。

public GanttGridViewModel()
{
    this.GanttGridCells = new ObservableCollection<GanttCell>();
    this.GanttGridRowCells = new ObservableCollection<GanttCell>();
    this.GanttGridColumnCells = new ObservableCollection<GanttCell>();
    this.GanttGridTaskCells = new ObservableCollection<GanttCell>();

    GanttHeader h = new GanttHeader();
 
    h.ColumnCount = this.ColumnCount;
    h.ColumnWidth = this.ColumnWidth;

    ObservableCollection<GanttHeaderCell> topCells = new ObservableCollection<GanttHeaderCell>();
    ObservableCollection<GanttHeaderCell> bottomCells = new ObservableCollection<GanttHeaderCell>();

    int colSpanCnt = 0;
    for (int i = 0; i <= this.ColumnCount; i++)
    {
        DateTime d = DateTime.Now.AddDays(i);
        if (d.ToString("dd") == "01" || i == 0)
        {
            int iDaysInMonth = DateTime.DaysInMonth(d.Year, d.Month);
            colSpanCnt = iDaysInMonth - int.Parse(d.ToString("dd")) + 1;
            topCells.Add(new GanttHeaderCell() { ColumnIndex = i, RowIndex = 0, ColumnSpan = colSpanCnt, Date = d });
        }
        bottomCells.Add(new GanttHeaderCell() { ColumnIndex = i, RowIndex = 0, Date = d });
    }

    h.BottomHeaderCells = bottomCells;
    h.TopHeaderCells = topCells;

    TimeSpan ts = DateTime.Now.AddYears(1) - DateTime.Now;
    h.ColumnCount = ts.Days;

    this.Header = h;


    // タスクを表現するセルを追加
    for (int i = 0; i < 500; i++)
    {
        this.GanttGridTaskCells.Add(new GanttCell() { RowIndex = i, RowSpan = 1, ColumnIndex = i, ColumnSpan = 2, DataTemplateType = GanttCell.TemplateType.Task });
    }

    // タスクの数だけ行を表現するセルを追加
    for (int i = 0; i < this.GanttGridTaskCells.Count - 1; i++)
    {
        if (i % 2 == 0)
        {
            this.GanttGridRowCells.Add(new GanttCell() { RowIndex = i, RowSpan = 1, ColumnIndex = 0, ColumnSpan = 365, DataTemplateType = GanttCell.TemplateType.RowLine });
        }
        else
        {
            this.GanttGridRowCells.Add(new GanttCell() { RowIndex = i, RowSpan = 1, ColumnIndex = 0, ColumnSpan = 365, DataTemplateType = GanttCell.TemplateType.AlternateRowLine });
        }
    }

    // 列の数だけ列を表現するセルを追加
    foreach (var item in this.Header.BottomHeaderCells)
    {
        string week = item.Date.ToString("ddd");
        GanttCell.TemplateType type = GanttCell.TemplateType.ColumnLine;
        if (week == "日")
        {
            type = GanttCell.TemplateType.ColumnLineOrange;
        }
        else if (week == "土")
        {
            type = GanttCell.TemplateType.ColumnLineBlue;
        }
        else if (Common.Holiday(item.Date).holiday != Common.HolidayInfo.HOLIDAY.WEEKDAY)
        {
            // 平日以外(祝日・振替休日)の場合は日と同じ色を設定
            type = GanttCell.TemplateType.ColumnLineOrange;
        }

        this.GanttGridColumnCells.Add(new GanttCell() { RowIndex = 0, ColumnIndex = item.ColumnIndex, ColumnSpan = 1, RowSpan = this.GanttGridTaskCells.Count, DataTemplateType = type });
    }

    //バインディングするためのセルを作成する
    foreach (var item in this.GanttGridColumnCells) { this.GanttGridCells.Add(item); }
    foreach (var item in this.GanttGridRowCells) { this.GanttGridCells.Add(item); }
    foreach (var item in this.GanttGridTaskCells) { this.GanttGridCells.Add(item); }

}

バインドされているのはGanttGridCellsプロパティだが見た目を変更するためにDataTemplateSelectorを継承したGanttGridTemplateSelecterを作成している。

GanttGridTemplateSelecterはGanttGrid.xamlで利用している
ItemsControlのItemTemplateSelectorに設定している。
各DataTemplateはUserControlのResourcesに定義しておいて、GanttGridCellsがバインドされたらバインドされているGanttCellのDataTemplateTypeプロパティに応じて利用するDateTemplateを切り替えている。

詳細はソースを参照。
前回アップしたものとWinMergeなどで比較するとわかりやすい。

ついでにDataGridにGanttGridTaskCellsをバインドすることで、ガントチャートっぽくなった。DataGridをスクロールしたときに同期するようにしてみた。
ガントチャート側をスクロールしたときにもスクロールするようにしたりする必要はある。

横に出すのをDataGridのような一覧にするか、移動、リサイズ可能なTextBoxを作成して、それをバインドするか悩むところ。
せっかくなんでいろんなソフトを作ってみようかと思う。

DataGridで階層を表現できると一番いいけど・・・。
セルのマージは難しそうだし、悩ましい。

<DownLoad>

ガントチャート作成日記

タスクを表現してみた。
タスクの移動とサイズをドラッグで変更できるようにプログラムを修正してみた。

リサイズと移動のためにThumbコントロールを利用している。
Gridの幅を細かくしたり、Canvasコントロールに配置すれば、ユーザーが自由にコントロールを配置できる。

<GanttGrid.xaml>に修正を行う。
明細にタスクを表現するためにItemsControlのスタイルを追加する。

                <!-- タスクを表現 -->
                <ItemsControl.ItemContainerStyle>
                    <Style>
                        <Setter Property="Grid.Column" Value="{Binding Path=ColumnIndex}"/>
                        <Setter Property="Grid.Row" Value="{Binding Path=RowIndex}"/>
                        <Setter Property="Grid.ColumnSpan" Value="{Binding Path=ColumnSpan}"/>
                    </Style>
                </ItemsControl.ItemContainerStyle>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <!--ドラッグ対象のオブジェクトを定義する-->
                        <Border BorderBrush="Black" BorderThickness="0.25"  Height="18">
                            <Grid Height="18">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="3"/>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="3"/>
                                </Grid.ColumnDefinitions>
                                <Thumb Name="LeftThumb" DragDelta="LeftThumb_DragDelta" QueryCursor="LeftThumb_QueryCursor" Grid.Column="0" >
                                    <Thumb.Template>
                                        <ControlTemplate>
                                            <Grid>
                                                <TextBlock Background="#FFF9CC95" Text="" />
                                            </Grid>
                                        </ControlTemplate>
                                    </Thumb.Template>
                                </Thumb>
                                <Thumb Name="CenterThumb" Grid.Column="1" DragDelta="Thumb_DragDelta" QueryCursor="CenterThumb_QueryCursor">
                                    <Thumb.Template>
                                        <ControlTemplate>
                                            <Grid>
                                                <TextBlock Background="#FFF9CC95" Text="サンプル"  FontFamily="MS Gothic"/>
                                            </Grid>
                                        </ControlTemplate>
                                    </Thumb.Template>
                                </Thumb>
                                <Thumb Name="RightThumb" Grid.Column="2" Width="5" Background="red" DragDelta="RightThumb_DragDelta" QueryCursor="RightThumb_QueryCursor">
                                    <Thumb.Template>
                                        <ControlTemplate>
                                            <Grid>
                                                <TextBlock Background="#FFF9CC95" Text="" />
                                            </Grid>
                                        </ControlTemplate>
                                    </Thumb.Template>
                                </Thumb>

                            </Grid>
                        </Border>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                
<GanntGrid.cs>
下記のソースコードを追加
26で割っているのはColumnWidthの長さ。
移動量をColumnWidthで割ってColumnIndexを変更量を計算している

        //Thumbコントロールのドラッグイベント処理
        private void Thumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            var thumb = sender as Thumb;
            if (thumb == null) return;

            var cell = thumb.DataContext as GanttCell;

            int move = (int)e.HorizontalChange / 26;
            Console.WriteLine((e.HorizontalChange / 26).ToString() + ";" + move.ToString());
            cell.ColumnIndex = cell.ColumnIndex + (int)move;

        }

        private void LeftThumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            Console.WriteLine((e.HorizontalChange / 26).ToString() + ";" + "LeftThumb_DragDelta");

            var thumb = sender as Thumb;
            if (thumb == null) return;

            var cell = thumb.DataContext as GanttCell;

            int move = (int)e.HorizontalChange / 26;
            cell.ColumnIndex = cell.ColumnIndex + (int)move;
            cell.ColumnSpan = cell.ColumnSpan + (int)move * -1;

            Console.WriteLine((move).ToString());

        }

        private void RightThumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            Console.WriteLine((e.HorizontalChange / 26).ToString() + ";" + "RightThumb_DragDelta");

            var thumb = sender as Thumb;
            if (thumb == null) return;

            var cell = thumb.DataContext as GanttCell;

            int move = (int)e.HorizontalChange / 26;
            cell.ColumnSpan = cell.ColumnSpan + (int)move;
        }

        private void LeftThumb_QueryCursor(object sender, QueryCursorEventArgs e)
        {
            e.Cursor = Cursors.SizeWE;
            e.Handled = true;
        }

        private void CenterThumb_QueryCursor(object sender, QueryCursorEventArgs e)
        {
            e.Cursor = Cursors.Cross;
            e.Handled = true;
        }

        private void RightThumb_QueryCursor(object sender, QueryCursorEventArgs e)
        {
            e.Cursor = Cursors.SizeWE;
            e.Handled = true;
        }





<DownLoad>

ガントチャート作成日記

ヘッダー部を作成するために大きく下記の内容を対応していく。

 ・大きく分けて年月部分と日付部分をセルを表現する
 ・ヘッダーを表示するためのGanttHeaderクラスを作成し、ViewModelにHeaderプロパティを追加
 ・年月部分を表現するためにユーザーコントロールを作成する
 ・日付部分を表現するためにDataTemplateを利用する
  ※ユーザーコントロールを大量に作成すると遅くなるらしいので。

①の部分はユーザーコントロールで作成した。
②の部分はDataTemplateを利用して作成した。




ちょっと困ったのが日曜日と土曜日の背景色について、ValueConverterでバインディングしている日付を渡して色を変えようと思ったが色を事前にXamlに定義したものを利用しようと思うとFindResouceが利用できないので、MultiValueConverterを利用して、自分自身を渡すことで対応した。(DayOfTheWeekBackgroundConverter.cs)

細かいのはサンプルソースを参照。

ーーーーー DayOfTheWeekBackgroundConverter.cs
       public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            DateTime b = (DateTime)values[0];
            FrameworkElement callingElement = (FrameworkElement)values[1];
            LinearGradientBrush brush = null;
            string week = b.ToString("ddd");
            if (week == "日")
            {
                brush = (LinearGradientBrush)callingElement.FindResource("OrangeGradientBrush");
            }
            else if (week == "土")
            {
                brush = (LinearGradientBrush)callingElement.FindResource("BlueGradientBrush");
            }
            else if (Common.Holiday(b).holiday != Common.HolidayInfo.HOLIDAY.WEEKDAY)
            {
                // 平日以外(祝日・振替休日)の場合は日と同じ色を設定
                brush = (LinearGradientBrush)callingElement.FindResource("OrangeGradientBrush");
            }
            else
            {
                brush = (LinearGradientBrush)callingElement.FindResource("NormalGradientBrush");
            }
            return brush;
        }
ーーーーー 

ーーーーー GanttGrid.xaml 
                            <Border x:Name="DayLabelRowBorder" BorderBrush="Silver"  BorderThickness="0.25">
                                <Border.Background>
                                    <MultiBinding Converter="{StaticResource ConverterDayOfTheWeekBackground}">
                                        <Binding Path="Date"/>
                                        <Binding RelativeSource="{RelativeSource Self}"/>
                                    </MultiBinding>
                                </Border.Background>
                                
                                <Grid>
                                    <Grid.RowDefinitions>
                                        <RowDefinition Height="*"/>
                                        <RowDefinition Height="*"/>
                                    </Grid.RowDefinitions>
                                    <TextBlock x:Name="Day" Text="{Binding Date,Mode=TwoWay,StringFormat=dd}" TextAlignment="Center"  VerticalAlignment="Center" FontFamily="MS Gothic" Grid.Row="0" Padding="0" TextOptions.TextFormattingMode="Display" TextOptions.TextRenderingMode="ClearType" />
                                    <TextBlock x:Name="DayOfWeek" Text="{Binding Date,Converter={StaticResource ConverterDayOfTheWeek}}" TextAlignment="Center"  VerticalAlignment="Center" FontFamily="MS Gothic" Grid.Row="1" Padding="0" TextOptions.TextFormattingMode="Display" TextOptions.TextRenderingMode="ClearType" />
                                </Grid>

                            </Border>
ーーーーー 


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

次は、タスクを表現する部分をやろうと思う。
タスクを大量に表示してもそんなに遅くならないようにするようにしたいと思うがどこまでできるか・・・。

ガントチャート作成日記

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


<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

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

ガントチャート作成日記

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

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

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

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

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側で選択した行の枠線をガントチャート側に反映させようと思う。

ガントチャート作成日記

今日はタスクの分割機能を実装してみた。

タスクを選んで右クリックでタスクを細分化することができる。
そのため、TODOリストの組み合わせという形ではなく、大きなタスクをざっと洗い出して、その中でさらに何をすればいいのかを細かく分けていけるようにしてみた。

タスクを選んで右クリック→タスクの分割を押下することで分割される。













タスクの分割設定ボタンを押下すると設定画面で分割数やタスク名を指定することができる。

不具合のある機能を修正し、タスクの分割機能などいくつかの機能を回収した。

公開URL:https://sites.google.com/site/mmbloguserfile/wai-bufairu/ProjectManager_Beta_0.0.0.1.zip

まだまだ作成中。

ガントチャート作成日記

今日もちょっとずつガントチャートを作成してみる。
なんかいろいろとバグがあるのでゆっくりつぶしながら、今日も機能を追加。

やっぱりテストとかよりも思いついた機能を実装しているほうが楽しい。

今日追加したのは、タスクの移動をしながら水平スクロールする機能。


右クリック+スクロールの場合は、水平スクロールするように機能を修正。
その時に合わせてタスクも横に移動するようにしてみた。

ほかにも、試しに使ってたらタスクを右クリックしたら「タスクの分割」を一気にできるようにする機能とかもほしいなーと思ったりしたので実装中。
  例)
    〇〇〇作業  ← 「タスクの分割」機能を実行
    ▲▲▲作業
     ↓
    〇〇〇作業
     ∟ 〇〇の準備
     ∟ 〇〇の資料作成
     ∟ 〇〇のデモプレゼン
    ▲▲▲作業

みたいな感じで、はじめは大きな枠でタスクを作って少しずつ細かいTODOに落とし込んでいくイメージ。

ちょっとずつバグもつぶれてきたので懲りずに近々ベータ版をアップしてみようかと思う。


ガントチャート作成日記

備考があるときだけ付箋っぽい画像を表示してカーソルが当たったら備考を表示するようにしたいなーと思ったので実装してみた。
慣れて着たらWPFはやりたいと思ったことをさくっと作れるので すごいと思う。




<ユーザーコントロール>
PopUpNote.xaml

<UserControl x:Class="ProjectManager.Controls.PopUpNote"
             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:ProjectManager.Controls"
             mc:Ignorable="d" >

    <!--フィルタ内容を選択するためのPopUpコントロール-->
    <Popup Name="popupInfomation" StaysOpen="False" Width="200" Height="180">
        <Border BorderBrush="Black" BorderThickness="0.5" Margin="0" Padding="0">
            <DockPanel  Margin="0">
                <TextBox BorderBrush="Black" Name="txtNotes" Background="LightYellow" x:FieldModifier="public" IsReadOnly="false" IsEnabled="True"></TextBox>
            </DockPanel>
        </Border>
    </Popup>

</UserControl>

    /// <summary>
    /// PopUpNote.xaml の相互作用ロジック
    /// </summary>
    public partial class PopUpNote : UserControl
    {
        public PopUpNote()
        {
            InitializeComponent();

        }
          
        private bool _isOpen = false;
        public bool IsOpen
        {
            get { return _isOpen; }
            set { _isOpen = value; popupInfomation.IsOpen = _isOpen; }
        }

        #region 添付プロパティ

        #region Notes

        /// <summary>
        /// ヘッダに表示する文字列
        /// DataGridFilterTextColumnHeaderクラスのHeaderTextプロパティに値を渡すための添付プロパティ
        /// </summary>
        //public string Notes
        //{
        //    get { return base.GetValue(NotesProperty) as string; }
        //    set { base.SetValue(NotesProperty, value); }
        //}
        public string Notes
        {
            get { return (string)this.GetValue(PopUpNote.NotesProperty); }
            set { this.SetValue(PopUpNote.NotesProperty, value); }
        }
        public static  DependencyProperty NotesProperty = DependencyProperty.RegisterAttached(
            "Notes", typeof(string),
            typeof(PopUpNote), new PropertyMetadata(string.Empty,
                                                                                new PropertyChangedCallback((sender, e) =>
                                                                                {
                                                                                    (sender as PopUpNote).OnNotesPropertyChanged(sender, e);
                                                                                })));
        private void OnNotesPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == null) { return; }
            this.txtNotes.Text = e.NewValue.ToString();
        }

        #endregion

        #endregion


<呼び出し側>
                    <!-- ***情報*** -->
                    <GridViewColumn Header="Info" Width="30" HeaderContainerStyle="{StaticResource GridViewColumnHeaderStyle}">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                                <!-- Marginで左右から-6pxとしないとHearder部のタイトルとラインの位置がずれる.GridViewの既知の不具合 -->
                                <Border BorderBrush="Black"  BorderThickness="0,0,0.5,0" Margin="-6,0,-6,0">
                                    <DockPanel  >
                                        <Image x:Name="popTriger" MouseEnter="popup_MouseEnter" Source="Images/postit.png" Visibility="{Binding Memo,Converter={StaticResource visibilityConverter}}"></Image>
                                        <csControls:PopUpNote x:Name="popup" Notes="{Binding Memo}" IsEnabled="True" >
                                        </csControls:PopUpNote>
                                    </DockPanel>
                                </Border>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>

実装例



すごいなーと思ったけど、ポップアップの中のテキストボックスが触れない。
貼り付けはできるっぽいけど・・・。簡単に調べた限りじゃわからんかった。
まぁいいってことにして少し使ってみてやっぱり変更したくなったら修正するようにしようと思う。

ガントチャート作成日記

少しづつ作っていたガントチャートがとりあえず、データを保存できるレベルになったので一端公開。

最新バージョンをダウンロードするように常に通知されるとか、タスクのカラーを変更できそうに見えるとか、Excel出力とか、帳票出力っぽいものとかボタンはあるけど動かないし、そもそも実装するかも未定だけど・・・。

とりあえず普通のタスク管理はできるのと、表示タブからいくつかの単位でまとめ直して表示することができる。

こまめにCtrl+Sで上書き保存してもらって変な動きしたらもう一回開きなおしてもらえばそれなりに使えるかも。

インストール不要なんで試してもらう分にはそれなりにいいかも。

DownLoad URL(ZipFile、Portable)
 => https://sites.google.com/site/mmbloguserfile/wai-bufairu/ProjectManager_Beta_0.0.0.0.zip

解凍してProjectManager.exeを起動してもらえば試せます。

<サンプル>

 表示タブ⇒作業者一覧 を選択


 作業者ごとにグルーピング(日付を変更すると元データも変更される)


 メイン画面=>タスク一覧を選択するとExcelっぽい感じで使える。
 Excelからのコピーペーストもできる
 ※空白セルがあるとずれる不具合がある。Excel上で全角空白か半角空白を入れてから張り付けてもらえばうまくいく。




ガントチャート作成日記

イナズマ線の表示の不具合を解消した。


イナズマ線はCellごとにPointCollection型のプロパティに値を指定している。
開始点、中間点、終了点はそれぞれ高さや行の幅×進捗率で計算して保持している。

        /// <summary>
        /// イナズマ線を表示
        /// </summary>
        /// <param name="targetTask">イナズマ線を表示するタスク</param>
        /// <param name="showPairThunderLine">ペアタスクのイナズマ線を表示するかどうか</param>
        public void ShowThunderLine(GanttGridTask targetTask,bool showPairThunderLine)
        {
            if (GanttGridCells == null) { return; }
            foreach (var rowItem in this.GanttGridCells.Where(i => i.DataTemplateType == GanttCell.TemplateType.ThunderLineRow && i.Task.Id.ToString() == targetTask.Id.ToString()))
            {
                // 通常タスクのイナズマ線を描画
                if (this.ThunderLineVisible)
                {
                    if (targetTask.Name != "" || targetTask.Name != null) { rowItem.ThunderLine = _GetThunderLinePoint(targetTask); }
                    else { rowItem.ThunderLine = null; }
                }

            }
        }

        /// <summary>
        /// イナズマ線のPointCollecctionを取得
        /// </summary>
        /// <param name="targetTask">イナズマ線を作成するタスク</param>
        /// <returns>
        ///  
        ///  ・基準日付時点でタスクが完了していないものは進捗位置までの線を引く
        ///    ⇒ 進捗率が1(完了)以外で、終了日
        ///  ・基準日までに開始できていないものは線を引く
        ///    ⇒ 進捗率が1(完了)以外で、終了日
        /// 
        ///  ・タスクの進捗率が基準日より遅れている場合に線を引く
        ///    ⇒ タスクの進捗率が1(完了)以外で、基準日より前にタスクの終了予定日がある
        ///      タスクの進捗率が1(完了)以外で、基準日より開始予定日が小さい場合
        ///  ・タスクの進捗率が基準日より進んでいる場合に線を引く
        ///
        /// </returns>
        private PointCollection _GetThunderLinePoint(GanttGridTask targetTask)
        {
            TimeSpan ts = this.ThunderLineBaseDate - ProjectStartDay; ;
            PointCollection _ThunderLine = new PointCollection();

            if ((targetTask.ProgressRate != 1 && targetTask.EndDay < this.ThunderLineBaseDate)
                || ((targetTask.ProgressRate != 1) && this.ThunderLineBaseDate > targetTask.StartDay)
                || ((targetTask.ProgressRate != 0) && this.ThunderLineBaseDate < targetTask.EndDay))
            {
                double startPoint = targetTask.Cell.ColumnIndex * this.ColumnWidth;
                double durectionPoint = targetTask.Cell.ColumnSpan * this.ColumnWidth;
                if (targetTask.ProgressRate == 0) { durectionPoint = durectionPoint * 0.01; }
                else { durectionPoint = durectionPoint * targetTask.ProgressRate; }

                _ThunderLine.Add(new Point(ts.Days * this.ColumnWidth, 0));
                _ThunderLine.Add(new Point(startPoint + durectionPoint, this.RowHeight / 2));
                _ThunderLine.Add(new Point(ts.Days * this.ColumnWidth, this.RowHeight));
            }
            else
            {
                _ThunderLine.Add(new Point(ts.Days * this.ColumnWidth, 0));
                _ThunderLine.Add(new Point(ts.Days * this.ColumnWidth, this.RowHeight));
            }

            return _ThunderLine;

        }

表現するためには、XAMLにDataTemplateを宣言しておいて、DataTemplateSelecterで変換している。

        <!-- イナズマ線を描画するためのDataTemplate -->
        <DataTemplate x:Key="ThunderLineRowLineTypeDataTemplate" >
            <Grid>
                <Polyline Points="{Binding ThunderLine}"  Stroke="Red" StrokeThickness="1" />
            </Grid>
        </DataTemplate>

ガントチャートを表現するためにItemControlに独自に作成したGanttTaskCell型をバインドしている。どのようなDataTemplateを利用するかはTemplateSelecterで切り替えている。そうすることで曜日によって色を変えたり、色々することができる。

詳細は全部完成してからいつか書きたいとは思う。

ついでに、垂直スクロールだけではなく、左クリックを押しながらホイールすれば水平スクロールもするように修正してみた。結構便利な気がする。


ガントチャート作成日記

昨日悩んでいた交互色について、プロパティを追加してバインドすることで解決した。
別dllのコントロールに対して、バインディングするのは気が引けるが今更かと思い直してバインドして解決した。
(TreeListViewとGanntGridはそれぞれ別々のdll)


やはり交互色があるほうがわかりやすい。
次はイナズマ線の不具合を直しつつ、ガントチャートの初期位置の表示を当日にするように修正してみる。

ガントチャート作成日記

TreeListViewにずっと交互色を付けたいと思っていた。調べたところAlternationCountプロパティを設定すればいいらしい・・・。

<dll呼び出し側>
<tree:TreeListView Name="treeView" AllowsColumnReorder="True" ItemsSource="{Binding GanttGridViewModel.Tasks}"
                               ScrollViewer.HorizontalScrollBarVisibility="Visible"  Background="White" ScrollViewer.VerticalScrollBarVisibility="Visible"
                               VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling" AlternationCount="2">


<dllのGeneric.xamlを抜粋>
        <!-- コントロールテンプレートのトリガーを定義 -->
        <ControlTemplate.Triggers>
            <!-- 交互色を指定 -->
            <!-- AlternationCountで交互色のパターンを指定。色についてはTreeListViewのGeneric.xamlのItemsControl.AlternationIndexで指定 -->
            <!-- 奇数行 -->
            <Trigger Property="ItemsControl.AlternationIndex" Value="0">
                <Setter TargetName="Border" Property="Background" Value="Transparent"/>
            </Trigger>
            <!-- 偶数行-->
            <Trigger Property="ItemsControl.AlternationIndex" Value="1">
                <Setter TargetName="Border" Property="Background" Value="#FFF0F0F0" />
            </Trigger>
         
        </ControlTemplate.Triggers>

    </ControlTemplate>


できた!!!

と思いきや、広げると・・・・


(゜-゜)・・・・。
やはりNo列で判定して書いたほうが確実かなぁ・・・。
ValueConverterを実装するほうが簡単かもしれないので次はそっちでやってみる。

ゴールは遠い・・・。


ガントチャート作成日記

スケジュール管理用のガントチャートのツールを作成しているときに独自に作成したTreeListViewで選択されている行に「▶」を出力したいと思ったのでいろいろいじってみた。

<修正前>
 <修正後>

思ったより簡単にできた。さすがWPF!!

Xaml抜粋

<TextBlock Text=" ▶" HorizontalAlignment="Left" VerticalAlignment="Center"  Margin="0,0,3,0" Visibility="{Binding IsSelected,Converter={StaticResource visibilityConverter},  RelativeSource={RelativeSource Mode=FindAncestor,AncestorType =TreeViewItem}}"/>

VisibilityConverter .cs
    /// <summary>
    /// ResizeRectangle.xamlで利用している
    /// リサイズ用のThumbコントロールの表示非表示を制御している
    /// </summary>
    public class VisibilityConverter : IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            bool b = (bool)value;
            return b ? Visibility.Visible : Visibility.Collapsed;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        #endregion
    }

やっていることはTextBlockに「▶」の文字を入れておく。表示非表示の制御をバインディングとValueConverterインターフェイスを実装。

バインディングは、親のTreeViewItemのIsSelectedプロパティを取得し、取得したプロパティの値(bool型)をValueConveterでVisibilityの値(列挙体のVisibility)に変換している。

試しに使っているといろいろ気になることがあるので修正中。果たして完成はいつになるのやら。

Gridコントロールへのバインドについて

GridコントロールのRow、Column、RowSpan、ColumnSpanに対してバインディングを行いたいと思って色々調べていると下記のサイトを見つけた。

https://rachel53461.wordpress.com/2011/09/17/wpf-grids-rowcolumn-count-properties/

GridコントロールのRow、Column、RowSpan、ColumnSpanに対してバインディングを行えるので、Canvasコントロールほど自由度は高くなく、方眼紙を塗りつぶすようなイメージでコントロールの配置を動的に変更できる。
ピクロス的な感じ。

Rowプロパティにバインドすることでバインドされている側のプロパティの値を変えればコントロールの位置を自分で変えることができる。
設定画面を作ればユーザーに自由にコントロールを配置してもらうことができ、単票形式の帳票であれば結構簡単に作れそうな気がする。

<ちょっと手を加えた>
    public class GridHelpers
    {
        #region RowCount Property

        /// <summary>
        /// Adds the specified number of Rows to RowDefinitions.
        /// Default Height is Auto
        /// </summary>
        public static readonly DependencyProperty RowCountProperty =
            DependencyProperty.RegisterAttached(
                "RowCount", typeof(int), typeof(GridHelpers),
                new PropertyMetadata(-1, RowCountChanged));

        // Get
        public static int GetRowCount(DependencyObject obj)
        {
            return (int)obj.GetValue(RowCountProperty);
        }

        // Set
        public static void SetRowCount(DependencyObject obj, int value)
        {
            obj.SetValue(RowCountProperty, value);
        }

        // Change Event - Adds the Rows
        public static void RowCountChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!(obj is Grid) || (int)e.NewValue < 0)
                return;

            Grid grid = (Grid)obj;
            grid.RowDefinitions.Clear();

            for (int i = 0; i < (int)e.NewValue; i++)
                grid.RowDefinitions.Add(
                    new RowDefinition() { Height = GridLength.Auto });

            SetStarRows(grid);

            // 行幅再設定
            SetRowHeight(grid);

        }

        #endregion

        #region ColumnCount Property

        /// <summary>
        /// Adds the specified number of Columns to ColumnDefinitions.
        /// Default Width is Auto
        /// </summary>
        public static readonly DependencyProperty ColumnCountProperty =
            DependencyProperty.RegisterAttached(
                "ColumnCount", typeof(int), typeof(GridHelpers),
                new PropertyMetadata(-1, ColumnCountChanged));

        // Get
        public static int GetColumnCount(DependencyObject obj)
        {
            return (int)obj.GetValue(ColumnCountProperty);
        }

        // Set
        public static void SetColumnCount(DependencyObject obj, int value)
        {
            obj.SetValue(ColumnCountProperty, value);
        }

        // Change Event - Add the Columns
        public static void ColumnCountChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!(obj is Grid) || (int)e.NewValue < 0)
                return;

            Grid grid = (Grid)obj;
            grid.ColumnDefinitions.Clear();

            for (int i = 0; i < (int)e.NewValue; i++)
                grid.ColumnDefinitions.Add(
                    new ColumnDefinition() { Width = GridLength.Auto });

            SetStarColumns(grid);

            // カラム幅再設定
            SetColumnWidth(grid);

        }

        #endregion

        #region StarRows Property

        /// <summary>
        /// Makes the specified Row's Height equal to Star.
        /// Can set on multiple Rows
        /// </summary>
        public static readonly DependencyProperty StarRowsProperty =
            DependencyProperty.RegisterAttached(
                "StarRows", typeof(string), typeof(GridHelpers),
                new PropertyMetadata(string.Empty, StarRowsChanged));

        // Get
        public static string GetStarRows(DependencyObject obj)
        {
            return (string)obj.GetValue(StarRowsProperty);
        }

        // Set
        public static void SetStarRows(DependencyObject obj, string value)
        {
            obj.SetValue(StarRowsProperty, value);
        }

        // Change Event - Makes specified Row's Height equal to Star
        public static void StarRowsChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString()))
                return;

            SetStarRows((Grid)obj);
        }

        #endregion

        #region StarColumns Property

        /// <summary>
        /// Makes the specified Column's Width equal to Star.
        /// Can set on multiple Columns
        /// </summary>
        public static readonly DependencyProperty StarColumnsProperty =
            DependencyProperty.RegisterAttached(
                "StarColumns", typeof(string), typeof(GridHelpers),
                new PropertyMetadata(string.Empty, StarColumnsChanged));

        // Get
        public static string GetStarColumns(DependencyObject obj)
        {
            return (string)obj.GetValue(StarColumnsProperty);
        }

        // Set
        public static void SetStarColumns(DependencyObject obj, string value)
        {
            obj.SetValue(StarColumnsProperty, value);
        }

        // Change Event - Makes specified Column's Width equal to Star
        public static void StarColumnsChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString()))
                return;

            SetStarColumns((Grid)obj);
        }

        #endregion

        #region ColumnWidth Property

        /// <summary>
        /// カラム幅設定用独自プロパティ
        /// </summary>
        public static readonly DependencyProperty ColumnWidthProperty =
            DependencyProperty.RegisterAttached(
                "ColumnWidth", typeof(int), typeof(GridHelpers),
                new PropertyMetadata(0, ColumnWidthChanged));

        // 取得処理
        public static int GetColumnWidth(DependencyObject obj)
        {
            return (int)obj.GetValue(ColumnWidthProperty);
        }

        // 設定処理
        public static void SetColumnWidth(DependencyObject obj, string value)
        {
            obj.SetValue(ColumnWidthProperty, value);
        }

        // 値変更時処理
        public static void ColumnWidthChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString()))
                return;

            SetColumnWidth((Grid)obj);
        }

        #endregion

        #region RowHeight Property
        /// <summary>
        /// カラム幅設定用独自プロパティ
        /// </summary>
        public static readonly DependencyProperty RowHeightProperty =
            DependencyProperty.RegisterAttached(
                "RowHeight", typeof(int), typeof(GridHelpers),
                new PropertyMetadata(0, RowHeightChanged));

        // 取得処理
        public static int GetRowHeight(DependencyObject obj)
        {
            return (int)obj.GetValue(RowHeightProperty);
        }

        // 設定処理
        public static void SetRowHeight(DependencyObject obj, string value)
        {
            obj.SetValue(RowHeightProperty, value);
        }

        // 値変更時処理
        public static void RowHeightChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            if (!(obj is Grid) || string.IsNullOrEmpty(e.NewValue.ToString()))
                return;
            SetRowHeight((Grid)obj);
        }

        #endregion


        private static void SetStarColumns(Grid grid)
        {
            string[] starColumns =
                GetStarColumns(grid).Split(',');

            for (int i = 0; i < grid.ColumnDefinitions.Count; i++)
            {
                if (starColumns.Contains(i.ToString()))
                    grid.ColumnDefinitions[i].Width =
                        new GridLength(1, GridUnitType.Star);
            }
        }

        private static void SetStarRows(Grid grid)
        {
            string[] starRows =
                GetStarRows(grid).Split(',');

            for (int i = 0; i < grid.RowDefinitions.Count; i++)
            {
                if (starRows.Contains(i.ToString()))
                    grid.RowDefinitions[i].Height =
                        new GridLength(1, GridUnitType.Star);
            }
        }

        // カラム幅設定の独自プロパティ設定用
        private static void SetColumnWidth(Grid grid)
        {
            for (int i = 0; i < grid.ColumnDefinitions.Count; i++)
            {
                grid.ColumnDefinitions[i].Width = new GridLength(GetColumnWidth(grid));
            }
        }

        // 行幅設定の独自プロパティ設定用
        private static void SetRowHeight(Grid grid)
        {
            for (int i = 0; i < grid.RowDefinitions.Count; i++)
            {
                grid.RowDefinitions[i].Height = new GridLength(GetRowHeight(grid));
            }
        }


<使い方>
<ScrollViewer Name="scrollDetail" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Grid.Row="1" ScrollViewer.IsDeferredScrollingEnabled="True"
  ScrollViewer.PanningMode="VerticalOnly" >
<ItemsControl Name="ic" ItemsSource="{Binding Source={StaticResource GanttGridCellsSource}}" VirtualizingPanel.IsVirtualizing="True" 
  VirtualizingPanel.VirtualizationMode="Recycling" ItemTemplateSelector="{StaticResource ItemViewModelTemplateSelector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Name="gridCalender" helper:GridHelpers.RowCount="{Binding RowCount}" helper:GridHelpers.ColumnCount="{Binding ColumnCount}"
  helper:GridHelpers.ColumnWidth="{Binding ColumnWidth}" helper:GridHelpers.RowHeight="{Binding RowHeight}"
  PreviewMouseDown="gridCalender_PreviewMouseDown" Margin="0,0,0,25">
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column" Value="{Binding Path=ColumnIndex}"/>
<Setter Property="Grid.Row" Value="{Binding Path=RowIndex}"/>
<Setter Property="Grid.ColumnSpan" Value="{Binding Path=ColumnSpan}"/>
<Setter Property="Grid.RowSpan" Value="{Binding Path=RowSpan}"/>
<Setter Property="Grid.ZIndex" Value="{Binding Path=ZIndex}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</ScrollViewer>

PowerShellでWPFアプリケーションをBindingするときの注意点

 1.PowerShellでWPFアプリケーションのBindingについて マニアックだけどPowerShellでWPFアプリケーションを作っている。INotifyChangedを実装したいけど、PowerShellにはgetterやsetterがないので通知が発行できない。 そ...