トップ回答者
【VisualStudio2015】ガントチャートアプリを作成する場合、一覧表示画面ではどのコントロールを使ったらいいですか

質問
-
お世話になります。
VisualStudio2015でデスクトップアプリ(WPF)作成中です。Windows7 32bitです。
C#始めたばかりで、WPFも初めて作っています。
他でまとめて質問しすぎてしまったため、今回は1つの質問に絞らせて頂きます。
現在、WPFで作ろうとしているものはTODOリストのようなものです。
大分類・中分類・小分類、タグ(ラベル)付けなどを可能にし
登録画面、一覧表示画面、背景色等、設定画面、カレンダー表示等など行いたいです。
この一覧画面に関する質問なのですが、
・大分類、中分類、小分類等スピナーのように表示したい(または、それらが見出しのようになって階層型一覧表示が理想)
・起動時、終了時にCSVファイルから読み込み分類を行う(なるべく時間が掛からないコントロールが理想)
・登録・編集画面のみでしか編集ができない。 右クリックしたら編集画面が表示できる。(他にもボタンから登録・編集可能)
・緊急時にあわせて色変更等行いたい(技術が伴ってきたらポップアップ通知とかも出したい)
どのコントロールでもできるかもしれませんが、上記内容ができるのが前提です。
候補としては
・ツリービュー
・リストボックス
・データグリッド
があります。
長くなりましたので、質問をまとめます。
<質問内容>
・ガントチャートの一覧画面を作成する場合、どのコントロールが適しているか
・それぞれのコントロールのメリット、デメリット(あれば)
以上、よろしくお願いいたします。
回答
-
どのコントロールが適しているかという質問に対する答えはプログラマの力量に依存するので…
- 初級者向け:
・既存のガントチャートコントロールを探してくる
・ScrollViewer内のパネルに全要素を配置する
- 中級者向け
・ListBoxやListViewやDataGridとキャンバスを組み合わせて1行毎にコントロール生成
・すべて画像(Pathなど)で描画+ScrollBar
- 上級者向け
・縦横にスクロールできるVirtualizingPanelで碁盤の目状に配置できるコントロールを自作
以下はDataGrid+Canvasで適当に作ってみたサンプル
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:app="clr-namespace:WpfApplication1" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <ResourceDictionary> <app:DateOffsetConverter x:Key="conv30" Scale="30"/> <app:DateOffsetConverter x:Key="conv1" Scale="1"/> <app:DateOffsetConverter x:Key="convNeg" Scale="-30"/> <app:BrushConverter x:Key="bconv" /> </ResourceDictionary> </Window.Resources> <DockPanel> <WrapPanel DockPanel.Dock="Bottom" TextElement.FontSize="20"> <Button Command="{Binding Path=Add}" Content="Add" CommandParameter="{Binding ElementName=dg,Path=SelectedItem}" Margin="5"/> <Button Command="{Binding Path=Hide}" Content="Hide" CommandParameter="{Binding ElementName=dg,Path=SelectedItem}" Margin="5"/> <Button Command="{Binding Path=Show}" Content="Show" CommandParameter="{Binding ElementName=dg,Path=SelectedItem}" Margin="5"/> <Button Command="{Binding Path=ColorTest}" Content="Color" Margin="5"/> </WrapPanel> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid IsHitTestVisible="False"> <Grid.RenderTransform> <TranslateTransform X="{Binding ElementName=dg,Path=RowHeaderActualWidth}" /> </Grid.RenderTransform> <ItemsControl ItemsSource="{Binding}" DataContext="123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > <ItemsControl.ItemContainerStyle> <Style TargetType="ContentPresenter" > <Setter Property="Width" Value="30" /> </Style> </ItemsControl.ItemContainerStyle> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderThickness="1,0,0,0" BorderBrush="Black" /> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> <DataGrid AutoGenerateColumns="False" SizeChanged="DataGrid_SizeChanged" ItemsSource="{Binding Path=Item.Items}" Background="Transparent" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" x:Name="dg" ScrollViewer.HorizontalScrollBarVisibility="Hidden" CanUserAddRows="false" CanUserDeleteRows="False" CanUserResizeColumns="False" CanUserSortColumns="False" > <DataGrid.RowStyle> <Style TargetType="DataGridRow"> <Setter Property="Background" Value="Transparent"/> <EventSetter Event="MouseRightButtonDown" Handler="DataGridRow_MouseRightButtonDown"/> </Style> </DataGrid.RowStyle> <DataGrid.RowHeaderStyle> <Style TargetType="DataGridRowHeader"> <Setter Property="Content" Value="{Binding Path=.}" /> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="{Binding Path=Level,Converter={StaticResource conv30}}"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <ContentPresenter Content="{Binding Path=Caption}" Grid.Column="1"/> </Grid> </DataTemplate> </Setter.Value> </Setter> </Style> </DataGrid.RowHeaderStyle> <DataGrid.Columns> <DataGridTemplateColumn IsReadOnly="true" > <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Canvas Height="20" Background="LightGreen"> <Canvas.RenderTransform> <TranslateTransform X="{Binding ElementName=hsv,Path=Value,Converter={StaticResource convNeg}}" /> </Canvas.RenderTransform> <Border Canvas.Left="{Binding Path=Start,Converter={StaticResource conv30}}" Canvas.Top="2" Height="16" Background="{Binding Path=Color,Converter={StaticResource bconv}}" Width="{Binding Path=Days,Converter={StaticResource conv30}}"> <Canvas> <TextBlock Text="{Binding Path=Start,StringFormat=yy/MM/dd}" /> </Canvas> </Border> </Canvas> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> <Grid Grid.Row="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="{Binding ElementName=dg,Path=RowHeaderActualWidth}" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <ScrollBar Grid.Column="1" Orientation="Horizontal" x:Name="hsv" Minimum="{Binding Path=Item.Start,Converter={StaticResource conv1}}" Maximum="{Binding Path=Item.End,Converter={StaticResource conv1}}" Value="{Binding Path=Offset}"/> </Grid> </Grid> </DockPanel> </Window>
using System; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Collections.ObjectModel; namespace WpfApplication1 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.DataContext = MainModel.CreateTestModel(); } private void DataGrid_SizeChanged(object sender, SizeChangedEventArgs e) { var dg = (DataGrid)sender; var col = dg.Columns[0]; col.Width = Math.Max(0, dg.ActualWidth - dg.RowHeaderActualWidth); } private void DataGridRow_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { var row=(DataGridRow)sender; MessageBox.Show(row.DataContext.ToString()); } } class DateOffsetConverter : IValueConverter { public DateOffsetConverter() { Scale = 1; } public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is DateTime) { return ((DateTime)value).ToOADate() * Scale; } else { var v = (IConvertible)value; return v.ToDouble(null) * Scale; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } public double Scale { get; set; } } class BrushConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new SolidColorBrush((Color)value); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } } namespace WpfApplication1 { class DelegateCommand : ICommand { public DelegateCommand(Action<Object> act) { this._action = act; } private Action<object> _action; public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { _action(parameter); } } class ModelBase : System.ComponentModel.INotifyPropertyChanged { public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string name) { var pc = PropertyChanged; if (pc != null) { pc(this, new System.ComponentModel.PropertyChangedEventArgs(name)); } } } class MainModel : ModelBase { public static MainModel CreateTestModel() { MainModel m = new MainModel(); m.Item.Start = DateTime.Now.Date; m.Item.Days = 50; Random rnd = new Random(); for (int i = 0; i < 5; i++) { Item child = new Item(); child.Start = DateTime.Now.AddDays(rnd.NextDouble() * (i + 1) * 5); child.Days = rnd.NextDouble() * 30; child.Caption = "ITEM " + i.ToString(); child.Color = Colors.Green; m.Item.Items.Add(child); DateTime d = child.Start; for (; ; ) { Item grandson = new Item(); child.Items.Add(grandson); grandson.Level = child.Level + 1; grandson.Start = d; grandson.Days = rnd.NextDouble() * 5; grandson.Caption = child.Caption + "-" + child.Items.Count().ToString(); if ((rnd.Next() & 0x03) == 3) { grandson.Color = Colors.Red; } else { grandson.Color = Colors.LightSkyBlue; } m.Item.Items.Add(grandson); if (grandson.End > child.End) { grandson.Days = (child.End - grandson.Start).TotalDays; break; } d = grandson.End; } } return m; } public MainModel() { Days = new ObservableCollection<DateTime>(); Add = new DelegateCommand((o) => { Item item = new Item(); Random rnd = new Random(); int index = rnd.Next(0, this.Item.Items.Count - 1); Item parent; parent = this.Item.Items[index]; if (o is Item) { parent = (Item)o; index = this.Item.Items.IndexOf(parent); } item.Level = parent.Level + 1; item.Start = parent.Start.AddDays(rnd.NextDouble()* parent.Days); item.Days = rnd.NextDouble() * parent.Days; item.Caption = parent.Caption + "-1"; item.Color = Colors.LightPink; parent.Items.Add(item); this.Item.Items.Insert(index + 1, item); }); Hide = new DelegateCommand((o) => { if (!(o is Item)) { return; } var parent = (Item)o; var index = this.Item.Items.IndexOf(parent) + 1; while (index < this.Item.Items.Count) { var child = this.Item.Items[index]; if (child.Level <= parent.Level) { break; } this.Item.Items.RemoveAt(index); } }); Show = new DelegateCommand((o) => { Hide.Execute(o); var parent = (Item)o; var index = this.Item.Items.IndexOf(parent) + 1; foreach(Item child in parent.Items) { this.Item.Items.Insert(index,child); index++; } }); ColorTest = new DelegateCommand((o) => { Random rnd = new Random(); byte[] bs=new byte[4]; foreach (Item item in this.Item.Items) { rnd.NextBytes(bs); Color c = Color.FromArgb(bs[0], bs[1], bs[2], bs[3]); item.Color = c; } }); } public Item Item { get { return _Item; } } private Item _Item = new Item(DateTime.Now, 10); public double Offset { get { return _Offset; } set { value = Math.Floor(value); if (_Offset != value) { _Offset = value; OnPropertyChanged("Offset"); } } } private double _Offset; public ObservableCollection<DateTime> Days{get;private set;} public ICommand Add { get; private set; } public ICommand Hide { get; private set; } public ICommand Show { get; private set; } public ICommand ColorTest { get; private set; } } class Item : ModelBase { public Item() { } public Item(DateTime start, double days) { this.Start = start; this.Days = days; } public string Caption { get { return _Caption; } set { _Caption = value; OnPropertyChanged("Caption"); } } private string _Caption; public int Level { get { return _Level; } set { _Level = value; OnPropertyChanged("Level"); } } private int _Level = 0; public DateTime Start { get { return _Start; } set { _Start = value; OnPropertyChanged("Start"); End = Start.AddDays(_Days); } } private DateTime _Start; public double Days { get { return _Days; } set { _Days = value; OnPropertyChanged("Days"); End = Start.AddDays(_Days); } } private double _Days; public DateTime End { get { return Start.AddDays(Days); } private set { _End = value; OnPropertyChanged("End"); } } private DateTime _End; public Color Color { get { return _Color; } set { _Color = value; OnPropertyChanged("Color"); } } private Color _Color; public ObservableCollection<Item> Items { get { return _Items; } } private ObservableCollection<Item> _Items = new ObservableCollection<Item>(); public override string ToString() { return string.Format("{0}\r\n{1}", Start, End); } } }
#サンプルについての細かい説明はしません個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク さろるん 2016年7月5日 7:54
- 初級者向け:
-
階層型一覧表示なら TreeView がいいと思いますが、階層レベルが決まってるなら、ListView もしくは ListBox でいいかもしれません。併しながら
> C#始めたばかりで、WPFも初めて作っています。
が気になります。ListView や DataGrid 等を利用してお好みの仕様にカスタマイズするのは、C# & WPF 初心者には極めて骨が折れる作業です。
gekka さんが、
> 初級者向け:
> ・既存のガントチャートコントロールを探してくる・・・
と回答されてますが、Infragistics さんが WPF で使えるコンポーネントを各種提供されているので、工数削減のため既存のコントロールの使用を検討してみた方がいいかも知れません。
Infragistics - WPF ガント
本フォーラムは、ユーザー(開発者)同士で情報交換を行うためのコミュニティです。初めて利用される方は、以下のアナウンスをご覧ください。 https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?
- 回答としてマーク さろるん 2016年7月5日 7:54
-
以下のような簡単な例もあります。こういったものを探されて、できるだけ簡単なことから応用されていくとノウハウが身に付き、どのようなコントロールを使うべきかの判断もよりできるようになるのではないかと思います。
How to create a Gantt Control in WPF
http://blog.functionalfun.net/2008/09/how-to-create-gantt-control-in-wpf.html#感覚だけですが、私が作るなら、とりあえずUserControlでバーを表現するようにして、それをListBoxで表示することから試してみるかなぁといったところです。バーそれぞれは一つずつオブジェクトで管理しておくと何かと良いかなぁと思うのがその理由です。
★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク さろるん 2016年7月5日 7:54
すべての返信
-
どのコントロールが適しているかという質問に対する答えはプログラマの力量に依存するので…
- 初級者向け:
・既存のガントチャートコントロールを探してくる
・ScrollViewer内のパネルに全要素を配置する
- 中級者向け
・ListBoxやListViewやDataGridとキャンバスを組み合わせて1行毎にコントロール生成
・すべて画像(Pathなど)で描画+ScrollBar
- 上級者向け
・縦横にスクロールできるVirtualizingPanelで碁盤の目状に配置できるコントロールを自作
以下はDataGrid+Canvasで適当に作ってみたサンプル
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:app="clr-namespace:WpfApplication1" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <ResourceDictionary> <app:DateOffsetConverter x:Key="conv30" Scale="30"/> <app:DateOffsetConverter x:Key="conv1" Scale="1"/> <app:DateOffsetConverter x:Key="convNeg" Scale="-30"/> <app:BrushConverter x:Key="bconv" /> </ResourceDictionary> </Window.Resources> <DockPanel> <WrapPanel DockPanel.Dock="Bottom" TextElement.FontSize="20"> <Button Command="{Binding Path=Add}" Content="Add" CommandParameter="{Binding ElementName=dg,Path=SelectedItem}" Margin="5"/> <Button Command="{Binding Path=Hide}" Content="Hide" CommandParameter="{Binding ElementName=dg,Path=SelectedItem}" Margin="5"/> <Button Command="{Binding Path=Show}" Content="Show" CommandParameter="{Binding ElementName=dg,Path=SelectedItem}" Margin="5"/> <Button Command="{Binding Path=ColorTest}" Content="Color" Margin="5"/> </WrapPanel> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid IsHitTestVisible="False"> <Grid.RenderTransform> <TranslateTransform X="{Binding ElementName=dg,Path=RowHeaderActualWidth}" /> </Grid.RenderTransform> <ItemsControl ItemsSource="{Binding}" DataContext="123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > <ItemsControl.ItemContainerStyle> <Style TargetType="ContentPresenter" > <Setter Property="Width" Value="30" /> </Style> </ItemsControl.ItemContainerStyle> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <Border HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderThickness="1,0,0,0" BorderBrush="Black" /> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid> <DataGrid AutoGenerateColumns="False" SizeChanged="DataGrid_SizeChanged" ItemsSource="{Binding Path=Item.Items}" Background="Transparent" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" x:Name="dg" ScrollViewer.HorizontalScrollBarVisibility="Hidden" CanUserAddRows="false" CanUserDeleteRows="False" CanUserResizeColumns="False" CanUserSortColumns="False" > <DataGrid.RowStyle> <Style TargetType="DataGridRow"> <Setter Property="Background" Value="Transparent"/> <EventSetter Event="MouseRightButtonDown" Handler="DataGridRow_MouseRightButtonDown"/> </Style> </DataGrid.RowStyle> <DataGrid.RowHeaderStyle> <Style TargetType="DataGridRowHeader"> <Setter Property="Content" Value="{Binding Path=.}" /> <Setter Property="ContentTemplate"> <Setter.Value> <DataTemplate> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="{Binding Path=Level,Converter={StaticResource conv30}}"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <ContentPresenter Content="{Binding Path=Caption}" Grid.Column="1"/> </Grid> </DataTemplate> </Setter.Value> </Setter> </Style> </DataGrid.RowHeaderStyle> <DataGrid.Columns> <DataGridTemplateColumn IsReadOnly="true" > <DataGridTemplateColumn.CellTemplate> <DataTemplate> <Canvas Height="20" Background="LightGreen"> <Canvas.RenderTransform> <TranslateTransform X="{Binding ElementName=hsv,Path=Value,Converter={StaticResource convNeg}}" /> </Canvas.RenderTransform> <Border Canvas.Left="{Binding Path=Start,Converter={StaticResource conv30}}" Canvas.Top="2" Height="16" Background="{Binding Path=Color,Converter={StaticResource bconv}}" Width="{Binding Path=Days,Converter={StaticResource conv30}}"> <Canvas> <TextBlock Text="{Binding Path=Start,StringFormat=yy/MM/dd}" /> </Canvas> </Border> </Canvas> </DataTemplate> </DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn> </DataGrid.Columns> </DataGrid> <Grid Grid.Row="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="{Binding ElementName=dg,Path=RowHeaderActualWidth}" /> <ColumnDefinition /> </Grid.ColumnDefinitions> <ScrollBar Grid.Column="1" Orientation="Horizontal" x:Name="hsv" Minimum="{Binding Path=Item.Start,Converter={StaticResource conv1}}" Maximum="{Binding Path=Item.End,Converter={StaticResource conv1}}" Value="{Binding Path=Offset}"/> </Grid> </Grid> </DockPanel> </Window>
using System; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; using System.Windows.Media; using System.Collections.ObjectModel; namespace WpfApplication1 { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.DataContext = MainModel.CreateTestModel(); } private void DataGrid_SizeChanged(object sender, SizeChangedEventArgs e) { var dg = (DataGrid)sender; var col = dg.Columns[0]; col.Width = Math.Max(0, dg.ActualWidth - dg.RowHeaderActualWidth); } private void DataGridRow_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { var row=(DataGridRow)sender; MessageBox.Show(row.DataContext.ToString()); } } class DateOffsetConverter : IValueConverter { public DateOffsetConverter() { Scale = 1; } public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (value is DateTime) { return ((DateTime)value).ToOADate() * Scale; } else { var v = (IConvertible)value; return v.ToDouble(null) * Scale; } } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } public double Scale { get; set; } } class BrushConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return new SolidColorBrush((Color)value); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } } } namespace WpfApplication1 { class DelegateCommand : ICommand { public DelegateCommand(Action<Object> act) { this._action = act; } private Action<object> _action; public bool CanExecute(object parameter) { return true; } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { _action(parameter); } } class ModelBase : System.ComponentModel.INotifyPropertyChanged { public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string name) { var pc = PropertyChanged; if (pc != null) { pc(this, new System.ComponentModel.PropertyChangedEventArgs(name)); } } } class MainModel : ModelBase { public static MainModel CreateTestModel() { MainModel m = new MainModel(); m.Item.Start = DateTime.Now.Date; m.Item.Days = 50; Random rnd = new Random(); for (int i = 0; i < 5; i++) { Item child = new Item(); child.Start = DateTime.Now.AddDays(rnd.NextDouble() * (i + 1) * 5); child.Days = rnd.NextDouble() * 30; child.Caption = "ITEM " + i.ToString(); child.Color = Colors.Green; m.Item.Items.Add(child); DateTime d = child.Start; for (; ; ) { Item grandson = new Item(); child.Items.Add(grandson); grandson.Level = child.Level + 1; grandson.Start = d; grandson.Days = rnd.NextDouble() * 5; grandson.Caption = child.Caption + "-" + child.Items.Count().ToString(); if ((rnd.Next() & 0x03) == 3) { grandson.Color = Colors.Red; } else { grandson.Color = Colors.LightSkyBlue; } m.Item.Items.Add(grandson); if (grandson.End > child.End) { grandson.Days = (child.End - grandson.Start).TotalDays; break; } d = grandson.End; } } return m; } public MainModel() { Days = new ObservableCollection<DateTime>(); Add = new DelegateCommand((o) => { Item item = new Item(); Random rnd = new Random(); int index = rnd.Next(0, this.Item.Items.Count - 1); Item parent; parent = this.Item.Items[index]; if (o is Item) { parent = (Item)o; index = this.Item.Items.IndexOf(parent); } item.Level = parent.Level + 1; item.Start = parent.Start.AddDays(rnd.NextDouble()* parent.Days); item.Days = rnd.NextDouble() * parent.Days; item.Caption = parent.Caption + "-1"; item.Color = Colors.LightPink; parent.Items.Add(item); this.Item.Items.Insert(index + 1, item); }); Hide = new DelegateCommand((o) => { if (!(o is Item)) { return; } var parent = (Item)o; var index = this.Item.Items.IndexOf(parent) + 1; while (index < this.Item.Items.Count) { var child = this.Item.Items[index]; if (child.Level <= parent.Level) { break; } this.Item.Items.RemoveAt(index); } }); Show = new DelegateCommand((o) => { Hide.Execute(o); var parent = (Item)o; var index = this.Item.Items.IndexOf(parent) + 1; foreach(Item child in parent.Items) { this.Item.Items.Insert(index,child); index++; } }); ColorTest = new DelegateCommand((o) => { Random rnd = new Random(); byte[] bs=new byte[4]; foreach (Item item in this.Item.Items) { rnd.NextBytes(bs); Color c = Color.FromArgb(bs[0], bs[1], bs[2], bs[3]); item.Color = c; } }); } public Item Item { get { return _Item; } } private Item _Item = new Item(DateTime.Now, 10); public double Offset { get { return _Offset; } set { value = Math.Floor(value); if (_Offset != value) { _Offset = value; OnPropertyChanged("Offset"); } } } private double _Offset; public ObservableCollection<DateTime> Days{get;private set;} public ICommand Add { get; private set; } public ICommand Hide { get; private set; } public ICommand Show { get; private set; } public ICommand ColorTest { get; private set; } } class Item : ModelBase { public Item() { } public Item(DateTime start, double days) { this.Start = start; this.Days = days; } public string Caption { get { return _Caption; } set { _Caption = value; OnPropertyChanged("Caption"); } } private string _Caption; public int Level { get { return _Level; } set { _Level = value; OnPropertyChanged("Level"); } } private int _Level = 0; public DateTime Start { get { return _Start; } set { _Start = value; OnPropertyChanged("Start"); End = Start.AddDays(_Days); } } private DateTime _Start; public double Days { get { return _Days; } set { _Days = value; OnPropertyChanged("Days"); End = Start.AddDays(_Days); } } private double _Days; public DateTime End { get { return Start.AddDays(Days); } private set { _End = value; OnPropertyChanged("End"); } } private DateTime _End; public Color Color { get { return _Color; } set { _Color = value; OnPropertyChanged("Color"); } } private Color _Color; public ObservableCollection<Item> Items { get { return _Items; } } private ObservableCollection<Item> _Items = new ObservableCollection<Item>(); public override string ToString() { return string.Format("{0}\r\n{1}", Start, End); } } }
#サンプルについての細かい説明はしません個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク さろるん 2016年7月5日 7:54
- 初級者向け:
-
階層型一覧表示なら TreeView がいいと思いますが、階層レベルが決まってるなら、ListView もしくは ListBox でいいかもしれません。併しながら
> C#始めたばかりで、WPFも初めて作っています。
が気になります。ListView や DataGrid 等を利用してお好みの仕様にカスタマイズするのは、C# & WPF 初心者には極めて骨が折れる作業です。
gekka さんが、
> 初級者向け:
> ・既存のガントチャートコントロールを探してくる・・・
と回答されてますが、Infragistics さんが WPF で使えるコンポーネントを各種提供されているので、工数削減のため既存のコントロールの使用を検討してみた方がいいかも知れません。
Infragistics - WPF ガント
本フォーラムは、ユーザー(開発者)同士で情報交換を行うためのコミュニティです。初めて利用される方は、以下のアナウンスをご覧ください。 https://social.msdn.microsoft.com/Forums/ja-JP/ca9ecfb7-4407-4fcb-b8bd-207d68257e68?
- 回答としてマーク さろるん 2016年7月5日 7:54
-
以下のような簡単な例もあります。こういったものを探されて、できるだけ簡単なことから応用されていくとノウハウが身に付き、どのようなコントロールを使うべきかの判断もよりできるようになるのではないかと思います。
How to create a Gantt Control in WPF
http://blog.functionalfun.net/2008/09/how-to-create-gantt-control-in-wpf.html#感覚だけですが、私が作るなら、とりあえずUserControlでバーを表現するようにして、それをListBoxで表示することから試してみるかなぁといったところです。バーそれぞれは一つずつオブジェクトで管理しておくと何かと良いかなぁと思うのがその理由です。
★良い回答には回答済みマークを付けよう! MVP - .NET http://d.hatena.ne.jp/trapemiya/
- 回答としてマーク さろるん 2016年7月5日 7:54