'##############################
'### レコード移動時のID採番 ###
'##############################
Private Sub Form_Current()
Me.btn_最新ID取得.Enabled = True '「btn最新ID取得」を使用可能に
Me.btn_追加.Enabled = False '「btn追加」を使用不可に(最新ID取得に導く仕掛け)
Const prefix As String = "L" '「Laminate」の頭文字 "L"
Dim maxID As String
maxID = DMax("fld_依頼ID", "T_依頼") '最終IDを取り出す
Dim lastNum As Long
lastNum = Replace(maxID, prefix, "") '最終IDから頭文字"L"を除き、数値型へ代入する
Dim newID As String
newID = prefix & Format(lastNum + 1, "000000") '+1して桁を揃えて頭文字"L"と結合
Me.txt_依頼ID.DefaultValue = "'" & newID & "'" '既定値へ代入
End Sub
Private InkeyEvent As Boolean
Private Sub 仕様書_KeyDown(KeyCode As Integer, Shift As Integer)
If InkeyEvent Then Exit Sub 'イベント連鎖防止
InkeyEvent = True
If KeyCode = vbKeyReturn Or KeyCode = vbKeySeparator Then
KeyCode = 0 'キー入力をなかっことに
SendKeys "^~" 'Ctrl + Enter
End If
InkeyEvent = False
End Sub
あるいは、下記のようにイベントプロシージャの付け外ししてもいいでしょう。
Private Sub 仕様書_KeyDown(KeyCode As Integer, Shift As Integer)
Me.仕様書.OnKeyDown = "" 'イベントプロシージャの割り当てを解除
If KeyCode = vbKeyReturn Or KeyCode = vbKeySeparator Then
KeyCode = 0 'キー入力をなかっことに
SendKeys "^~" 'Ctrl + Enter
End If
Me.仕様書.OnKeyDown = "[イベント プロシージャ]" 'イベントプロシージャの割り当て
End Sub
Private Sub 商品ID_BeforeUpdate(Cancel As Integer)
'//最初の登録の時は何もしない
If Me.Recordset.RecordCount <= 1 Then Exit Sub
With Me.Recordset.Clone
If Me!区分け <> !区分け Then
Me.Undo
MsgBox "区分けエラー"
End If
.close
End With
End Sub
Not IsNull(DLookup("区分け","Q取引明細","取引ID="&[取引ID]&" AND 明細ID<>"&[明細ID]&" AND 区分け='"&[区分け]&"'"))
で、同じ「区分け」を指定していると判断できます 入力規則で判定するなら、これを使って
=IIf(DCount("*","取引明細テーブル","取引ID=" & [取引ID])<=1 Or Not IsNull(DLookup("区分け","Q取引明細","取引ID="&[取引ID]&" AND 明細ID<>"&[明細ID]&" AND 区分け='"&[区分け]&"'")),[商品ID])
VBAでやるなら、現在サブフォームに読み込まれているレコード内で比較すればいいので
Private Sub 商品ID_BeforeUpdate(Cancel As Integer)
'//最初の登録の時は何もしない
If Me.Recordset.RecordCount <= 1 Then Exit Sub
With Me.RecordsetClone
If Me.CurrentRecord = 1 Then .Next '//先頭レコード同士で比較しない
If Me!区分け <> !区分け Then
Me.Undo
MsgBox "区分けエラー"
End If
.close
End With
End Sub
Private Sub 商品ID_BeforeUpdate(Cancel As Integer)
'//最初の登録の時は何もしない
If Me.Recordset.RecordCount <= 1 Then Exit Sub
Dim 区分け As String
区分け = DLookup("区分け", "商品テーブル", "商品ID=" & DLookup("商品ID", "取引明細テーブル", "取引ID=" & Me!取引ID & " AND 明細ID <> " & Me!明細ID))
If Me!商品ID.Column(2) <> 区分け Then
Me.Undo
MsgBox "区分けエラー"
End If
End Sub
最終手段としては、見積番号ごとの見積金額集計のクエリを別途作成し、
「qry見積_分類集計」と見積番号で結合させて、構成率を出そうかと思っています。
しかしながら、シンプルに「qry見積_分類集計」だけで何とかならないかと思っています。
「F_依頼履歴」って初めて出てきましたが、最初の「F_依頼一覧」というのはタイプミスしたということでしょうか。
あと、
「F_依頼入力」は非連結ということは、「F_依頼入力」のレコードソースは空欄ということですよね。
だとすると、話はまったく違ってきます。
「F_依頼履歴」の特定のレコードのIDをダブルクリックしたら、そのIDのレコードデータを非連結フォームの「F_依頼入力」に入力したいということですか。
非連結にしたい理由はなんでしょうか。
合理的な理由がなければ、連結フォームにするのをお勧めします。
そうすれば、
で解決するはずです。
hatenaさん、まさにそれがしたのです!
レコードソースは「T_依頼」にある「fld_依頼ID」です。
ちなみに、ダブルクリックをする「F_依頼履歴」のレコードソースは
「T_依頼」の選択クエリ「Q_依頼履歴」で、「F_依頼入力」は非連結です。
で、下記を記述して実行したところ
「フォームまたはレポートがテーブルまたはクエリを基に作成されていないため、
このアクションまたはメソッドは無効です」と出現し、黄色くなりました。
OpenFormしたときに他に何もしていなければ、先頭レコードが表示されています。
そこで、
Me.[txt_依頼ID].Value = OpenArgs
としたら、先頭レコードのIDが上書きされてしまいます。
ご希望のことはそれではないですよね。(通常主キーは上書きしないものです)
ご希望のことは、「F_依頼一覧」で選択したレコードと同じIDのレコードを表示したい、ということではないですか。
もし、そうなら、開く時にそのIDでフィルターをかければいいだけです。
依頼ID はレコードソースのフィールド名にしてください。
そうでないなら、もう少しやりたいことを詳細に説明してください。
hirotonさん、
>これは質問の表題からかけ離れた全く別の内容ですね。
あら、そうなんですね😅
私は、それが目的で代入したくていたのですが、
対処方法がまったく別になってしまうとは…。
上手く伝えられず、申し訳ないです💦
「L000004」を表示したいのです!
hirotonさん、その通りです!
とりあえず
これは質問の表題からかけ離れた全く別の内容ですね。これはこれでそれ用の対応が必要でしょう(今やり取りしている対応とは全く別に対応する必要がります)
この結果、実際には「txt_依頼ID」になにが表示されていますか?「L000004」が表示されていないと思ってhirotonは回答してますが合っていますか?
hatenaさん、
Form_Currentの自動採番のコードは「F_依頼入力」に記述してあります。
また、DoCmd.OpenForm "F_依頼入力", , , , , acDialog, Me.fld_依頼ID.Value は
「F_依頼一覧」に掲載されている既存レコードです。
「F_依頼入力」を使って、新規レコードを追加することが出来つつ
既存レコードの内容も表示できるようにしたいのです。
今回は、その既存レコードのIDを代入するにあたり、苦戦しております。
またイベント順は、hatenaさんの書いている通りになっていると思います。
hirotonさん、Form_Currentだけ残したところ、
ダブルクリックしても採番されたIDが代入されました。
それから、hatenaさんから提案して頂いた
のMe.[txt_依頼ID].Value = OpenArgsにブレイクポイントを置き
「L000004」をダブルクリックすると、Me.[txt_依頼ID].Value が最新IDの「L000005」
OpenArgs が「L000004」となっていました。
ちなみに、フォームを開いたとき、
Form_Open → Form_Load → Form_Current の順でイベントが発生することは念頭に置いてコーディングする必要があります。
このForm_Currentの自動採番のコードは、「F_依頼一覧」、「F_依頼入力」のどちらに記述されているものですか。
DoCmd.OpenForm "F_依頼入力", , , , , acDialog, Me.fld_依頼ID.Value
で開いたとき、カレントレコードはどうなってますか。既存レコードですか。新規レコードですか。とりあえず、上記の点を明確にしてください。
hatena様
毎度回答ありがとうございます。助かります。
Sendkeysでまたイベントが発生してしまっていたとは思いませんでした。
また、イベントの解除や割り当てができるなんて知りませんでした。
また勉強になりました。
Me.仕様書.OnKeyDown = ""
Me.仕様書.OnKeyDown = "[イベント プロシージャ]"
テキストボックスの「Enterキー入力時動作」プロパティを「フィールドに行を追加」で、解決なんですね。
今回はこれで対応しようと思います。
(イベントプロシージャ作らなくともよかったんですね。)
ありがとうございました。
TextBox.Value プロパティ (Access)
TextBox.DefaultValue プロパティ (Access)
Microsfot公式でもわざわざ注釈をつけるくらいですが、
value
とdefaultvalue
は別物です提示されたコードを見る限りは表示が変わるようなことは考えられません
それでも
Form_Current
が影響しているということであれば、フォーム読み込み直後にレコード移動が実行され(
Me!txt_依頼ID.Value=OpenArgs
したレコードは保存されて)、実際に表示されているレコードは「新規レコード」になっているとかでしょうか
「F_依頼入力」のすべてのコードを削除して
Form_Current
のコードだけにして試してみて下さい。もしくは、DoCmd.OpenForm
のところからデバッグ作業で実際の動きを確認する必要があるでしょう【超初心者向け】エクセルVBAでデバッグをする方法を解説します(経理・会計事務所向けエクセルスピードアップ講座さん)
hilotonさん、ありがとうございます!
コメントアウトしたら、ダブルクリックで代入は
できましたが、採番が出来なくなりました。
単純に
Form_Current
を削除する(コメントアウトする)とどうなりますか?>りんご様
ご回答ありがとうございます。申し訳ないのですが書いてある内容がよく理解できませんでした。
>hatena様
ご教授ありがとうございます。教えていただいた方法で実現できそうです!こちらで試してみたいと思います。
hatenaさん
チョット変化しましたので報告します。
ココが原因なのかな?と薄々思っていたのですが...。
自動採番する仕組みとなっておりまして...
という具合になっていますが
試しに、ココに Me.[txt_依頼ID].Value = OpenArgs を入れると
採番は出来なくなる一方、fld_依頼IDをダブルクリックすると代入できる
ようになりました。
自動採番の機能を残しつつ、代入の方法はありますか?
hatenaさん、早速ご対応頂きありがとうございます。
ご指摘のところを、何度も確認しましたが、
変化がありません。
「F_依頼一覧」の「fld_依頼ID」でのダブルクリックのイベントプロシージャは
Private Sub fld_依頼ID_DblClick(Cancel As Integer)
DoCmd.OpenForm "F_依頼入力", , , , , acDialog, Me.fld_依頼ID.Value
End Sub
に、入力ミスはありますか?
何度もスミマセン。
「F_依頼入力」フォームをデザインビューで開いて、プロパティシートのイベントタブの「開くとき」に
[イベント プロシージャ]
と設定されてますか。
もし、設定されていない場合は、ドロップダウンリストから選択して設定してください。
次に、「開くとき」のビルドボタン[...]をクリックして、下記のコードが表示されるか確認してください。
下記のように表示されていたら、上記のようになるように真ん中にコードを入力してください。
これで、IDが代入されるはずです。
hatenaさん、ありがとうございます!
ご提案頂いたコードを入力しましたが、
ダブルクリックしたIDは代入されませんでした。
「付箋のような画面」というのがフォームを想定しているなら、
New キーワード を使ってフォームのインスタンスを生成すれば可能です。
下記で似たようなことをしていますので、参考にしてください。
同じフォームを複数表示する - hatena chips
同じフォームを複数表示する その2 - hatena chips
acDialog(ダイアログモード)にすると、そのフォームを閉じるか非表示になるまで、次の行のコードは実行されません。
つまり、次の行の
Forms![F_依頼入力]![txt_依頼ID].Value = Me.fld_依頼ID.Value
が実行されるときには、[F_依頼入力]は既に閉じているので、「見つかりません。」というエラーになります。
対処法としてはいろいろありますが、OpenArgs引数で値を渡して、フォームの開くときイベントで代入する方法ですね。
F_依頼入力 の開くときイベント
キークリック(KeyDown)イベント内で、Enterキー押下をSendKeysしたら、それで再びキークリックイベントが発生して、と連鎖して繰り返されることになるのでそうなりますね。
下記のようにフラグを利用して、イベント連鎖を防止して、キー入力をキャンセルしておくといいでしょう。
あるいは、下記のようにイベントプロシージャの付け外ししてもいいでしょう。
ただ、こんなことをしなくても、
テキストボックスの「Enterキー入力時動作」プロパティを「フィールドに行を追加」にしておけば済みますけどね。
1つの画面に8つのリストボックスを置いて表示する感じにでしょうかね。ユーチューバー芸人を目指すならばネタとして評価されるかもしれませんが、アプリ開発言語で頑張る方が無難です。Accessに限らずお仕事にするなら大きなハンディキャップとして評価されるかもしれませんね。
hatenaさん指摘のように詳細の見えない質問ではありますが、マクロなんか使わなくてもACCESSの基本設計で対応できる案件な気がしますね
サブフォームを含むフォーム (一対多のフォーム) を作成する
情報不足です。下記の情報を提示してください。
関係するテーブルの名前、フィールド構成、主キー設定を提示してください。
入力するフォームは連結フォームなのか非連結フォームなのか。連結フォームならどのテーブルと連結しているか。
入力するのはどのフィールドなのか(連結の場合)、どのようなデータなのか(非連結の場合)。
登録先のテーブル名、フィールド名。
できれば、それぞれのテーブルのデータ例を提示して、どのようなデータを入力して、どのように反映させるのか例示してもらえると的確な回答が得られやすいでしょう。
hatenaさん
長らくありがとうございました。
無事に全ての問題を解決することができました。
ご教示いただき、誠にありがとうございました。
回答ありがとうございます。
テーブルに差フィールドを追加して、グラフ描画前にデータを随時更新する。
あらかじめ、クエリをオブジェクトとして作っておき、それをソースにモダングラフを作る。
こういう方針で、やってみたら少しずつ形になってきました。
今後も、なるべく用意されている機能で作っていこうと思います。
今回は、ご指導いただきありがとうございました。
グラフコントロールを使うなら、ソースに[差]フィールドが必要になるので、テーブルにフィールドを追加するか、クエリの演算フィールドを作成することになります。
前レコードとの差を求めるような処理はクエリは苦手なので複雑になります。テーブルにフィールドを追加できるならそちらの方がシンプルです。質問のコードを少し手直しするだけでできます。
グラフをレポートに表示させるなら、VBAですべて描画する(Lineメソッド等を使う)という方法もあります。フォームなら、直線コントロールをVBAで配置することになります。
自分はグラフを使ったこともあるのですが、結構使い方が難しく、自分の思い通りの表示にならないので、ちょっと苦手ということもあり、こちらの方法を使うかもしれません。ただ、グラフの要素をすべてコードが書く必要があるので、かなり敷居は高いと思います。なれれば自分の思い通りのグラフを作成することができます。また、描画時にVBAで差を計算すればいいので、[差]フィールドは不要です。
365なら「モダングラフ」が使えるのでこれを検討するのもいいかもしれません。私は使ったことがないのですが、使いやすそうです。(下記リンク参照)
Access2019のモダングラフの挿入(フォームやレポートで簡単グラフ作成) | Access 2019 | 初心者のためのOffice講座
まずは、上記の情報から、自身でいいと思うものを選択して取り組んでみてください。
それが一番わかりやすいですね。テーブルに追加するのは永続的に捨てデータができるだけだと思って敬遠していましたが、たいしたデメリットではないので、それで対応しようと思います。
今回はテーブルで対応すればいいのかなと思いますが、まだ方針が定まりません。最終的にはデータの差についての折れ線グラフ(それっぽい画像でもよい)描画が目的で、その手段が間違いであれば、根本的に考え直したいです。
0603のようなデータがある場合は、無視します。ロットを昇順にして並び替え、データがないものは無視し、あるものの中で直前のデータとの差を使います。
最終目標と現在のデータから考えて、思いついたのはグラフコントロールを使ってみる方法でしたが、それが最善であるかもわかっていません。
この考え方は全く知りませんでした。アクセスでグラフを作るのは大変だと認識しているので、この方法を検討してみたいと思います。
以上、初級者の舌足らずな説明で申し訳ございませんが、引き続きご指導のほどよろしくお願いします。
Null As [差]
というのは演算フィールドですね。演算フィールドを更新することはできません。この式なら Null 固定です。
テーブルに[差]というフィールドを追加することが可能なら、.edit .Update で更新することができます。
テーブルにフィールドを追加することができないなら、DLookupなどの定義域集計関数を使うか、サブクエリを使った演算フィールドを設定することになります。
どちらでご希望でしょう。
また、データが空欄の場合は、差はどうなるのでしょうか。
折れ線グラフはどのように作成する予定でしょうか。
グラフコントロールを利用するのでしょうか。
直線コントロールを使って自前でグラフを描画するようにすれば、差フィールドはなくても、描画するときに計算してもいいでしょう。
色々なアイデアありがとうございました(レスポンス遅くなりました)。
それぞれ試してみたのですが無反応でした。これは私が完全に理解しておらずVBAの間違い・不足によるものではと思ってます。
それで乏しい知識をひねり、対象の取引IDに対するDFirst関数で先頭のものを参照して2つ目以降を比較することで何とか上手くいきました。
もっと勉強してから頂いたアイデアも再トライしたいと思っています。
色々とお手数かけました。
結局、VBAコードを一括変更は怖いので、1つ1つ変更していきます。
hatena様の「処理を纏める」ことを行なっていきたいと思います。
一部修正
Recordset.Clone
を使うと編集前の状態でRecordsetが作成されるようです(考えてみれば当たり前)。最初のレコードの編集でも編集前と編集後を比較できるのでレコード位置のチェックは不要になりました参考
フォームの Recordset, RecorsetClone, RecordSet.Clone の違いとは?(hatena chipsさん)
本日教えて頂いたコードで上手く出来ました。
ありがとうございました。
現状、どこまでできていて、どの点でつまづているのか不明なので、とりあえず一般論として、下記のような設計になるでしょう。
データベースファイルを開いたときに、ログインフォーム的なものを表示させて、ユーザーを特定する。
ユーザーIDをどこかに保存しておく。
閲覧フォームを開くときに、そのユーザーIDでフィルターをかける。
補足
id名
変数を作らなくても、端にで、動くと思われます
変数に置くメリットとしては、rsのopen時間を減らす、ファイルの存在チェック(ファイル名の変更)ができるようになるなどです
書き出しなのでフィールドの存在チェックは不要でした
取引メインテーブルに区分けフィールドを追加して下さい。
取引メインフォームに区分けフィールド連結テキストボックスを追加して下さい。
そして、取引明細サブフォームの商品選択(コンボにて)の抽出条件に取引メインフォームの区分けフィールド連結テキストボックスを設定するような仕組みを考えて下さい。
例えば、ヘッダーに商品クエリのリストボックスを置く。取引メインフォーム区分け連結テキストボックスの値に応じて、動的に絞り込み、結果が表示されるようにする。必要な商品を選択して、ボタンを押せば、取引明細サブフォームの商品選択に反映されるようにする。
もっと言えば、サブフォームのレコードソースをクエリにして、「区分け」を含むようにすれば更に簡単ですね
「取引明細テーブル」と「商品テーブル」を
商品ID
で結合して区分け
フィールドを追加します自分のグループで自分以外に同じ区分けがあるか?をチェックすればいいので
で、同じ「区分け」を指定していると判断できます
入力規則で判定するなら、これを使って
VBAでやるなら、現在サブフォームに読み込まれているレコード内で比較すればいいので
このように実装できます
VBAでやるなら更新前処理でチェックすればいいでしょう
入力内容はなかったことにして元に戻すなら
Me.Undo
が使えます入力規則のように入力内容をチェックする必要はないので「区分け」の比較だけでいいですね。入力中のデータの方はコンボボックスのデータに「区分け」も含めるようにしておけば簡単に指定できます
コメントアップ時にChr(13)とChr(10)でエラーが出たので、
Chr(13)→Chr13
Chr(10)→Chr10
としました。