tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルResizeイベントが複数回発生する
記事No11771
投稿日: 2016/12/14(Wed) 17:08
投稿者皆月
知恵を貸して下さい。

フォーム上にラベル等を70個ぐらい貼ったフォームを最大化ボタンを使用して最大化⇔通常化しています。
フォームサイズを変更した時にラベルのサイズ等を変更したいのでAncherやDockを試しましたが
設定が悪いのか思い通りの動作をしないので自分でそれっぽい処理を書きました。

が、フォーム上のラベルが20個程度なら問題ないのですが70個を超えてくると
フォーム最大化状態→フォーム通常状態にした時にResizeイベントが複数回(3〜5回)発生して
再表示処理でパラパラ漫画のようになり、その後フォームレイアウトがぐっちゃぐちゃになります。

Q1.フォーム最大状態→フォーム通常サイズ化 時は1回だけ処理を
  行うようにするにはどうすれば良いでしょう?
Q2.この際だから大人しくAncherとDockを理解したほうがよいものでしょうか?

よろしく御願いします。

Windows7 64bit 開発環境:VisualBasic2010 Express / NetFramework3.5用アプリケーション

*****ここから現在のソフト*****

Private bufHeight As Integer
Private bufWidth As Integer

Private Sub frmMain_Load(sender As Object, e As System.EventArgs) Handles Me.Load
    Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle 'つまんでサイズ変更禁止
        bufHeight = Me.Height
        bufwidth = Me.Width
end sub

Private Sub frmMain_Resize(sender As Object, e As System.EventArgs) Handles Me.Resize

        Debug.Print("{0}サイズ変更イベント発生", Now)
    
    Dim ScaleChg As Double = CDbl(Me.Width / bufWidth)  '倍率
        bufHeight = Me.Height 'フォームサイズを保持
        bufWidth = Me.Width    

    'コントロールのサイズ変更
    For Each c As Control In Me.Controls
            Try
                c.Top = CInt(c.Top * ScaleChg)
                c.Left = CInt(c.Left * ScaleChg)
                c.Height = CInt(c.Height * ScaleChg)
                c.Width = CInt(c.Width * ScaleChg)
                c.Font = New Font(c.Font.FontFamily, _
                          CInt(CDbl(c.Font.Size) * ScaleChg), c.Font.Style)
            Catch ex As Exception
                '何もしない。
            End Try
        Next c
End Sub

[ツリー表示へ]
タイトルRe: Resizeイベントが複数回発生する
記事No11772
投稿日: 2016/12/16(Fri) 10:41
投稿者魔界の仮面弁士
> AncherやDockを試しましたが
※Ancher → Anchor

レイアウトにもよりますが、TableLayoutPanel などの
レイアウト用パネルコントロールを併用してみるのは如何でしょうか。


> その後フォームレイアウトがぐっちゃぐちゃになります。

『c.Height = CInt(c.Height * ScaleChg)』などとしていますが、
座標情報は整数のため、丸め誤差の関係上、フォームを
元のサイズに戻したときに、以前と同じ位置に復元されない可能性があります。

前回の Bounds 情報に対してリサイズを測るのではなく、
基準となる最初のレイアウト情報を保持しておき、
それと比較して座標計算を行った方が安全です。

今回は FixedSingle なので僅かな差異にしかなりませんが、
Sizable のまま 1 ドットずつゆっくりリサイズした場合などでは
問題が顕現しやすくなります。


> Resizeイベントが複数回(3〜5回)発生して

手元の環境では、最大化最小化のケースでは再現しませんでしたが、
そもそも、Resize や Layout は連続して発生しうるものです。

その時々で Me.Bounds.ToString() は同じ値でしょうか?
それとも違うサイズでしょうか?

環境によっては、リサイズ時にアニメーション効果を伴うものがあり、
それが影響している可能性があります。あるいは PC 側の
グラフィック性能に依存するのかも知れませんが。


それと、複数のコントロールのレイアウトを変更する場合は、
SuspendLayout メソッド/ResumeLayout メソッドも有効です。
ただし Dock 指定されたコントロールや、子コントロールを含むコントロールが
無い場合には、これを呼び出しても変わり映えしないかも知れません。
http://dobon.net/vb/dotnet/control/suspendlayout.html


> Q1.フォーム最大状態→フォーム通常サイズ化 時は1回だけ処理を
>   行うようにするにはどうすれば良いでしょう?
> Q2.この際だから大人しくAncherとDockを理解したほうがよいものでしょうか?

最初の Resize 発生時に Application.Idle イベントを AddHandler し、
その Idle イベント内でサイズ調整するようにします。重複呼び出しを
防ぐため、Idle イベント内では、自身を直ちに RemoveHandler してください。

もしくは、ResizeEnd が使える可能性もあります。
http://bbs.wankuma.com/index.cgi?mode=al2&namber=60076&KLOG=100
ただし ResizeBegin/End は、フォーム移動時にも通知されるのでご注意ください。

これらのコード例としては、下記が参考になるかと思います。
http://dobon.net/vb/dotnet/form/preventcontrolresize.html



> Dim ScaleChg As Double = CDbl(Me.Width / bufWidth)  '倍率
「Integer / Integer」は常に Double 型なので、CDbl は冗長です。


> For Each c As Control In Me.Controls
この場合、フォーム直下に貼ったコントロールのみが対象になり、
Panel 等の下に貼った子階層コントロールはリサイズされませんが
その点は問題無いでしょうか。


> c.Top = CInt(c.Top * ScaleChg)
> c.Left = CInt(c.Left * ScaleChg)
> c.Height = CInt(c.Height * ScaleChg)
> c.Width = CInt(c.Width * ScaleChg)
4 回に分けてセットするのではなく、
SetBounds メソッドもしくは Bounds プロパティを用いて
1 回にまとめる事をお奨めします。


> c.Font = New Font(c.Font.FontFamily, _
>       CInt(CDbl(c.Font.Size) * ScaleChg), c.Font.Style)
Font は IDisposable なので、頻繁に作成することは問題が生じるかも知れません。

現時点で問題ないようであれば、ガベージコレクトまかせでも良いですが、
長時間の連続実行時などで問題が出るようなら、新しいフォントをセットした後で
古い物を明示的に Dispose するようにするか、あるいは複数のコントロールで
同じフォントインスタンスを共有するなどしてリソースを節約できるかと思います。

[ツリー表示へ]
タイトルRe^2: Resizeイベントが複数回発生する
記事No11773
投稿日: 2016/12/16(Fri) 16:51
投稿者皆月
魔界の仮面弁士 さん

回答ありがとうございます。

>その時々で Me.Bounds.ToString() は同じ値でしょうか?
>それとも違うサイズでしょうか?
ためしに、Resizeイベントの頭に「Debug.Print(now & ":" & Me.Bounds.ToString())」を入れて、
最大→通常にやってみました。以下、イミディエイトウィンドウのログ。
2016/12/16 14:17:34 {X=88,Y=88,Width=1796,Height=1595}
2016/12/16 14:17:34 {X=88,Y=88,Width=1676,Height=1489}
2016/12/16 14:17:35 {X=88,Y=88,Width=1564,Height=1391}
2016/12/16 14:17:35 {X=88,Y=88,Width=1460,Height=1300}
2016/12/16 14:17:36 {X=88,Y=88,Width=1364,Height=1215}
2016/12/16 14:17:36 {X=88,Y=88,Width=1275,Height=1137}
2016/12/16 14:17:37 {X=88,Y=88,Width=1192,Height=1064}
2016/12/16 14:17:37 {X=88,Y=88,Width=1115,Height=997}
2016/12/16 14:17:38 {X=88,Y=88,Width=1044,Height=934}
2016/12/16 14:17:38 {X=88,Y=88,Width=978,Height=876}
2016/12/16 14:17:39 {X=88,Y=88,Width=917,Height=822}
2016/12/16 14:17:39 {X=88,Y=88,Width=860,Height=772}
2016/12/16 14:17:39 {X=88,Y=88,Width=807,Height=725}
2016/12/16 14:17:39 {X=88,Y=88,Width=758,Height=682}
2016/12/16 14:17:40 {X=88,Y=88,Width=712,Height=642}
2016/12/16 14:17:40 {X=88,Y=88,Width=670,Height=605}
2016/12/16 14:17:40 {X=88,Y=88,Width=631,Height=570}
2016/12/16 14:17:40 {X=88,Y=88,Width=595,Height=538}
2016/12/16 14:17:40 {X=88,Y=88,Width=562,Height=509}
2016/12/16 14:17:40 {X=88,Y=88,Width=562,Height=509}
2016/12/16 14:17:40 {X=88,Y=88,Width=562,Height=509}
2016/12/16 14:17:40 {X=88,Y=88,Width=562,Height=509}

>SuspendLayout メソッド/ResumeLayout メソッドも有効です。
これは試してみましたが効果が目に見えてはわかりませんでした。

>最初の Resize 発生時に Application.Idle イベントを AddHandler し、
来週試してみます。

一先ず、ありがとうございました。結果でたらまた書き込ませていただきます。

[ツリー表示へ]