毎回とてもお世話になっています。
環境:Win2000 VB6.0
Excelから読み込んでListViewに表示させて、もし変更などがあったら
変更をして、ボタンを押されたらExcelに書き込むというものを作っています。
ボタンが押された時にExcelに書き込むことはできているのですが
ボタンが押された時は一度Excelのレコードを削除してから
もう一度書き込みをしたいのですがうまくいきません。
なにか参考になるアドバイスお願い致します。
Dim DB As DAO.Database
Dim RS As DAO.Recordset
For i = 1 To ListView1.ListItems.Count
RS.Delete
Next i
これだとレコード自体削除されませんでした。
>うまくいきません。
もっと原因を調べられる情報を書いて下さい。エラーメッセージが出るなら
必ずそれを書いて下さい。
こちらの環境では
>この ISAM では、リンク テーブル内のデータを削除することはできません。
>(Error 3617)
が出ますが?そもそも許可されていない事は出来ないと思いますが?。
マイクロソフトのサポートページで対応が載ってます。
http://support.microsoft.com/default.aspx?scid=kb;ja;257819
> この ISAM では、リンク テーブル内のデータを削除することはできません。
> (Error 3617)
このエラーメッセージ出ます。
> マイクロソフトのサポートページで対応が載ってます。
というのは、
> 注 : レコードを削除するには、各フィールドの内容を個別に削除します。
のことですか?
Dim DB As DAO.Database
Dim RS As DAO.Recordset
Dim xlBook As Excel.Workbook
Dim xlSheet As Excel.Worksheet
n = 6
For i = 1 To RS.RecordCount
xlSheet.Cells(n, 1).Delete
xlSheet.Cells(n, 2).Delete
xlSheet.Cells(n, 3).Delete
xlSheet.Cells(n, 4).Delete
xlSheet.Cells(n, 5).Delete
xlSheet.Cells(n, 6).Delete
xlSheet.Cells(n, 7).Delete
xlSheet.Cells(n, 8).Delete
xlSheet.Cells(n, 9).Delete
xlSheet.Cells(n, 10).Delete
xlSheet.Cells(n, 11).Delete
n = n + 1
Next i
とやると、最後のレコードしか消えません。
For文が間違っていると思うのですが
ヒント下さい。お願い致します。
そもそも
>ボタンが押された時にExcelに書き込むことはできているのですが
>ボタンが押された時は一度Excelのレコードを削除してから
>もう一度書き込みをしたいのですがうまくいきません。
といった処理の必要性がボクには謎なんですが、
今更 Worksheet オブジェクトを使うなら DAO を使う必要が
あるのか?ってのも謎です。で、極めつけは i と何の関係も無い
n という変数が何の説明も無くいきなり現れて、6 からカウント
されてるというホントに意味不明なソースを見せられて…
…何を助言しろとおっしゃってるのか理解しがたいです。
>とやると、最後のレコードしか消えません。
6〜12行目が削除されてるとかじゃないんですか?
Worksheet オブジェクトを使ってデータを消すだけなら
[VB6.0]
With RS
xlSheet.Range("$A$1").Resize(.RecordCount, .Fields.Count).Delete
End With
で良いんじゃないですか?(^^;)1つにまとめてみて下さい。
.Delete するとセルが移動しますので、.Clear でもいい気がします。
>そもそも
>>ボタンが押された時にExcelに書き込むことはできているのですが
>>ボタンが押された時は一度Excelのレコードを削除してから
>>もう一度書き込みをしたいのですがうまくいきません。
>といった処理の必要性がボクには謎なんですが、
ボタンは1つです。
そのボタンが押された時にListViewにあるレコードがExcelに書き込まれます。
起動時(Form_Load)にはそのExcelからListViewに読み込まれます。
ListViewの内容は変更が可能になっています。
のでボタンが押された時には同じExcelに書き込みをするので一度Excelのレコードを削除してから
書き込みをしようと考えています。
>今更 Worksheet オブジェクトを使うなら DAO を使う必要が
>あるのか?ってのも謎です。
>xlSheet.Range("$A$1").Resize(.RecordCount, .Fields.Count).Delete
このソースでしたら両方使わないといけなくないですか?
>で、極めつけは i と何の関係も無い
>n という変数が何の説明も無くいきなり現れて、6 からカウント
>されてるというホントに意味不明なソースを見せられて…
iはレコードのカウントです。
nは6行目から書き込みをしたいためです。
>…何を助言しろとおっしゃってるのか理解しがたいです。
>>とやると、最後のレコードしか消えません。
>6〜12行目が削除されてるとかじゃないんですか?
6行目からの削除でいいんです。
12行目まで削除ではなく、レコードすべてを削除したいんです。
多少理解できました。
文句ばっかり言って申し訳ないですが、このあたりの理解を深めて頂かないと
何がやりたいのかも伝わりませんので頑張って下さい。
まず、ちょっと話題が外れますが、『レコード』という言葉の使い方が大雑把に
感じますので少しそちらの話題で書き込みします。この掲示板で『レコード』と
いう表現を使った場合、データベースのグループ化された入れ物の事だと思って
下さい。
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/jpado260/htm/mdobjrecord.asp
>ListViewにあるレコードがExcelに書き込まれます。
とか
>一度Excelのレコードを削除してから
といった表現は、多少プログラムに慣れている者にとっては非常に分かり辛いです。
ListView の場合は Item と表現します。Excel の場合はセル(範囲: Range)です。
>ListViewにあるレコードがExcelに書き込まれます。
をこちらが必要とする情報を含めて正しく書くなら
『ListView に表示されている文字列を DAO.Recordset.Fields(i) に設定し、
.Update コマンドを実行することで Excel のシート上に書き込みます。』
でしょうか?面倒に思うかもしれませんが、初心者の方の中には .Update コマンド
を忘れてるだけの人もいます。そういった間違いをなくすため、自分のやった
ことは"必要な事をもらさない程度に詳しく"、ヘルプなどで使われている"他人が
理解できる表現"で書くように心がけて下さい。
以上の内容でレコードと呼べる存在は DAO.Recordset の各行のみです。
Recordset は本来データベースを操作する為のもので、データベースを相手に
してこそレコードの意味が生き、ちゃんとした操作ができます。
Excel はデータベースではありません。レコードという入れ物の、セルアドレスに
対応した場所にセルのデータを入れているだけであって、レコードと Excel の
セルは全く別の存在だということを意識しておいて下さい。
>ボタンは1つです。
>そのボタンが押された時にListViewにあるレコードがExcelに書き込まれます。
>起動時(Form_Load)にはそのExcelからListViewに読み込まれます。
>ListViewの内容は変更が可能になっています。
>のでボタンが押された時には同じExcelに書き込みをするので一度Excelのレコードを削除してから
>書き込みをしようと考えています。
まず、DAO.Recordset のレコードは削除できないらしい…という事は理解して
いただけたと思います。ここで一般には2つの方法を思いつきます。
1、DAO.Recordset は無視してシートのデータを直接消してしまう。(きのこさんの考えた方法です)
2、DAO.Recordset.Fields(i) = "" で上書き保存する。
上書きの処理を載せていただいておりませんので分かりませんが、どうせデータを
書き込むなら、一般には削除するだけ無駄となります。2、の方法でも問題ない
ケースも有り得ます。不都合があるならその理由を書いていただかないと、ボクには
伝わりません。ですから謎です。
>このソースでしたら両方使わないといけなくないですか?
上にあげた方法なら DAO のみで出来ますし、Worksheet オブジェクトを使うなら
Variant 型の配列でデータを一括して読み込む方法もあります。
Dim Datax() As Variant
Datax = xlSheet.Range("$A$1").Resize(20 + 1 , 11 + 1).Value
配列の Index が1つずれるので注意が必要ですが、DAO のみか Worksheet オブジェクト
のみでも実現可能です。特に問題ないのであれば、使う技術は共通化を図ったほうが
理解しやすいですし、ソースもすっきりします。不必要なオブジェクトも生成しなくて
よくなります。ですから複数の技術を使った今回のコードはボクにとって謎です。
>6行目からの削除でいいんです。
>12行目まで削除ではなく、レコードすべてを削除したいんです。
何の説明も無く 6 行目から削除して
>とやると、最後のレコードしか消えません。
と書いてあったからレコードカウントが実は 6 で 6 行目から削除しているので
最後のレコードが消え、そこから余分な 6 カウントの処理が走りますから
12 行目まで削除されていると予想しました。そこをチェックして欲しかったんですが?
とりあえず違うみたいなのでいいですが、Delete メソッドを複数回実行するのは
処理としていまいちです。Excel シートの Range 範囲を計算して1処理にした
方が良いでしょう。何か理由があって嫌な場合でも、少なくとも For 文は 6 回
余分に動いてますから、そこは直した方が良いでしょう。
>とやると、最後のレコードしか消えません。
Worksheet オブジェクトを使った Delete は DAO とは関係ないですし、削除が
6 行からで良いならこのコードでも処理としてはお望みの結果を実現していると
思います。Delete した時のセルの動きはどうなってますか?『上に詰める』…は
Excel で設定できましたかねぇ?(忘れました)デフォルトは『横に詰める』だった
と思うんですが…『上に詰める』だったら当然ながら処理はおかしくなります。
でも最後のレコードしか消えないと言うのであれば、別の処理が関係していると
思われます。現象が再現できるコードを調べ直して下さい。
いろいろありがとうございます。
勉強不足なのは十分承知です。
なので教えていただきたく思っております。
Excelは表になっています。
なので、1行目や2行目には項目名があるので6行目からの書き込みにしたいのです。
>1、DAO.Recordset は無視してシートのデータを直接消してしまう。(きのこさんの考えた方法です)
>2、DAO.Recordset.Fields(i) = "" で上書き保存する。
>上書きの処理を載せていただいておりませんので分かりませんが、どうせデータを
>書き込むなら、一般には削除するだけ無駄となります。2、の方法でも問題ない
>ケースも有り得ます。不都合があるならその理由を書いていただかないと、ボクには
>伝わりません。ですから謎です。
ListView上では削除もできるようになっているので
Excelのセル内を削除したほうがよいと考えたので1の方法の処理を選びました。
Excelのセル内を削除する方法を教えていただきたいです。
それには情報がたりませんか?
>Excelのセル内を削除する方法を教えていただきたいです。
>それには情報がたりませんか?
削除する方法はきのこさんのコードであってます。
>xlSheet.Cells(n, 1).Delete
>とやると、最後のレコードしか消えません。
これは最後の行だけは消える処理が成功しているという事ですよね?
削除する方法は多分あってるんだと思います。
じゃあ、原因はどこか?…デバッグしないと分かりません(^^;)。
情報が足りないと言えば足りないですが、どこにエラーがあるかは
限定できません。…ソースを全て載せられても…困りますし。
DAO を使わない(Worksheet オブジェクトと配列を使った)サンプル
を載せておきます。データを削除する処理は作ってありますが
使ってません。
[VB6.0]
Option Explicit
Private xlApp As Excel.Application
Private xlBook As Excel.Workbook
Private xlSheet As Excel.Worksheet
Private Const xlFilePath As String = "c:\test.xls" ' 既存のファイルを利用する場合に設定
Private Const TopCellRow As Long = 6 ' データとして利用する先頭行
Private Const TopCellCol As Long = 1 ' データとして利用する先頭列
Private mTopCellAddress As String
Private mLastCell As Excel.Range ' 使用されている最終セルを格納
' Excel ワークシートの仕様制限 65,536 行、256 列
Private Const RowMax As Long = 65536
Private Const ColMax As Long = 256
Private mData() As Variant
Private Sub Form_Load()
' 使用したコントロールは Command1 Command2 Command3 Command4 ListView1
Me.Command1.Caption = "Read"
Me.Command2.Caption = "Delete のみ"
Me.Command3.Caption = "Write のみ"
Me.Command4.Caption = "Delete Write"
With Me.ListView1
.View = lvwReport
.MultiSelect = True
.FullRowSelect = True
.Gridlines = True
End With
Set xlApp = CreateObject("Excel.Application")
' 既存のファイルが見つからない場合は、新規 Book
If Len(Dir(xlFilePath, vbNormal)) = 0 Then
Set xlBook = xlApp.Workbooks.Add
Else
Set xlBook = xlApp.Workbooks.Add(xlFilePath)
End If
Set xlSheet = xlBook.Worksheets(1)
xlApp.Visible = True
mTopCellAddress = GetCellAddressString(TopCellRow, TopCellCol)
Set mLastCell = xlSheet.Cells.SpecialCells(Excel.XlCellType.xlCellTypeLastCell)
End Sub
Private Sub Command1_Click()
' Dim i As Long
' Dim j As Long
mData = ExcelRead(mTopCellAddress, mLastCell.Row - TopCellRow + 1, mLastCell.Column - TopCellCol + 1)
' データの確認用コード
' Debug.Print LBound(mData, 1)
' Debug.Print LBound(mData, 2)
' For i = LBound(mData, 1) To UBound(mData, 1)
' For j = LBound(mData, 2) To UBound(mData, 2)
' Debug.Print "$" & IntToAlphabet(j + TopCellCol - 1) & "$" & CStr(i + TopCellRow - 1) & " : " & CStr(mData(i, j))
' Next
' Next
Call SetListViewData(Me.ListView1, mData)
End Sub
Private Sub Command2_Click()
' データを削除したい場合はコメントから戻して下さい。
' Call ExcelClear(mTopCellAddress, mLastCell.Row - TopCellRow + 1, mLastCell.Column - TopCellCol + 1)
End Sub
Private Sub Command3_Click()
' Dim i As Long
' Dim j As Long
Call GetListViewData(Me.ListView1, mData)
' データの確認用コード
' For i = LBound(mData, 1) To UBound(mData, 1)
' For j = LBound(mData, 2) To UBound(mData, 2)
' Debug.Print "$" & IntToAlphabet(j + TopCellCol - 1) & "$" & CStr(i + TopCellRow - 1) & " : " & CStr(mData(i, j))
' Next
' Next
Call ExcelWrite(mTopCellAddress, mData)
End Sub
Private Sub Command4_Click()
' Call Command2_Click
' Call Command3_Click
End Sub
Private Sub Form_Unload(Cancel As Integer)
Dim TempPath As String
TempPath = xlFilePath
If MsgBox(TempPath & " に保存しますか?", vbOKCancel) = vbOK Then
xlApp.DisplayAlerts = False
xlSheet.SaveAs TempPath
xlApp.DisplayAlerts = True
End If
On Error Resume Next
xlBook.Saved = True
xlApp.Quit
' オブジェクトを解放します。
Set xlSheet = Nothing
Set xlBook = Nothing
Set xlApp = Nothing
End Sub
Private Sub ExcelWrite(ByVal TopCellAddress As String, ByRef Datax() As Variant)
xlSheet.Range(TopCellAddress).Resize(UBound(Datax, 1), UBound(Datax, 2)).Value = Datax
End Sub
Private Sub ExcelClear(ByVal TopCellAddress As String, ByVal Rows As Long, Cols As Long)
xlSheet.Range(TopCellAddress).Resize(Rows, Cols).Clear
' Delete だとセルごと削除
' xlSheet.Range(TopCellAddress).Resize(Rows, Cols).Delete
End Sub
Private Function ExcelRead(ByVal TopCellAddress As String, ByVal Rows As Long, Cols As Long) As Variant()
ExcelRead = xlSheet.Range(TopCellAddress).Resize(Rows, Cols).Value
End Function
Private Function IntToAlphabet(ByVal NumberX As Long) As String
Dim wNum As Long
Dim wStr As String
If NumberX <= 0 Then
NumberX = 1
End If
' 26以上の時は再帰
wNum = (NumberX - 1) \ 26
If wNum >= 1 Then
wStr = IntToAlphabet(wNum)
End If
wNum = (NumberX - 1) Mod 26
IntToAlphabet = wStr & Chr(Asc("A") + wNum)
End Function
Private Function GetCellAddressString(ByVal Row As Long, ByVal Col As Long) As String
If RowMax < Row Then
MsgBox "行数に Excel ワークシートの制限を越えた値が代入されました。"
Row = RowMax
End If
If ColMax < Col Then
MsgBox "列数に Excel ワークシートの制限を越えた値が代入されました。"
Col = ColMax
End If
GetCellAddressString = "$" & IntToAlphabet(Col) & "$" & CStr(Row)
End Function
Private Sub SetListViewData(ByVal ListViewX As ListView, ByRef Data() As Variant)
Dim i As Long
Dim j As Long
Dim wItem As ListItem
With ListViewX
With .ColumnHeaders
.Clear
For j = LBound(mData, 2) To UBound(mData, 2)
.Add , , IntToAlphabet(j + TopCellCol - 1)
Next
End With
With .ListItems
.Clear
For i = LBound(mData, 1) To UBound(mData, 1)
For j = LBound(mData, 2) To UBound(mData, 2)
If j = LBound(mData, 2) Then
Set wItem = .Add(, , CStr(mData(i, j)))
Else
wItem.SubItems(j - 1) = CStr(mData(i, j))
End If
Next
Next
End With
End With
End Sub
Private Sub GetListViewData(ByVal ListViewX As ListView, ByRef Data() As Variant)
Dim wRowMax As Long
Dim wColMax As Long
Dim i As Long
Dim j As Long
With ListViewX
wRowMax = .ListItems.Count
wColMax = .ColumnHeaders.Count
With .ListItems
For i = LBound(Data, 1) To UBound(Data, 1)
For j = LBound(Data, 2) To UBound(Data, 2)
If i <= wRowMax And j <= wColMax Then
If j = LBound(Data, 2) Then
Data(i, j) = .Item(i)
Else
Data(i, j) = .Item(i).SubItems(j - 1)
End If
Else
Data(i, j) = ""
End If
Next
Next
End With
End With
End Sub
注意:
Excel オブジェクトの参照の開放や途中でユーザがブックを
閉じたときのエラー処理なんかは入れてません。
あくまでサンプルですので問題が起こったら各自で対処して下さい。
実際問題 Command2 と Command4 は必要ないです。(コメントにしてますが)
DAO のみ使用も少し試してみましたが、
RS.Fields(j - 1) = ""
で見た目は消えますが、セルを使用しているという情報は残るみたいですね。
WorkSheet オブジェクトで行削除しますかねぇ。うまい方法を思いつきません。
[VB6.0]見た目は消えると思われるコード(DAO のみ使用)
Private Sub WriteListViewDataToDAOExcel(ByVal ListViewX As ListView)
Dim DB As DAO.Database
Dim RS As DAO.Recordset
Set DB = OpenDatabase(xlFileName, False, False, "Excel 8.0;HDR=NO;")
Set RS = DB.OpenRecordset(xlSheetName)
Dim wRowMax As Long
Dim wColMax As Long
Dim i As Long
Dim j As Long
Dim wStartRow As Long
wStartRow = 6 - 1 ' Row は 0 始まり
RS.Move wStartRow
With ListViewX
wRowMax = .ListItems.Count
wColMax = .ColumnHeaders.Count
With .ListItems
i = 1
Do Until RS.EOF
RS.Edit
For j = 1 To RS.Fields.Count
If i <= wRowMax And j <= wColMax Then
If j = 1 Then
RS.Fields(j - 1) = .Item(i)
Else
RS.Fields(j - 1) = .Item(i).SubItems(j - 1)
End If
Else
RS.Fields(j - 1) = ""
End If
Next
RS.Update
i = i + 1
RS.MoveNext
Loop
End With
End With
End Sub
レス遅れてしまって申し訳ありません。
これから特攻隊長まるるう殿に教えていただいたソースを
自分なりにやってみますので
少しお時間をいただきたく思います。
わからないところがありましたら
またお聞きします。
ひとつ質問です。
以前私がソースを提示したとき
>削除する方法はきのこさんのコードであってます。
と言っていただきました。
n = 6
For i = 1 To RS.RecordCount
xlSheet.Cells(n, 1).Delete
xlSheet.Cells(n, 2).Delete
xlSheet.Cells(n, 3).Delete
xlSheet.Cells(n, 4).Delete
xlSheet.Cells(n, 5).Delete
xlSheet.Cells(n, 6).Delete
xlSheet.Cells(n, 7).Delete
xlSheet.Cells(n, 8).Delete
xlSheet.Cells(n, 9).Delete
xlSheet.Cells(n, 10).Delete
xlSheet.Cells(n, 11).Delete
n = n + 1
Next i
これをデバックすると
RS.RecordCount=<オブジェクト変数またはWithブロック変数が設定されていません。
と、表示されます。
エラーにはなりません。
それでループをしないでそのままFor文を抜けてしまいます。
なので1つしか削除されません。(ListViewでもし2つ以上削除削除した場合)
これって、Excel上でカウントされていないことになりますよね?
ということは
For i = 1 To RS.RecordCount
このFor文ではExcel上をカウントできないということですか?
でしたらどうすればよいでしょう?
私はExcelをDAOで読み込んだことが無いのでよく分かりませんが…。
> これをデバックすると
> RS.RecordCount=<オブジェクト変数またはWithブロック変数が設定されていません。
> と、表示されます。
> エラーにはなりません。
エラーです。
On Error Resume Nextとかにしてませんか?
普通にやってればそれはエラーで止まります。
このエラーはメッセージ通り、オブジェクト変数に実体が入ってないために起こります。
ちゃんとRSにオブジェクトをセットしてないんじゃないでしょうか?
できました!!
特攻隊長まるるう殿に提示していただいたもので
思っていたものができました。
どうもありがとうございました。