4つのテキストボックスにある文字をBASICでつなぎたいのです。
そして別のテキストボックスに表示したいです。
ふたつ悩んでます。
下記のコードを修正したいです。
===
質問①
下に記載したコードの左側に “@” をつけてます。
このひとつめの “@” のところで止まってしまいます。
BASIC ランタイムエラー.
‘1’
Type: com.sun.star.container.NoSuchElementException
Message:
と表示されます。
===
質問②
CANCATの使用法と記述がわかりません。
ふたつめの “@” の場所に記載した内容で良いのでしょうか?
上記①の所で止まってしまうので、検証できてません。
あてずっぽうで書いたので、まるで自信がないです。
===
質問③
ほかに間違ってる所があれば教えてください。
===
Sub ConcatAABBCCDD()
Dim oForm As Object
Dim oField1 As Object
Dim oField2 As Object
Dim oField3 As Object
Dim oField4 As Object
Dim oField5 As Object
Dim document As Object
Dim dispatcher As Object
document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService(“com.sun.star.frame.DispatchHelper”)
dispatcher.executeDispatch(document, “.uno:RecSave”, “”, 0, Array())
dispatcher.executeDispatch(document, “.uno:RecRefresh”, “”, 0, Array())
oForm = ThisComponent.Drawpage.Forms.getByName(“ABCDフォーム”)
Doc = ThisComponent
DocCtl = Doc.getCurrentController()
oField1 = oForm.getByName(“AA”)
oField2 = oForm.getByName(“BB”)
@ oField3 = oForm.getByName(“CC”)
oField4 = oForm.getByName(“DD”)
oField5 = oForm.getByName(“EE”)
@ AABBCCDD= concat(oField1.Text, oField2.Text, oField1.Text, oField4.Text)
CtlView = DocCtl.GetControl(oPutField)
CtlView.Text = AABBCCDD
oField5.commit()
dispatcher.executeDispatch(document, “.uno:RecSave”, “”, 0, Array())
dispatcher.executeDispatch(document, “.uno:RecRefresh”, “”, 0, Array())
End Sub
よろしくおねがいします。
当該コード、俺の環境でodbファイルのマクロから実行してみるも、DrawPageプロパティが見つけられずにエラーが出てます。odsファイル内のマクロから実行したりグラフから実行しているなら、XDrawPageSupplierがあるからわかるのですが…
Writerのときも、SwXTextDocumentがgetDrawPage()とgetDrawPages()の両方のメソッドを持つっぽい。なんでTextDocumentサービスにXDrawPagesSuppliersやXDrawPageSupplierがないのかはよくわからない。
実際、手元のodsファイルのマクロから、以下のコードが実行できてます。
oForm = ThisComponent.getSheets().getByName("Sheet1").DrawPage.Forms().getByName("Form1")
質問1 Type: com.sun.star.container.NoSuchElementExceptionってどういうエラー?
そのForm1にCCという名前のコントロールはちゃんと存在していますか?
質問2 Concat関数ってどうやって使うの?
StarBasicに存在する標準関数は、基本的に以下に載っている、インデントされていない部分にあるものだけだ。
https://opengrok.libreoffice.org/xref/core/basic/source/runtime/stdobj.cxx?r=7283125c#67
従ってConcat関数などという関数はStarBasicにはない。(表計算のCalcの数式の方にはある)
- AbsはDouble型の引数を1つ取り、Double型の値を返す関数(FUNCTION_)。
- Arrayはどうやら引数を取らないようだ。MS OfficeのArray関数って可変長引数っていう引数をいくつも並べられるものだった気がするが。
- AscはString型の引数を1つ取り、Long型の値を返す関数。
- AscWもString型の引数を1つ取り、Long型の値を返す関数。ただし、COMPATONLY_って書いてあるから、Option VBASupport 1が書かれていない場合は使用できない(…はずなんだけど、今手元で試したら再起動するまでVBASupport 1の効果が切れてなかった。なぜだろう(おぃ)
- CallByNameは引数を3つ取ってVariant型の値を返す関数。
Asc関数はどんなことやる関数なのか、を調べる事もできる。
https://opengrok.libreoffice.org/xref/core/basic/source/runtime/methods.cxx?r=e48e0632#295
プログラミング言語Cや同じくC++の知識がある人にはこれも見せておきたい
https://opengrok.libreoffice.org/xref/core/basic/source/inc/rtlproto.hxx?r=0f7a7c8e#25
そしてStarBasicにはJoin関数という、文字列連結用の関数が存在している。
https://opengrok.libreoffice.org/xref/core/basic/source/runtime/methods1.cxx?r=48314f25#1521
Sub Main()
Dim x(2) As String
x(0) = "a"
x(1) = "b"
x(2) = "c"
Msgbox(Join(x,","))
End Sub
このコードは"a,b,c"に相当する文字列をダイアログに表示する。
今後 質問2に対する回答で優しく書きたいけど書けなさそうなことリスト(上級編)。
- 以下の話は上記に載ってない関数とかに関わるCalcやWriterの話。今回のBaseには直接関係ないが、参考程度に。
- .NETの標準ライブラリは、関数がそのまま存在するのではなく、メソッドは全てクラスに属するようになっている。VB6にあった関数の多くも結構な割合が静的メソッドとしてMicrosoft.VisualBasic名前空間の各種クラスに属すものになっている
- 一般にこれらを使用するためには、クラス名.メソッド名(引数リスト)等と表記するが、初心者層が多いであろうこの言語に今までと少し変わった分類と書き方を習得させるのは少しハードルが高いとMSが思ったのか、.vbprojファイルに名前空間とクラス名を列挙しておくと、プログラマはそれらの記述を省略することが出来るようになっており、これによりVB6時代に近い書き方ができるようになっており、移行のハードルを下げようとした…のだろう。
- StarBasicも基本的にこれらに似た手法を採っている。(上記の関数リストは確かstdObjだか何だかのメソッドとして登録されていた気がする。多分追調査はしないけど。)
- CreateUNOService関数などでは、その引数にサービス名を指定するが、その時生成されるオブジェクトのC++上の名前は以下のような感じでLibreOfficeのコードに存在する。
vbaobj.component (revision f96926c1) - OpenGrok cross reference for /core/sc/util/vbaobj.component - Calcドキュメントにつけるマクロ上において、ooo.vba.excel.Globalsサービスに相当するオブジェクトは、Option VBASupport 1のときにのみVBAGlobalsという名前で参照できるが、この名前は省略することが出来、メソッドやプロパティ名から書き始めることが出来る。
- ところで、どうやら、これらのメソッドやプロパティのうち、getやsetという小文字の文字列で始まるメソッドはgetやsetを除いた名前のプロパティとして使用できるようだ。どういう仕組みでそうなっているのかはわからないが。
- そしてScVbaGlobalsには、getExcel()やgetActiveWorkbook()というメソッドが存在するのでExcelやActiveWorkbookという書き出し方ができる
vbaglobals.cxx (revision 9059457a) - OpenGrok cross reference for /core/sc/source/ui/vba/vbaglobals.cxx
余談 そこまでわかってて、マクロでGetObject()関数は使えますか?
- 先に述べた議論と同様、GetObject()という関数は存在しない。
- MS OfficeのVBAと同様、外部のDLLの関数を呼ぶ機能は存在する。
- Ole32.dllにCoGetObject関数が存在する
https://docs.microsoft.com/en-us/windows/desktop/api/objbase/nf-objbase-cogetobject - MS Officeは32ビット版と64ビット版が存在はしているものの、多分多くのエンドユーザが使っているのは32ビット版。そして、32ビット版と64ビット版では若干書き方が異なる。
- LibreOfficeも32ビット版と64ビット版が存在するんだけど、 DLLの呼び出し方は変わるだろうか?
- 「動いていること」は「安全なコードを書いた」ことと同値ではない。低レイヤ層に行けば行くほどそうなる。
- その辺を安全に検証できるか自分の力量に自信がなく怖気づいている。
暇人さん。いつもありがとうございます。
どうしようもない私にお付き合いくださって、感謝しています。
====
ご返答ありがとうございます。ほっとしています。
以前の質問に対して全くレスがつかないままなので心配してました。
====
ひとつめ
====
で、、これ↓ですが、
フォームで実施する「レイアウト作業」にとんでもないバグ?があるみたいです。
---------
最初、暇人さんからの以下の返信を見て
com.sun.star.container.NoSuchElementExceptionってどういうエラー?
そのForm1にCCという名前のコントロールはちゃんと存在していますか?
やっぱりそうなんだぁ。。だったのです。
oField1 = oForm.getByName("AA")
oField2 = oForm.getByName("BB")
までは止まらないのに、
oField3 = oForm.getByName("CC")
で止まってしまうので、何度も確認し、
もう最後には、"CC"の部分をまるままコピペして貼り付けてみたのですが、
やっぱりここで、止まってしまいます。
ですので自分の解釈(←どこにも教科書が無いので解釈です)が間違っているのかな。。
と思って質問してみたのですが。原因がまるで掴めませんでした。
で。頂いた返事を見てから、昨夜、もう一度見てみたら、コントトールの名前が
「テキストボックス1(←数字だけバラバラ)」に初期化されていたのです。
???と思って修正したのですが、ふと気がついて以下のことを試しました。
フォーム内のレイアウトを修正するとき、
ラベルフィールドと、テキストボックスフィールドをグループ化して移動しますよね?
でないと、ひとつひとつ動かさねばならず、グチャグチャになります。
これやると、つまりグループ化をすると、
フィールドの名前が強制的に初期化されてしまうのです。
いろいろ試しましたが、テキストボックスの場合は、
必ず全てで再現するわけではなく、約半数のテキストボックスがそうなります。
ですが再現するテキストボックスは、何度試しても必ず初期化されます。
今回の場合、“AA” “BB” は初期化されず、“CC” が初期化されていました。
何度試しても、“CC” だけ、必ず名前が初期化されてしまいます。
他にもたくさんありましたが、マクロを設定しない限りは普通に動きます。
最悪なのはチェックボックスです。ほぼ100%初期化されます。
探せば同じ症状のコントロールが他にもあるかもしれません。
いずれにしても、フォーム内のコントロールのグループ化をすべて解除し
消えていたコントロールの名前をつけ直したら、ここは通るようになりました。
====
ふたつめ
====
結局、うんうんと言いながら徹夜してしまいました。
文字の接続が、わかりません。
少しいじると、別のエラーが発生する繰り返しです。
わからないことだらけで降参です。教えてください。
現在は以下の状態です。
“AA” “BB” “CC” “DD” を空間無しで繋いで
“AABBCCDD” を oField4 に貼り付けたいです。
以下の左側 “@” で止まります。
BASIC ランタイムエラー.
‘380’
プロパティ値が正しくありません。
と出ます。
デバッグがそこで止まるのと、
その先のデバッグの仕方がまるでわからず、"@"以下の行は検証できていません。
------------
------------
Sub ConcatAABBCCDD()
Dim oForm As Object
Dim oField0 As Object
Dim oField1 As Object
Dim oField2 As Object
Dim oField3 As Object
Dim oField4 As Object
Dim document As Object
Dim dispatcher As Object
document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService(“com.sun.star.frame.DispatchHelper”)
dispatcher.executeDispatch(document, “.uno:RecSave”, “”, 0, Array())
dispatcher.executeDispatch(document, “.uno:RecRefresh”, “”, 0, Array())
oForm = ThisComponent.Drawpage.Forms.getByName(“ABCDフォーム”)
Doc = ThisComponent
DocCtl = Doc.getCurrentController()
oField0 = oForm.getByName(“AA”)
oField1 = oForm.getByName(“BB”)
oField2 = oForm.getByName(“CC”)
oField3 = oForm.getByName(“DD”)
oField4 = oForm.getByName(“EE”)
Dim AaBbCcDd As Object
Dim AaBbCcDdText(3) As String
@ AaBbCcDdText(0) = oField0
AaBbCcDdText(1) = oField1
AaBbCcDdText(2) = oField2
AaBbCcDdText(3) = oField3
AaBbCcDd = Join(AaBbCcDdText,"")
CtlView = DocCtl.GetControl(oField0, oField1, oField2, oField3, oField4)
CtlView.Text = AaBbCcDdText
dispatcher.executeDispatch(document, “.uno:RecSave”, “”, 0, Array())
dispatcher.executeDispatch(document, “.uno:RecRefresh”, “”, 0, Array())
End Sub
==========
よろしくおねがいします。
ふたつめ
多分、型が違う
Dim AaBbCcDd As String ' 型をStringに変更
Dim AaBbCcDdText(3) As String
AaBbCcDdText(0) = oField0.Text
AaBbCcDdText(1) = oField1.Text
AaBbCcDdText(2) = oField2.Text
AaBbCcDdText(3) = oField3.Text
AaBbCcDd = Join(AaBbCcDdText,"")
CtlView.Text = AaBbCcDd '代入する変数を変更
だとどうです?あ、これらoField0とかはテキストボックスだと仮定しています。
MS OfficeのVBAだと、多分各コントロールにデフォルトプロパティが設定されていて、今回の代入文など、演算がオブジェクトの型を要求しないために処理が実行できないときは、そのデフォルトプロパティが利用されたものとして再度評価を行うような仕組みがあります。(文法からそのオブジェクト自身に対する演算なのか、デフォルトプロパティの内容に対する演算なのか判断できなくなってしまうため、VB6にはSet構文やIs演算子が必要になっているわけですね。VB.NETでも移行する者たちのため多分こういうものは使えるようになっていますが、デフォルトプロパティを前提としない設計を中心にしているせいか、これらの構文を全く使わなくても大丈夫なようになっているはずです)
VBAからStarBasicに移行する人たちのためか、Option VBASupport 1のとき、一部だけ使えるようにしているのですが、StarBasicではこれらのコントロールにはデフォルトプロパティが設定されていないようです。個人的には初心者に媚びへつらっただけの悪い機能だと思っており、毎回ちゃんとTextプロパティを参照する形で書くことを勧めたいです。
これやると、つまりグループ化をすると、
フィールドの名前が強制的に初期化されてしまうのです。
気分に余裕のあるときに検証してみたいです。
以前の質問に対して全くレスがつかないままなので
自分は単純に答えがわからなかったんです。odsやodtのときは当該構文で出来るようなんですけどね…
実は一回手元で成功したんですが、その際IDEがクラッシュしてしまい、復元されてきたものに、成功したコードが含まれておらず、かつ、そのコードを再び書き上げることが出来ず、調査が難航しています。
あ、これらoField0とかはテキストボックスだと仮定しています。↓↓ ご推察のとおりです。
全てテキストボックスです。
実は、教えていただいた以下ですが、既に試していました。
それでも同じ場所で止まってしまうのです。
Dim AaBbCcDd As String ' 型をStringに変更
Dim AaBbCcDdText(3) As StringAaBbCcDdText(0) = oField0.Text
AaBbCcDdText(1) = oField1.Text
AaBbCcDdText(2) = oField2.Text
AaBbCcDdText(3) = oField3.Text
AaBbCcDd = Join(AaBbCcDdText,"")
CtlView.Text = AaBbCcDd '代入する変数を変更
これで出てくるメッセージは以下です。
BASIC ランタイムエラー.
'423'
Text
ちなみにエラーメッセージは変わっています。
型が Object のままだと、エラーメッセージは以下です。
BASIC ランタイムエラー.
'380'
プロパティ値が正しくありません。
ここで、更に試したことがあります。
Option VBASupport 1 をREMしてみました。
以下のようにメッセージ文が変わります
まさか、、Textプロパティ?メソッドが、無い???
なんてことは考えにくいと思うのですが。。
REM Option VBASupport 1 すると。
正しくありません。→見つかりません。に変わったのです???
BASIC ランタイムエラー.
次のプロパティまたはメソッドが見つかりません: Text。
もう諦めたほうが良いのでしょうか??
繰り返しますが、何をやっても「同じ行」で止まります。 エラーメッセージが変わるだけです。
自分はOption VBASupport 1は外して物事を考えることが多いです。今の実装だとエラーメッセージではなくエラー番号が出てしまいますし、前述のデフォルトプロパティのせいで本来の原因とは違うエラーメッセージが表示されたりして厄介なので。
次のプロパティまたはメソッドが見つかりません: Text
LibreOffice自体のC++のコードを読んだわけではありませんが、このプロパティが見つからないということは、「自分がその変数に代入されているものがテキストボックスだ誤解しているが、実は違うものが入っている」可能性があると考えてます。
自分なら、oField0をウォッチウィンドウに放り込んで中身を確認してみます。SupportedServiceNamesやImplementationNameが何になっているのか確認するわけですね。まぁ文字列とかだったらそのプロパティとかはないわけですが。
また、再現する最小限のファイルが作れ、個人情報等を含まず、俺に見られてもいいファイルを準備できるなら、そのファイルをどこかにアップロードして、「ちょっとhimajin100000さんや、実際のファイル提供するから再現するか、した場合何が原因か調べてくれんかのう?」という風にするのもアリです。まぁ解決できる保証はできないのですが。
深夜遅くのお返事。驚きました。ありがとうございます。
最後、どうしようもなければ、見ていただきたいです。
ですが、上記のコメントを拝見して、はたと気がついました。
oField0
oField1
oField2
oField3
は、テキストボックスを、入力ミスを避けるためにリストボックスに変換したものです。
それぞれ、他に入力用の一覧表として作成してある4つのフォームの1列面を参照します。
oField4 はテキストボックスです。
もしかしたらこれでは駄目なのでしょうか?
実は、oField4がプライマリーキーです。
つまり、
oField0
oField1
oField2
oField3
は、一意のプライマリーキーを生成するための要素として考えています。
ですが、プライマリーキーが無いとテーブルレコードの保存ができません。
ようするに、このマクロを実行する時は
oField0
oField1
oField2
oField3
がまだ保存されていない状態なのです。
これが駄目なら、
このテーブルでの oField4 (プライマリーキー)は、連番で発行し、
その状態でいったん保存して、
oField5をさらに作成しその次の処理で文字をつなげるしかないのかな。。
と気が付きました。
これで合っていますでしょうか?
ちなみにこのフォームは、
この処理で繋げるコードを作成するだけのためのものです。
ここで生成したコードを全てのレコードと紐づけて、操作したいのです。
追伸:
ちなみに、ウォッチウィンドウがわかりません。
これはなんですか?何のためのものですか?どこで見つければいいですか?
↓↓ ご推察のとおりです。
全てテキストボックスです。
リストボックスに変換したものです。
なんとなくそんな気はしてました。後でどんなプロパティがあるか調べておきます。
確かメールで届いたコメントにはウォッチウィンドウがどれかわからないという話が書いてあったのでひとまず図で示しておきます。
やはり、リストボックスにはTextプロパティは無いために起きています。エラーを出したStarBasicの主張は正しいです。
-
Writerを起動
-
メインメニューから[フォーム]→[コントロールウィザード]があればチェックを外す
-
[リストボックス]を選択してから、文書上でドラッグドロップしてリストボックスを配置します
-
このリストボックスを右クリック→[フォーム]
-
[名前]をForm1としました。
-
再びこのリストボックスを右クリック→[コントロール]
-
[名前]をListBox1にします。
8.さらに[リストの項目]に「a」「b」「c」の3つの項目を追加しました。なお、Shiftを押しながらEnterすると新しい項目を作れます。 -
さらに[標準選択範囲]に"0"を入力しました。
-
このような準備を整えてから、以下のコードを実行してみました。サブルーチン内でF8を押していくとステップ実行されます。なお、If文の中のExit Subが実行されたら誤りです
Option Explicit
Sub Main()
Dim lb As Object
lb = ThisComponent.DrawPage.Forms.getByName(“Form1”).getByName(“ListBox1”)
If IsEmpty(lb.SelectedValue) Then
Exit Sub
End If
Msgbox(lb.SelectedValue)
End Sub
そしたら、(この手のやつは0から数えるので) 0番目に相当する「a」がダイアログに表示されました。
ちなみに[標準選択範囲]を"1"にしていたら、「b」が表示されます
うげ、getByNameのメソッド名の途中に開き角括弧が紛れ込んでた。こそこそと編集(汗
暇人さん
ありがとうございます。
もう何度、この言葉を使わせていただいたでしょう。
情けないです。。
でもおかげさまで、少しづつですがわかってきた気がします。
でもまだまだ入口のところですけれど。
先に朗報から。
ここまで悩んでいた部分、すなわち4つのテキストの連結です。
ただおそらく、このフォームでの最後の難関?
にまたぶつかってしまいまいした。それは報告の後に記載します。
=====
①
フォーム上に新しいテキストボックスを、リストボックスと同じ数だけ作成しました。
全てのリストボックスを単にコピペして。それをリストボックスに変換しました。
②
新たなテキストボックスを、リストボックスの後ろ側に配置して、非表示にしました。
最初は単に後ろ側に配置しただけだったのですが、何故か上から透過して下側がダブって見えます。
たぶんそういう仕様なのだろうと判断して、コントロールの設定で非表示にしました。
③
コードを以下のようにしました。
別に理由はないのですが、なんとなく簡単に書きたかったので
JOIN は使用せず、&で繋いでいます。
以下のようになりました。
====
Sub ConcatAABBCCDD()
Dim oForm As Object
Dim oField0 As Object
Dim oField1 As Object
Dim oField2 As Object
Dim oField3 As Object
Dim oField4 As Object
Dim document As Object
Dim dispatcher As Object
document = ThisComponent.CurrentController.Frame
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
dispatcher.executeDispatch(document, ".uno:RecSave", "", 0, Array())
dispatcher.executeDispatch(document, ".uno:RecRefresh", "", 0, Array())
oForm = ThisComponent.Drawpage.Forms.getByName("ABCDフォーム")
Doc = ThisComponent
DocCtl = Doc.getCurrentController()
oField0 = oForm.getByName("AA")
oField1 = oForm.getByName("BB")
oField2 = oForm.getByName("CC")
oField3 = oForm.getByName("DD")
oField4 = oForm.getByName("EE")
AaBbCcDd = oField0.Text & oField1.Text & oField2.Text & oField3.Text
CtlView = DocCtl.GetControl(oField4)
CtlView.Text = AaBbCcDd
dispatcher.executeDispatch(document, ".uno:RecSave", "", 0, Array())
dispatcher.executeDispatch(document, ".uno:RecRefresh", "", 0, Array())
End Sub
=======
③
フォーム上に、ボタンを1つ作成して、上記のMACROを配置しました。
④
このボタンを押すと。ちゃんと。
oField4に、oField0.Text & oField1.Text & oField2.Text & oField3.Text された表示が出ます。
ところが。。。
保存ボタンを押すと、テーブル側のレコードに、oField4だけ保存されません。
NULLしてしまいます。
oField0.Text
oField1.Text
oField2.Text
oField3.Text
はレコードとして反映されているのにです。
もしかしたらこれかな??
CtlView = DocCtl.GetControl(oField4)
CtlView.Text = AaBbCcDd
と思い、ネットでビューの意味を勉強しました。
恥ずかしながら、実はこの CtlView の意味をわかっていませんでした。
そのように見えてるだけの「幻みたいなもの」なのですね。
じゃあどうすればペーストできるのかがわからず、調べたら、
"uno:Past " というのを使うみたいだな、。。
と思うのですが、書き方がわかりません。
適当にいろいろ書いてみたのですが、駄目でした。
ダメ元で、
oField4 = AaBbCcDd
してみましたがプロパティがどうとか言われて駄目です。
よろしくおねがいします。