Excelのレコードクリア

解決


きのこ  2004-08-16 11:33:19  No: 115618  IP: [192.*.*.*]

毎回とてもお世話になっています。
環境: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

これだとレコード自体削除されませんでした。

編集 削除
特攻隊長まるるう  2004-08-17 12:04:45  No: 115619  IP: [192.*.*.*]

>うまくいきません。
もっと原因を調べられる情報を書いて下さい。エラーメッセージが出るなら
必ずそれを書いて下さい。
こちらの環境では
>この ISAM では、リンク テーブル内のデータを削除することはできません。
>(Error 3617)
が出ますが?そもそも許可されていない事は出来ないと思いますが?。

マイクロソフトのサポートページで対応が載ってます。
http://support.microsoft.com/default.aspx?scid=kb;ja;257819

編集 削除
きのこ  2004-08-17 13:43:09  No: 115620  IP: [192.*.*.*]

>  この 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文が間違っていると思うのですが
ヒント下さい。お願い致します。

編集 削除
特攻隊長まるるう  2004-08-17 14:52:09  No: 115621  IP: [192.*.*.*]

そもそも
>ボタンが押された時に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 でもいい気がします。

編集 削除
きのこ  2004-08-17 16:03:37  No: 115622  IP: [192.*.*.*]

>そもそも
>>ボタンが押された時に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行目まで削除ではなく、レコードすべてを削除したいんです。

編集 削除
特攻隊長まるるう  2004-08-17 18:30:35  No: 115623  IP: [192.*.*.*]

多少理解できました。
文句ばっかり言って申し訳ないですが、このあたりの理解を深めて頂かないと
何がやりたいのかも伝わりませんので頑張って下さい。

まず、ちょっと話題が外れますが、『レコード』という言葉の使い方が大雑把に
感じますので少しそちらの話題で書き込みします。この掲示板で『レコード』と
いう表現を使った場合、データベースのグループ化された入れ物の事だと思って
下さい。
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 で設定できましたかねぇ?(忘れました)デフォルトは『横に詰める』だった
と思うんですが…『上に詰める』だったら当然ながら処理はおかしくなります。
でも最後のレコードしか消えないと言うのであれば、別の処理が関係していると
思われます。現象が再現できるコードを調べ直して下さい。

編集 削除
きのこ  2004-08-18 13:10:49  No: 115624  IP: [192.*.*.*]

いろいろありがとうございます。
勉強不足なのは十分承知です。
なので教えていただきたく思っております。

Excelは表になっています。
なので、1行目や2行目には項目名があるので6行目からの書き込みにしたいのです。

>1、DAO.Recordset は無視してシートのデータを直接消してしまう。(きのこさんの考えた方法です)
>2、DAO.Recordset.Fields(i) = "" で上書き保存する。
>上書きの処理を載せていただいておりませんので分かりませんが、どうせデータを
>書き込むなら、一般には削除するだけ無駄となります。2、の方法でも問題ない
>ケースも有り得ます。不都合があるならその理由を書いていただかないと、ボクには
>伝わりません。ですから謎です。

ListView上では削除もできるようになっているので
Excelのセル内を削除したほうがよいと考えたので1の方法の処理を選びました。

Excelのセル内を削除する方法を教えていただきたいです。
それには情報がたりませんか?

編集 削除
特攻隊長まるるう  2004-08-18 15:59:38  No: 115625  IP: [192.*.*.*]

>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

編集 削除
特攻隊長まるるう  2004-08-18 16:13:38  No: 115626  IP: [192.*.*.*]

注意:
Excel オブジェクトの参照の開放や途中でユーザがブックを
閉じたときのエラー処理なんかは入れてません。
あくまでサンプルですので問題が起こったら各自で対処して下さい。
実際問題 Command2 と Command4 は必要ないです。(コメントにしてますが)

編集 削除
特攻隊長まるるう  2004-08-18 18:43:23  No: 115627  IP: [192.*.*.*]

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

編集 削除
きのこ  2004-08-23 10:58:48  No: 115628  IP: [192.*.*.*]

レス遅れてしまって申し訳ありません。

これから特攻隊長まるるう殿に教えていただいたソースを
自分なりにやってみますので
少しお時間をいただきたく思います。
わからないところがありましたら
またお聞きします。

編集 削除
きのこ  2004-08-23 11:39:21  No: 115629  IP: [192.*.*.*]

ひとつ質問です。

以前私がソースを提示したとき
>削除する方法はきのこさんのコードであってます。
と言っていただきました。

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上をカウントできないということですか?

でしたらどうすればよいでしょう?

編集 削除
nanashi  2004-08-23 12:10:29  No: 115630  IP: [192.*.*.*]

私はExcelをDAOで読み込んだことが無いのでよく分かりませんが…。

> これをデバックすると
> RS.RecordCount=<オブジェクト変数またはWithブロック変数が設定されていません。
> と、表示されます。
> エラーにはなりません。

エラーです。
On Error Resume Nextとかにしてませんか?
普通にやってればそれはエラーで止まります。

このエラーはメッセージ通り、オブジェクト変数に実体が入ってないために起こります。
ちゃんとRSにオブジェクトをセットしてないんじゃないでしょうか?

編集 削除
きのこ  2004-08-24 10:42:46  No: 115631  IP: [192.*.*.*]

できました!!
特攻隊長まるるう殿に提示していただいたもので
思っていたものができました。
どうもありがとうございました。

編集 削除