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

■35216 / 親記事)  DataGridViewの行ヘッダーに行番号を表示した時のエラー
  
□投稿者/ たこ 一般人(12回)-(2022/11/04(Fri) 02:12:28)
  • アイコン環境/言語:[Windows10 VB.NET .NET Framework 4.7.2 VS2019] 
    分類:[.NET] 

    いつもお世話になります。

    DataGridViewの行ヘッダーに行番号を表示する
    https://dobon.net/vb/dotnet/datagridview/drawrownumber.html

    を使わせて頂いておりますが、1つ目のデータを挿入した際に「System.Configuration.ConfigurationErrorsException: '構成システムを初期化できませんでした。'」と出て停止してしまいます。
    (2つ目のデータ以降はエラーが出ない)
    「例外設定」の「次からスローされた場合を除く」の項目に「Log.dll」とあるので、ログクラスが何かしらいたずらしているとは思うのです。

    何か解決の糸口は無いかと書込みさせて頂きます。


    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
    Private Sub DataGridView1_CellPainting(ByVal sender As Object, ByVal e As DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
      '列ヘッダーかどうか調べる
        If e.ColumnIndex < 0 And e.RowIndex >= 0 Then
          'セルを描画する
          e.Paint(e.ClipBounds, DataGridViewPaintParts.All)       '←ここで発生 コメントアウトするとフォーム拡大縮小時にゴミが残る…

          '行番号を描画する範囲を決定する
          'e.AdvancedBorderStyleやe.CellStyle.Paddingは無視しています
          Dim indexRect As Rectangle = e.CellBounds
          indexRect.Inflate(-2, -2)
          '行番号を描画する
          TextRenderer.DrawText(e.Graphics,
                    (e.RowIndex + 1).ToString(),
                    e.CellStyle.Font,
                    indexRect,
                    e.CellStyle.ForeColor,
                    TextFormatFlags.Right Or TextFormatFlags.VerticalCenter)
          '描画が完了したことを知らせる
          e.Handled = True
        End If

        DataGridView1.AlternatingRowsDefaultCellStyle.BackColor = Color.LightGray
      End Sub
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------



    呼出し元
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
      Private Sub DGVDataAdd(workStr As List(Of String()))
        DataGridView1.Rows.Clear()
        On Error GoTo ErrorNext
        System.Threading.Monitor.Enter(workStr)
        Dim ws As List(Of String()) = workStr
        System.Threading.Monitor.Exit(workStr)
        System.Threading.Monitor.Enter(ws)
        Dim x As Integer = 0
        For Each work In ws
          System.Threading.Monitor.Enter(work)
          DataGridView1.Rows.Add(work)
          DataGridView1(LogData.LogKinds, x).Style.BackColor = LogKind_ColorChange(work)
          DataGridView1(LogData.Operation, x).Style.ForeColor = Operation_ColorChange(work)
          x += 1
          System.Threading.Monitor.Exit(work)
        Next
        System.Threading.Monitor.Exit(ws)
    ErrorNext:
        DataGridView1.Refresh()
      End Sub
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
    いろんなスレッドに呼ばれるので、エラーが出て最終的に「On Error Resume Next」を付けました^^;



    呼出し元の呼出し元
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
      Private Delegate Sub DGVAddInvoke(DataStr As List(Of String()))

      ''' <summary>
      ''' AlertLog変更イベント
      ''' </summary>
      Private Sub _LogStock__AlartLogEvent(sender As Object, e As EventArgs) Handles _LogStock._AlertLogEvent
        If DispLevel = LogEventArgs.Kinds.Alart Then
          Invoke(New DGVAddInvoke(AddressOf DGVDataAdd), _LogStock.AlertLog)
        End If
      End Sub

      ''' <summary>
      ''' OperateLog変更イベント
      ''' </summary>
      Private Sub _LogStock__OperateLogEvent(sender As Object, e As EventArgs) Handles _LogStock._OperateLogEvent
        If DispLevel = LogEventArgs.Kinds.Operate Then
          Invoke(New DGVAddInvoke(AddressOf DGVDataAdd), _LogStock.OperateLog)
        End If
      End Sub

      ''' <summary>
      ''' SystemLog変更イベント
      ''' </summary>
      Private Sub _LogStock__SystemLogEvent(sender As Object, e As EventArgs) Handles _LogStock._SystemLogEvent
        If DispLevel = LogEventArgs.Kinds.System Then
          Invoke(New DGVAddInvoke(AddressOf DGVDataAdd), _LogStock.SystemLog)
        End If
      End Sub

      ''' <summary>
      ''' DebugLog変更イベント
      ''' </summary>
      Private Sub _LogStock__DebugLogEvent(sender As Object, e As EventArgs) Handles _LogStock._DebugLogEvent
        If DispLevel = LogEventArgs.Kinds.Debug Then
          Invoke(New DGVAddInvoke(AddressOf DGVDataAdd), _LogStock.DebugLog)
        End If
      End Sub
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
    以上3つのメソッドはLogDispクラスで、イベントはLogクラスから飛んできます。



    Logクラス
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
      Public Delegate Sub LogEventHandler(sender As Object, e As EventArgs)
      Public Event _AlertLogEvent As LogEventHandler
      Public Event _OperateLogEvent As LogEventHandler
      Public Event _SystemLogEvent As LogEventHandler
      Public Event _DebugLogEvent As LogEventHandler

      Private _AlertLog As New List(Of String())
      Private _OperateLog As New List(Of String())
      Private _SystemLog As New List(Of String())
      Private _DebugLog As New List(Of String())


      Public Sub Write(sender As Object, e As LogEventArgs)

        〜〜〜 (中略) 〜〜〜

        If e.LogKinds = LogEventArgs.Kinds.System Then
          _DebugLog.Insert(0, WriteData.Split(","))
          _SystemLog.Insert(0, WriteData.Split(","))
          If _SystemLog.Count > _MaxDispLog Then _SystemLog.RemoveAt(_MaxDispLog)
          RaiseEvent _SystemLogEvent(Me, New EventArgs())
          RaiseEvent _DebugLogEvent(Me, New EventArgs())
          Exit Sub
        End If
      End Sub
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------

    LogクラスもDispLogも同じLogプロジェクト内で、クラスライブラリ
    DispLogはユーザーコントロールです。

    よろしくお願いします。
マルチポストを報告
違反を報告
引用返信 削除キー/
■35218 / ResNo.1)  Re[1]: DataGridViewの行ヘッダーに行番号を表示した時のエラー
□投稿者/ 魔界の仮面弁士 大御所(1475回)-(2022/11/04(Fri) 10:44:35)
  • アイコンNo35216に返信(たこさんの記事)
    >「System.Configuration.ConfigurationErrorsException: '構成システムを初期化できませんでした。'」
    プログラムそのものでは無く、アプリケーション構成ファイルである
    exe名.config (開発時は app.config) ファイル、あるいは user.config の
    内容に不整合や破損があると、その例外が発生します。
    user.config の場所についてはこちら。
    https://atmarkit.itmedia.co.jp/fdotnet/dotnettips/558appsettings/appsettings.html


    ということで、ログ関連の設定情報に問題があるのでは無いでしょうか。

    プロジェクトの構成が分からないので具体的なアドバイスはできませんが、
    Log.dll のプロジェクトがあるのなら、確認すべき app.config は Log プロジェクトの app.config ではなく、
    exe 側のプロジェクト側の app.cofing のはずです。exe 側の .config に対して、Log プロジェクト用の
    エントリーが正しく記録されているかどうかを確認してみてください。


    > LogクラスもDispLogも同じLogプロジェクト内で、クラスライブラリ
    > DispLogはユーザーコントロールです。
    NLog や log4net の類では無く、自作のロギングクラスということだとすると、
    第三者には、具体的なアドバイスが難しいかもしれません。


    > 呼出し元
    > Private Sub DGVDataAdd(workStr As List(Of String()))
    >   DataGridView1.Rows.Clear()
    >   On Error GoTo ErrorNext
    DataGridView1.Rows.Clear() が呼ばれているという事は、DGVDataAdd メソッドの呼び出し元が
    UI スレッド上からの呼び出しであることは保証されているのですよね?

    で、引数の List(Of ) は、「別スレッドでも同時に読み書きされる可能性がある」という事でしょうか。


    > いろんなスレッドに呼ばれるので、エラーが出て最終的に「On Error Resume Next」を付けました^^;
    え?「いろんなスレッドに呼ばれる」のですか?
    この実装だと、UI スレッド以外からの呼び出しは NG に見えますが…。

    ワーカースレッドから呼ばれることを前提としているのであれば、
    DataGridView に対する読み書き部分を取り除く必要があるでしょう。


    >   System.Threading.Monitor.Enter(workStr)
    >   Dim ws As List(Of String()) = workStr
    >   System.Threading.Monitor.Exit(workStr)
    >   System.Threading.Monitor.Enter(ws)
    ・ws は、workStr のコピーでは無く、同一インスタンスへの参照ですよね。
     Exit 直後に再 Enterをする実装になっているのは何故ですか?
     ループ全体を ws で囲って、ループ内では workStr で囲っている理由が分からないです。
     (ReaderWriterLock クラスのような事がしたいわけでも無さそうですし…)

    ・もしも処理によって粒度の異なるロックを用意したいという意図であれば、
     ロック範囲のレベルごとに、別々の「Object インスタンス」を設けた方が良いかもしれません。
      Private ReadOnly lockObject As New Object()

    ・UI スレッドで Monitor を扱う場合は、無制限待機になってしまうことを避けるため、
     .Enter ではなく、待機上限時間を指定可能な .TryEnter の方が安全かと思います。
     (ロック失敗時にアプリケーション例外とするのか、処理を無視するのか、リトライ判定させるかは要件次第…)
違反を報告
引用返信 削除キー/
■35219 / ResNo.2)  Re[2]: DataGridViewの行ヘッダーに行番号を表示した時のエラー
□投稿者/ たこ 一般人(14回)-(2022/11/04(Fri) 22:14:40)
  • アイコンNo35218に返信(魔界の仮面弁士さんの記事)
    > ■No35216に返信(たこさんの記事)

    いつもお世話になります。
    魔界の仮面弁士様、早速のご回答ありがとうございます。



    > ということで、ログ関連の設定情報に問題があるのでは無いでしょうか。
    >
    > プロジェクトの構成が分からないので具体的なアドバイスはできませんが、
    > Log.dll のプロジェクトがあるのなら、確認すべき app.config は Log プロジェクトの app.config ではなく、
    > exe 側のプロジェクト側の app.cofing のはずです。exe 側の .config に対して、Log プロジェクト用の
    > エントリーが正しく記録されているかどうかを確認してみてください。


    exe側のApp.config
    ----------------------------------------------------------------------------------------------------
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <configSections>
      </configSections>
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
      </startup>
    </configuration>
    ----------------------------------------------------------------------------------------------------
    別におかしいところは見当たらず…orz




    > DataGridView1.Rows.Clear() が呼ばれているという事は、DGVDataAdd メソッドの呼び出し元が
    > UI スレッド上からの呼び出しであることは保証されているのですよね?


    InvokeでUIスレッド上からの呼び出しであることは保証されている…と思っています。。。



    > で、引数の List(Of ) は、「別スレッドでも同時に読み書きされる可能性がある」という事でしょうか。


    はい。値渡ししています。


    >>いろんなスレッドに呼ばれるので、エラーが出て最終的に「On Error Resume Next」を付けました^^;
    > え?「いろんなスレッドに呼ばれる」のですか?
    > この実装だと、UI スレッド以外からの呼び出しは NG に見えますが…。


    正確にはいろいろなクラスのメソッドからイベントで呼ばれる…でした^^;



    > ワーカースレッドから呼ばれることを前提としているのであれば、
    > DataGridView に対する読み書き部分を取り除く必要があるでしょう。
    >
    >
    >>  System.Threading.Monitor.Enter(workStr)
    >>  Dim ws As List(Of String()) = workStr
    >>  System.Threading.Monitor.Exit(workStr)
    >>  System.Threading.Monitor.Enter(ws)
    > ・ws は、workStr のコピーでは無く、同一インスタンスへの参照ですよね。
    >  Exit 直後に再 Enterをする実装になっているのは何故ですか?
    >  ループ全体を ws で囲って、ループ内では workStr で囲っている理由が分からないです。
    >  (ReaderWriterLock クラスのような事がしたいわけでも無さそうですし…)
    >
    > ・もしも処理によって粒度の異なるロックを用意したいという意図であれば、
    >  ロック範囲のレベルごとに、別々の「Object インスタンス」を設けた方が良いかもしれません。
    >   Private ReadOnly lockObject As New Object()
    >
    > ・UI スレッドで Monitor を扱う場合は、無制限待機になってしまうことを避けるため、
    >  .Enter ではなく、待機上限時間を指定可能な .TryEnter の方が安全かと思います。
    >  (ロック失敗時にアプリケーション例外とするのか、処理を無視するのか、リトライ判定させるかは要件次第…)


    TryEnterと言うメソッドもあるのですね…
    まだまだスレッドとエラー処理については勉強が必要そうです……


    で、本題の「DataGridViewの行ヘッダーに行番号を表示した時のエラー」について、今回はそこまで厳密にリアルタイムに表示されなくても良い為、
    魔法の言葉「On Error Resume Next」で逃げる事にしました^^;

    最終形
    -----------------------------------------------------------------------------------------------------------------------------------------------------
      Private Sub DataGridView1_CellPainting(ByVal sender As Object, ByVal e As DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
        On Error Resume Next
        '列ヘッダーかどうか調べる
        If e.ColumnIndex < 0 And e.RowIndex >= 0 Then
          'セルを描画する
          e.Paint(e.ClipBounds, DataGridViewPaintParts.All)

          '行番号を描画する範囲を決定する
          'e.AdvancedBorderStyleやe.CellStyle.Paddingは無視しています
          Dim indexRect As Rectangle = e.CellBounds
          indexRect.Inflate(-2, -2)
          '行番号を描画する
          TextRenderer.DrawText(e.Graphics,
                     (e.RowIndex + 1).ToString(),
                     e.CellStyle.Font,
                     indexRect,
     e.CellStyle.ForeColor,
                     TextFormatFlags.Right Or TextFormatFlags.VerticalCenter)
          '描画が完了したことを知らせる
          e.Handled = True
        End If

        DataGridView1.AlternatingRowsDefaultCellStyle.BackColor = Color.LightGray
      End Sub


      Private Sub DGVDataAdd(workStr As List(Of String()))
        DataGridView1.Rows.Clear()
        On Error GoTo ErrorNext
        Dim ws As List(Of String()) = workStr
        Dim x As Integer = 0
        For Each work In ws
          DataGridView1.Rows.Add(work)
          DataGridView1(LogData.LogKinds, x).Style.BackColor = LogKind_ColorChange(work)
          DataGridView1(LogData.Operation, x).Style.ForeColor = Operation_ColorChange(work)
          x += 1
        Next
    ErrorNext:
        DataGridView1.Refresh()
      End Sub

    -----------------------------------------------------------------------------------------------------------------------------------------------------


    板汚し失礼致しました。
    またよろしくお願い致します。
    ありがとうございました。
解決み!
違反を報告
引用返信 削除キー/
■35220 / ResNo.3)  Re[3]: DataGridViewの行ヘッダーに行番号を表示した時のエラー
□投稿者/ 魔界の仮面弁士 大御所(1476回)-(2022/11/07(Mon) 17:05:33)
  • アイコンNo35219に返信(たこさんの記事)
    >> あるいは user.config の内容に不整合や破損があると、
    > exe側のApp.config
    > 別におかしいところは見当たらず…orz
    user.config や machine.config は大丈夫なのですね?
    https://ja.stackoverflow.com/questions/65632/%E6%A7%8B%E6%88%90%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E5%88%9D%E6%9C%9F%E5%8C%96%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%9B%E3%82%93%E3%81%A7%E3%81%97%E3%81%9F-%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6


    また、exe 側は .config 設定が特に無いものの、
    dll 側では connectionStrings や applicationSettings を
    登録してある…という事も無いのですよね。

    とすると、Invoke のタイミングの問題?


    > 正確にはいろいろなクラスのメソッドからイベントで呼ばれる…でした^^;
    常に Invoke であれば「いろんなスレッドに呼ばれる」ことはなく、
    UI スレッドからの呼び出しであると言えそうですが、Invoke し忘れが怖い所。

    呼び出し元の使い方を保証できない場合は、InvokeRequired を使って
    「別スレッドから呼び出された場合は、自身を Invoke しなおす」という
    実装にする手法があります。
    https://atmarkit.itmedia.co.jp/ait/articles/0506/17/news111.html#:~:text=invokeメソット&#12441;か&#12441;必要かと&#12441;うか

    いずれにせよ、イベント再入などが起こらないように注意が必要です。
    フォームを閉じている最中や起動途中に、割り込まれて Invoke されるような
    ケースがありえる場合は、追加の保護機構が必要になるかもしれません。


    >>で、引数の List(Of ) は、「別スレッドでも同時に読み書きされる可能性がある」という事でしょうか。
    > はい。値渡ししています。
    ロギングのため、追記される差分情報のみを同期的に渡すのであれば、
    同一インスタンスでは無く、値のコピーを渡す方が簡単だったりします。
    同一インスタンスへの同時書き込みが発生しなくなるので、
    Monitor や SyncLock などによる同期制御が不要で、Invoke だけで済むかと。
解決み!
違反を報告
引用返信 削除キー/
■35313 / ResNo.4)  Re[4]: DataGridViewの行ヘッダーに行番号を表示した時のエラー
□投稿者/ Aurea 一般人(1回)-(2023/01/04(Wed) 10:41:25)
  • アイコンコメントにあるものはもう試しましたか?最初のコメントは、実際に試してみればうまくいく可能性が高いと思います。私は自分でそれを実行しましたが、これまでのところ良い結果が得られました。これが、コードについて検討できるさまざまな状況が実際にたくさんあると言える理由です。それが私が最初に言えることであり、あなたと私は両方とも、現時点で私たち全員が検討できる選択肢がほとんどないことを知っています.今あなたがしなければならないことは、今のところ私たち全員が得ることができる最善の回答を考え出すことです.
違反を報告
引用返信 削除キー/



スレッド内ページ移動 / << 0 >>

このスレッドに書きこむ

Mode/  Pass/


- Child Tree -