DOBON.NET DOBON.NETプログラミング掲示板過去ログ

ガントチャートのサンプルでVS2010が落ちる

環境/言語:[Windows Vista/7 C# VC2010 Winアプリ]
分類:[.NET]

お世話になります。

ガントチャートなるものを試したくググってみると下記のサンプルを見つけました。
http://www.codeproject.com/Articles/20731/Gantt-Chart

VBで記述されていたので、Web譲歩変換ページを使いC#に書き換えてみましたが、作成されたコンポーネントをフォームに追加しようとするとVC2010がバグって終了します。

変換後のファイルを迷惑かと思いますが載せましたのでできましたらどなたか教えてください。

1500行あまりあり長いのでそのファイルをzip形式でアップしてみました。
添付ファイル: GanttChart.Designer.zip (9 KB)
> VBで記述されていたので、Web譲歩変換ページを使いC#に書き換えてみましたが、作成されたコンポーネントをフォームに追加しようとするとVC2010がバグって終了します。

  話がよく見えないのだが・・・
  VS2010とタイトルには書かれているが本文ではVC2010となっている。

  フォームは何で作られた言語のものに貼るつもりでしょうか?

  因みにガントチャ−ト機能のみを単体のプロジェクトにして、新規
  プロジェクトを作って参照し、フォームに貼りましたが問題なく動
  作しました。

  わざわざC#にしなければならない理由は?変換がおかしいだけでは
  ないでしょうか。VBのままなら問題ないと思いますが。

以上。参考まで
■No30236に返信(オショウさんの記事)
>   話がよく見えないのだが・・・
>   VS2010とタイトルには書かれているが本文ではVC2010となっている。
>
>   フォームは何で作られた言語のものに貼るつもりでしょうか?
わかりにくくて申し訳ありません。
VS2010として読んでください。


>   因みにガントチャ−ト機能のみを単体のプロジェクトにして、新規
>   プロジェクトを作って参照し、フォームに貼りましたが問題なく動
>   作しました。
>
>   わざわざC#にしなければならない理由は?変換がおかしいだけでは
>   ないでしょうか。VBのままなら問題ないと思いますが。
サンプルの開発言語がVBでわからないのでC#で作ってます。そのためVBコードをC#に変換しましたが、「問題なく動作しました」とは、VBのガンチャート機能をそのままC#で利用可能?なのですか?それともアップした私コード?

表現がわかりにくくて申し訳ありませんでしたが、「C#でサンプルを作成したい」という意味で教えていただけないでしょうか?

よろしくお願いします。
> サンプルの開発言語がVBでわからないのでC#で作ってます。そのためVBコードをC#に変換しましたが、「問題なく動作しました」とは、VBのガンチャート機能をそのままC#で利用可能?なのですか?それともアップした私コード?

  アップされたコードに変換ミスがあるようです。
  VBのままのGanttChartコントロールをC#に貼っても問題なく
  動作してます。

  なので、他の変換プログラムを用いて変換するか、手作業で
  確実な変換を行ってください。

※ 問題なくとは、異常終了しない。と意味です。

以上。
>   アップされたコードに変換ミスがあるようです。
>   VBのままのGanttChartコントロールをC#に貼っても問題なく
>   動作してます。

この、「VBのままのGanttChartコントロールをC#に貼っても問題なく動作してます」とは、どのように貼り付けているのでしょうか?

C#のアプリ内でコンポーネントなどコンパイルするとツールボックス内にその部品が作成されますが、C#で作成中のアプリ内にこのVBのコードを取り込む?という意味でしょうか?それでは、思った形にはなりませんが。

VBやVBで作成されたこーどをC#で扱ったことないためどうしていいかわかりません。

もう少し詳しく教えてください。

よろしくお願いします。
> この、「VBのままのGanttChartコントロールをC#に貼っても問題なく動作してます」とは、どのように貼り付けているのでしょうか?

  ミックスドランゲージなので、VIsualStudioはProfessional以上
  でないとできません。

  GanttChartコントロールはVBのコードで作成されていますので、そこに
  追加でC#のWindowsフォームのプロジェクトを作成すれば、そのままツ
  ールボックス中のGanntChartをフォームに適用できます。

  Express版は使ったことが無いので、こういう場合の方法は知りません。

以上。
No30230 の投稿が丸投げを禁止したルールに違反しているのではないかというご報告をいただきました。 No30230 の投稿を拝見すると、意味不明の誤字(?)もあり、その点も安易な投稿を禁止したルールに違反しています。今一度「書き込みのルールについて」をご確認いただけますように、お願いいたします。

書き込みのルールについて
http://dobon.net/vb/bbs/index.html
お世話になります。


> ミックスドランゲージなので、VIsualStudioはProfessional以上
> でないとできません。
私が使用しているのは、Visual Studio 2010 Professinal です。
これならC#、VB混合で使用できるという意味でしょうか?
ならば、やり方を知りたいのですが、簡単にご教授していただける内容でしょうか?または、それを説明しているWebサイトなど教えていただけないでしょうか?
「C# ミックスドランゲージ」などでググってみたものの中々出てきませんでした。

> GanttChartコントロールはVBのコードで作成されていますので、そこに
> 追加でC#のWindowsフォームのプロジェクトを作成すれば、そのままツ
> ールボックス中のGanntChartをフォームに適用できます。
上記のミックスドランゲージというのは、この方法ですか?
新規でVBのプロジェクトを作ってC#のフォームを追加していくだけ?でしょうか?
(これより、やってみます。)


> ■No30243に返信(管理人さんの記事)
> No30230 の投稿が丸投げを禁止したルールに違反しているのではないかというご> 報告をいただきました。
確かにアップしたコードが「丸投げ」と指摘されても当然でした。
うっかりしていました。以後気を付けます。
お世話になります。

調べた結果、ミックスドランゲージは、わからないのであきらめ、VBをC#に変換した個所を調べることにしました。たぶん初期化が怪しいということはわかりましたが、下記の点を教えてください。


今まで修正した個所は、
objBmp = new Bitmap(this.Width - barStartRight, lastLineStop, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
が、
int lastLineStop=0; で初期化のため落ちていた。
lastLineStop = 10; にしたら回避できた。
のと、DateTime の初期化がVBと同じように null(Nothing)が指定できないために落ちた?(ここは推測です)
ですが、下記の部分がわかりません。コードの一部抜粋です。


private List<Header> GetFullHeaderList()
{
List<Header> result = new List<Header>();
System.DateTime newFromTime = new System.DateTime(FromDate.Year, FromDate.Month, FromDate.Day);
string item = string.Empty;


Header header = new Header();

header.HeaderText = item;
header.Time = new System.DateTime(newFromTime.Year, newFromTime.Month, newFromTime.Day, newFromTime.Hour, newFromTime.Minute, 0);

result.Add(header); ここで落ちる。

色々、文をコメントにして調べた結果、たぶん残りはここ?ぐらいと思います。
ListのHeaderは、下記にありますが、C#は変換したものVBは、原文です。

おかしな部分がありましたらご指摘ください。


C#のコード
private class Header
{
private string _headerText;
private int _startLocation;

private System.DateTime _time = new DateTime(0);
public string HeaderText
{
get { return _headerText; }
set { _headerText = value; }
}

public int StartLocation
{
get { return _startLocation; }
set { _startLocation = value; }
}

public System.DateTime Time
{
get { return _time; }
set { _time = value; }
}
}

VBのコード
Private Class Header

Private _headerText As String
Private _startLocation As Integer
Private _headerTextInsteadOfTime As String = ""
Private _time As Date = Nothing

Public Property HeaderText() As String
Get
Return _headerText
End Get
Set(ByVal value As String)
_headerText = value
End Set
End Property

Public Property StartLocation() As Integer
Get
Return _startLocation
End Get
Set(ByVal value As Integer)
_startLocation = value
End Set
End Property
変換ソフトは使わなかったので試しにやってみましたが・・・
かなりヒドイ変換結果ですネ!

DateTime型は、Nothging => null ではなく、DateTime.MinValue に
するようです。

if文のAnd/Or も『&&』や『||』になるはずが、『&』や『|』になっ
たりもしてますネ・・・

※ 変換ソフトによって、変換ミスの箇所は異なると思いますので、
  変換後のコードを詳細に見ないと、部分的な指摘のみでは解消
  できないと思います。

コツコツ直していけば動作するようになります。

※ C#で修正をしたところ、異常終了せずに動作するようになりま
  した。C#のGanttChartで、C#のWindowsフォームでです。

頑張って下さい!

以上。
■No30248に返信(Hiroさんの記事)
>>ミックスドランゲージなので、VIsualStudioはProfessional以上
>>でないとできません。
> 私が使用しているのは、Visual Studio 2010 Professinal です。
> これならC#、VB混合で使用できるという意味でしょうか?

mixed languageといわれると,一つのアセンブリの中に複数言語,という気がしますが……。

それはさておき,一つのアセンブリを作成するためにC#とVBを混ぜることはVSではできませんが,
他の言語で書かれたアセンブリを参照設定することは可能です。
なので,ソリューションにVBのクラスライブラリプロジェクトの追加し,
そのプロジェクトをC#のプロジェクトから参照することで,C#とVB双方のコードをま交ぜることができます。

mixed languageなアセンブリを作るのはお薦めしません。
/t:netmoduleでコンパイルしたモジュールを作ってそれをリンクすればできますが,
VSからはサポートしていませんし,意味がありません。
# さらに,別途コンパイルしたモジュールは.dll/.exeとは別ファイルになります。


■No30263に返信(オショウさんの記事)
> if文のAnd/Or も『&&』や『||』になるはずが、『&』や『|』になっ
> たりもしてますネ・・・

それは何も問題が無いような……。
AndAlsoやOrElseが&や|になるのであれば問題だと思いますが……。
お世話になります。

> mixed languageといわれると,一つのアセンブリの中に複数言語,という気がしますが……。
>
私もそう思ってましたが違うんですね。ググってもなかなか出てこないので不思議だったんですが。

> mixed languageなアセンブリを作るのはお薦めしません。
あきらめがつきます。

> DateTime型は、Nothging => null ではなく、DateTime.MinValue に
> するようです。
> if文のAnd/Or も『&&』や『||』になるはずが、『&』や『|』になっ
> たりもしてますネ・・・
この二つは、私も気づいて直しました。


> コツコツ直していけば動作するようになります。
はい、30262 で書いた部分がわかりません。List.Add() の部分で落ちます。
単に、初期化ではないのでしょうか?

普通というか私がよくやるのは、このHeaderクラス内にコンストラクターを作って
Add(new Header()); などとやるのですが、VBのサンプルではそうなっていません。これでは、いくつか追加するとその最後のみが有効にしかならない気がします。
VBは、これが普通ですか?それともこれを意図してる???
>> コツコツ直していけば動作するようになります。
>はい、30262 で書いた部分がわかりません。List.Add() の部分で落ちます。
>単に、初期化ではないのでしょうか?

オショウさんの書き込みより、

>DateTime型は、Nothging => null ではなく、DateTime.MinValue に
>するようです。

とありますので、

private System.DateTime _time = DateTime.MinValue;

みたいにすればいいのではないでしょうか。


>これでは、いくつか追加するとその最後のみが有効にしかならない気がします。

この文章の前後を含め、貴方の質問の意図が読み取れないのですが、一体何を懸念しているのでしょうか?
クラスのインスタンスについて理解しているのであれば、このような質問が出てこないと思いますが、どうでしょう。
お世話になります。

努力をしてますが、List.Add() のところでのエラーが回避できません。
その関数の全文を下記に示しますが、 result.Add(header); というエラー箇所の分が全部で2つあり、前半はOKのようです。後半のこの文が問題のようですが、コンパイラーは、通ります。

ツールボックスにできたコンポーネントをフォームにドラッグするとエラーになりそのメッセージは、「インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。」と、今は、表示されるようになりました。

 それまでは、VS2010が落ちていました。

この後半のresult.Add() をコメントにすると上記のエラーは出ません。

また、日付の初期化部分は、どうやら関係ないみたいですが、下記のようにしました。
= new DateTime(DateTime.MinValue.Year, DateTime.MinValue.Month, DateTime.MinValue.Day); 


エラーの出る関数です。
private List<Header> GetFullHeaderList()
{
System.DateTime newFromTime = new System.DateTime(FromDate.Year, FromDate.Month, FromDate.Day);
string item = string.Empty; // null;

TimeSpan interval = ToDate - FromDate;

List<Header> result = new List<Header>();
try
{
if (interval.Days > 1)
{
while (newFromTime <= ToDate)
{
Header header = new Header();

header.HeaderText = "";
header.Time = new System.DateTime(newFromTime.Year, newFromTime.Month, newFromTime.Day, 0, 0, 0);
result.Add(header); ****** ここは、エラーなし

newFromTime = newFromTime.AddDays(1);
// The minimum interval of time between the headers
}
}
else
{
var _with1 = newFromTime;
newFromTime = _with1.AddHours(FromDate.Hour);

if (headerFromDate.Minute < 59 && headerFromDate.Minute > 29)
{
newFromTime = _with1.AddMinutes(30);
}
else
{
newFromTime = _with1.AddMinutes(0);
}

while (newFromTime <= ToDate)
{
item = newFromTime.Hour.ToString() + ":";

if (newFromTime.Minute < 10)
{
item += "0" + newFromTime.Minute.ToString();
}
else
{
item += "" + newFromTime.Minute.ToString();
}

Header header = new Header();
header.HeaderText = item;
header.Time = new System.DateTime(newFromTime.Year, newFromTime.Month, newFromTime.Day, newFromTime.Hour, newFromTime.Minute, 0);
result.Add(header); ****** ここがエラーになる *****

newFromTime = newFromTime.AddMinutes(5);
// The minimum interval of time between the headers
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}

return result;
}
このソース、CodeProjectでの掲載内容から大きく変更されているようですが...

デザイナでこのコントロールを追加するとエラーになるとの事ですが、本当に

> result.Add(header);

の行でエラーになっているのでしょうか?
他のソースを見ていないので推測の域を出ないのですが、このメソッドを呼び出している箇所(DrawHeader)
がおかしいのではないかとまず疑いたくなりますが。

とりあえず、デザイナでこのコントロールを追加せず、コード上でこのコントロールをフォームに追加して、
その様子をステップ実行で調べてみてはどうでしょうか。
> 他のソースを見ていないので推測の域を出ないのですが、このメソッドを呼び出している箇所(DrawHeader)
> がおかしいのではないかとまず疑いたくなりますが。

  なかなか解決に至りそうにないので・・・

  コントロールをフォームに貼った瞬間に落ちるなら、実際に
  異常が発生している仮称は、『OnPaint』になるはずです。
  なんせそこが呼び出されるので・・・

  で、OnPaintの中身としては、『PaintChart』を呼び出して
  いるので、PaintChartの中を追っかければ解消できると思い
  ます。

  PaintChartの怪しそうな関数呼び出しをコメントアウトして
  みたら、どこで落ちてるか解ると思います。

  と言うか、ソースコード見て解らないなら、他人のコードを
  使うべきではない。と思いますが。

以上。参考まで
お世話になります。

中々解決しなくてお騒がしております。
エラーの箇所が今度こそは特定したと思いますが、直し方がわかりません。

お知恵をお貸しください。
エラーメッセージは、「インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。」とあったので List.Add()では、ちょっと違うみたいなのでもう一度見直してみました。

すると下記の箇所ならこのエラーメッセージと一致しますしこの箇所をコメント扱いにするとList.Add()でのエラーも消えました。
shownHeaderList[1].Timeの添え字が1であることが問題のようです。0ならエラーにならない。

ここをどのような形にか書き換えることは可能でしょうか?


private List<Header> shownHeaderList = new List<Header>();

private void DrawBars(Graphics grfx, bool ignoreScrollAndMousePosition) //= false)
{
if (shownHeaderList == null)
return;
if (shownHeaderList.Count == 0)
return;

int index = 0;

TimeSpan timeBetween = shownHeaderList[1].Time - shownHeaderList[0].Time;  <−ここ
int minutesBetween = (timeBetween.Days * 1440) + (timeBetween.Hours * 60) + timeBetween.Minutes;
dynamic widthBetween = (shownHeaderList[1].StartLocation - shownHeaderList[0].StartLocation);
decimal perMinute = widthBetween / minutesBetween;

VB原文の同箇所
Private Sub DrawBars(ByVal grfx As Graphics, Optional ByVal ignoreScrollAndMousePosition As Boolean = False)
If shownHeaderList Is Nothing Then Exit Sub
If shownHeaderList.Count = 0 Then Exit Sub

Dim index As Integer = 0

' Finds pixels per minute
Dim timeBetween As TimeSpan = shownHeaderList(1).Time - shownHeaderList(0).Time
Dim minutesBetween As Integer = CInt(timeBetween.TotalMinutes) '(timeBetween.Days * 1440) + (timeBetween.Hours * 60) + timeBetween.Minutes
Dim widthBetween = (shownHeaderList(1).StartLocation - shownHeaderList(0).StartLocation)
Dim perMinute As Decimal = widthBetween / minutesBetween
shownHeaderList[1]で「インデックスが範囲を超えています」エラーになるということは、
shownHeaderList内に要素が1つしかないということです。
(shownHeaderList.Countが0であれば、ここにはたどり着かない)
であれば、上記箇所の修正ではなくて、shownHeaderListに要素がどのように追加されているかを
調べる必要があります。
(元々のVBのソースに問題があるのか、C#に変換したものに問題があるのか、あるいは手を加えた部分に
バグがあるのか、分かりませんが)
お世話になります。

■No30307に返信(ズッカさんの記事)
> shownHeaderList[1]で「インデックスが範囲を超えています」エラーになるということは、
> shownHeaderList内に要素が1つしかないということです。
このコードは、コンポーネントとしてツールボックスに登録されるものでプログラムが実行される前のツールからフォームに張り付ける際にエラーになります。

ですので下記のご指摘の時点では、まだ要素内は空です。なにも登録されていません。


> (shownHeaderList.Countが0であれば、ここにはたどり着かない)
> であれば、上記箇所の修正ではなくて、shownHeaderListに要素がどのように追加されているかを
> 調べる必要があります。
確かに
if (shownHeaderList == null)
  return;
if (shownHeaderList.Count == 0)
  return;
この文があれば、通常のデバッグならエラーを回避すると思いますが、フォーム貼り付け時にOnPaint()が呼ばれている(先の投稿より)ならば、ここはどのように判断されているのでしょうか?

null や 0 で回避しているなら「インデックスの範囲外・・・(行番号も一致)」は、回避していると思います。

実際には、ここをコメントにするとエラーがでない。フォーム貼り付け時、ここのコメントを解除するとエラーになる。

まぁ、初期値がないからnullでなく0件って解釈してるなら・・・0はよくても1はだめ?(<- 都合よく解釈してみました。これがいいなら初期値で2つ与えておいて、プログラム開始時にそれを消してやって新たに追加すればいいかも。都合のいい解釈ですが・・・)
> このコードは、コンポーネントとしてツールボックスに登録されるものでプログラムが実行される前のツールからフォームに張り付ける際にエラーになります。

私が■No30282でアドバイスした

> とりあえず、デザイナでこのコントロールを追加せず、コード上でこのコントロールをフォームに追加して、
> その様子をステップ実行で調べてみてはどうでしょうか。

は試してみましたか?

> ですので下記のご指摘の時点では、まだ要素内は空です。なにも登録されていません。

「まだ要素内は空」であることを、どうやって確認しましたか?
(デザイナでフォームに貼り付けただけだから、要素内は空のはず、というのは誤りです。)

デザイナで貼り付けてエラーになるのであれば、OnPaintもそうですが、コンストラクタからの動作も確認すべきでは?

こういうコントロールを作成してデザイナで使いたいのであれば、少なくともデザイナで貼り付けた時に
何が行われるかをもっと理解する必要があると思いますよ。
お世話になります。

アドバイスくださった方々にお礼と感謝申し上げます。
とりあえずサンプルとして動作確認できるようになりました。
ただ、これが役にたつかどうかは、これからです。まだ、単にコード変換が何とかなっただけです。
やっとこのあぷりの検証ができます。


>>ですので下記のご指摘の時点では、まだ要素内は空です。なにも登録されていません。
>
> 「まだ要素内は空」であることを、どうやって確認しましたか?
> (デザイナでフォームに貼り付けただけだから、要素内は空のはず、というのは誤りです。)
>
はい、「要素内は空のはず」だと信じてました。
実行するフォームに張り付けてみたところshownHeaderListの中身は、1件でした。
のでshownHeaderList[1].Time はエラーになってました。もちろんこれは推測の範囲でしたがコントロールコンポーネント内では確認のすべがなく
if (shownHeaderList.Count < 2)
   return;
とすることで回避する考えは、浮かびませんでした。(実際、これで回避しました)


> デザイナで貼り付けてエラーになるのであれば、OnPaintもそうですが、コンストラクタからの動作も確認すべきでは?
> 何が行われるかをもっと理解する必要があると思いますよ。
デザイナで張り付けるだけでコードが実行するとは最初考えていませんでした。
考えてみれば、textBox を張るだけでその形をあらわしているということは、なにがしかのコードが実行されているという意味なのですね。
(今までのアドバイスでなにがしか実行されていることを理解)

今回の件でコントロール・コンポーネントが複雑だと重々知らされました。
解決済み!

DOBON.NET | プログラミング道 | プログラミング掲示板