C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Labelを90度回転する方法

[トピック内 11 記事 (1 - 11 表示)]  << 0 >>

■96416 / inTopicNo.1)  Labelを90度回転する方法
  
□投稿者/ Georgia (1回)-(2020/11/21(Sat) 17:58:22)

分類:[.NET 全般] 

VB.NETを使っております。

Labelを90度回転して表示させたいのですが、
https://stackoverflow.com/questions/1371943/c-sharp-vertical-label-in-a-windows-forms

このページにいくつかクラスが書かれてあります。

一つ目のコードを参考にして、

Imports System.ComponentModel

Partial Public Class VerticalLabel
    Inherits UserControl

    Public Sub New()
        InitializeComponent()
    End Sub






    Private _Text As String = ""

    <Description("(カスタム) コントロールに関連付けられたテキストです。"),
      Category("Appearance")>           '動作
    Public Property Text2() As String
        Get
            Return Me._Text
        End Get
        Set(value As String)
            Me._Text = value
        End Set
    End Property


    Private Sub VerticalLabel_SizeChanged(ByVal sender As Object, ByVal e As EventArgs)
        GenerateTexture()
    End Sub

    Private Sub GenerateTexture()

        Dim format As StringFormat = New StringFormat()
        format.Alignment = StringAlignment.Near
        format.LineAlignment = StringAlignment.Near
        format.Trimming = StringTrimming.EllipsisCharacter
        Dim img As Bitmap = New Bitmap(Me.Height, Me.Width)
        Dim G As Graphics = Graphics.FromImage(img)
        G.Clear(Me.BackColor)
        Dim brush_text As SolidBrush = New SolidBrush(Me.ForeColor)
        G.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit

        G.DrawString(Me.Text2, Me.Font, brush_text, New Rectangle(0, 0, img.Width, img.Height), format)

        brush_text.Dispose()
        img.RotateFlip(RotateFlipType.Rotate270FlipNone)
        Me.BackgroundImage = img

    End Sub

    Private Sub InitializeComponent()
        Me.SuspendLayout()
        '
        'VerticalLabel
        '
        Me.Name = "VerticalLabel"
        Me.Size = New System.Drawing.Size(55, 136)
        Me.ResumeLayout(False)

    End Sub

    Private Sub VerticalLabel_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        GenerateTexture()
    End Sub


End Class


このように書いてみたところ、うまく表示させることができました。

しかし、通常はVSのエディター上のLabelのプロパティで
Textを編集すると、即座にGUIに反映されるはずですが、
上のコードでText2を編集しても、外観がすぐに変化せず
一度ビルドしないと反映されません。



    Protected Overrides Sub OnPaint(e As PaintEventArgs)

            Dim format As StringFormat = New StringFormat()
        format.Alignment = StringAlignment.Near
        format.LineAlignment = StringAlignment.Near
        format.Trimming = StringTrimming.EllipsisCharacter
        Dim img As Bitmap = New Bitmap(Me.Height, Me.Width)

        Dim g As Graphics = e.Graphics

        g.Clear(Me.BackColor)
        Dim brush_text As SolidBrush = New SolidBrush(Me.ForeColor)
        G.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixelGridFit

        G.DrawString(Me.Text2, Me.Font, brush_text, New Rectangle(0, 0, img.Width, img.Height), format)

        brush_text.Dispose()
        img.RotateFlip(RotateFlipType.Rotate270FlipNone)
            '  Me.BackgroundImage = img

    End Sub

こんな感じで、OnPaintの方に書くとすぐに反映されるのですが、
一度描画したら二度と変更することはないのに
ウインドウに変化があるたびに、OnPaintが呼び出されて再描画することになるため、
Me.BackgroundImageの方が使った方が良いのではないかと考えています。

Me.BackgroundImageを使った方法で、
エディター上での編集が即座に反映される方法があればお教えください。


引用返信 編集キー/
■96418 / inTopicNo.2)  Re[1]: Labelを90度回転する方法
□投稿者/ Hongliang (1116回)-(2020/11/21(Sat) 18:13:30)
> 一度描画したら二度と変更することはないのに
> ウインドウに変化があるたびに、OnPaintが呼び出されて再描画することになるため、
> Me.BackgroundImageの方が使った方が良いのではないかと考えています。
BackgroundImageを使ったところで、フレームワークが内部でWM_PAINTが飛んでくるたびに画像を描画することになるだけなので、そこを気にする意味はないですよ。
OnPaintで直接描画すればいいと思います。

質問自体の回答としては、多分Text2のSetでGenerateTextureを呼び出すようにすればいいかと思いますが。
引用返信 編集キー/
■96420 / inTopicNo.3)  Re[2]: Labelを90度回転する方法
□投稿者/ Georgia (2回)-(2020/11/21(Sat) 19:12:52)
No96418 (Hongliang さん) に返信

ありがとうございます。

仰る通り、

Text2のSetでGenerateTextureに入れるとうまくいきました。

ところで、
>BackgroundImageを使ったところで、フレームワークが内部でWM_PAINTが飛んでくるたびに画像を描画することになるだけなので
これって本当なのでしょうか?

確認しようにもバックグランド処理なので
Forループを10000回、回したりできませんので
確かめる方法があれば良いのですが。
引用返信 編集キー/
■96421 / inTopicNo.4)  Re[3]: Labelを90度回転する方法
□投稿者/ Georgia (3回)-(2020/11/21(Sat) 19:50:00)
Me.BackgroundImage を使った方法で
LabelがTablelayoutpanelに入っており、
そのTablelayoutpanelがフォームにアンカーされているとします。

この状態でウインドウサイズを変えると
Labelのサイズが変更されるため、


Private Sub VerticalLabel_SizeChanged(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.SizeChanged
GenerateTexture()
End Sub


呼び出されます。

すると、Labelの内容は更新されるのですが
なぜか2〜3回Labelが点滅してから更新されてしまいます。

OnPaintの方を使うとこのような挙動はないのですが、
この点滅を無くすことはできないでしょうか?

引用返信 編集キー/
■96422 / inTopicNo.5)  Re[3]: Labelを90度回転する方法
□投稿者/ Hongliang (1118回)-(2020/11/21(Sat) 20:11:46)
> これって本当なのでしょうか?

簡単な実験としては、

Class UserControl1
    Inherits UserControl
    Private _Count As Integer = 0
    Protected Overrides Sub OnPaintBackground(e As PaintEventArgs)
        _Count += 1
        If _Count < 5 Then
            MyBase.OnPaintBackground(e)
        End If
    End Sub
End Class

こんなUserControlを作って、
Formに張り付けてBackgroundImageを設定したアプリを起動し、
最小化して戻すのを数回繰り返してみればいいでしょう。

ReferenceSourceを追っかけるのが確実ではありますが。
https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/Control.cs,1b32394d4092d79a
https://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/ScrollableControl.cs,24feec50034d1f25

引用返信 編集キー/
■96423 / inTopicNo.6)  Re[4]: Labelを90度回転する方法
□投稿者/ Georgia (4回)-(2020/11/21(Sat) 21:05:35)
ありがとうございます。


後ほど、いろいろと試してみたいと思います。


OnPaintの方でできるだけ高速化する方法を模索しているのですが

以下のコードで



Public Class RotatingLabel
    Inherits System.Windows.Forms.Label

    Private m_RotateAngle As Integer = 3
    Private m_NewText As String = ""        'String.Empty

    Public Property RotateAngle As Integer
        Get
            Return CInt(Math.Min(360, CUInt(m_RotateAngle)))
        End Get
        Set(ByVal value As Integer)
            m_RotateAngle = Math.Min(360, CInt(value))

            Invalidate()
        End Set
    End Property

    Public Property NewText As String
        Get
            Return m_NewText
        End Get
        Set(ByVal value As String)
            m_NewText = value

            Invalidate()
        End Set
    End Property


    ' Dim InitBool As Boolean = False

    Dim horizShift As Integer = 0
    Dim vertShift As Integer = 0

    Private Sub SetRotation(g As Graphics)


        Dim DegToRad As Func(Of Double, Double) = Function(angle) Math.PI * angle / 180.0
        Dim size As SizeF = g.MeasureString(Me.NewText, Me.Font, Me.Parent.Width)



        Dim normalAngle As Integer = CInt(((RotateAngle Mod 360) + 360) Mod 360)
        Dim normaleRads As Double = DegToRad(normalAngle)
        Dim hSinTheta As Integer = CInt(Math.Ceiling((size.Height * Math.Sin(normaleRads))))
        Dim wCosTheta As Integer = CInt(Math.Ceiling((size.Width * Math.Cos(normaleRads))))
        Dim wSinTheta As Integer = CInt(Math.Ceiling((size.Width * Math.Sin(normaleRads))))
        Dim hCosTheta As Integer = CInt(Math.Ceiling((size.Height * Math.Cos(normaleRads))))
        Dim rotatedWidth As Integer = Math.Abs(hSinTheta) + Math.Abs(wCosTheta)
        Dim rotatedHeight As Integer = Math.Abs(wSinTheta) + Math.Abs(hCosTheta)

        Me.MinimumSize = New Size(rotatedWidth, 0)
        Me.MaximumSize = New Size(rotatedWidth, rotatedHeight)
        Me.Size = Me.MaximumSize



        Dim numQuadrants As Integer = If((normalAngle >= 0 AndAlso normalAngle < 90), 1,
            If((normalAngle >= 90 AndAlso normalAngle < 180), 2,
            If((normalAngle >= 180 AndAlso normalAngle < 270), 3,
            If((normalAngle >= 270 AndAlso normalAngle < 360), 4, 0))))


        If numQuadrants = 1 Then
            horizShift = Math.Abs(hSinTheta)
        ElseIf numQuadrants = 2 Then
            horizShift = rotatedWidth
            vertShift = Math.Abs(hCosTheta)
        ElseIf numQuadrants = 3 Then
            horizShift = Math.Abs(wCosTheta)
            vertShift = rotatedHeight
        ElseIf numQuadrants = 4 Then
            vertShift = Math.Abs(wSinTheta)
        End If

        ' g.Dispose()

    End Sub


    Dim InitBool As Boolean = False

    Public Sub New()

        '  InitBool = True

    End Sub



    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)

        Dim g As Graphics = e.Graphics

        If InitBool = False Then

            Call SetRotation(g)

        End If


        g.TranslateTransform(horizShift, vertShift)
        g.RotateTransform(Me.RotateAngle)

        Dim b As Brush = New SolidBrush(Me.ForeColor)
        g.DrawString(Me.NewText, Me.Font, b, 0F, 0F)
        b.Dispose()

        MyBase.OnPaint(e)


    End Sub


End Class

引用返信 編集キー/
■96424 / inTopicNo.7)  Re[5]: Labelを90度回転する方法
□投稿者/ Georgia (5回)-(2020/11/21(Sat) 21:05:55)




Private Sub SetRotationの計算(SinやCosの計算)は最初に一回行えば良く、OnPaintのたびに実行するのは非効率だと思います。

そのため、
Private Sub SetRotationが最初に一度だけ実行されるようにしたいのですが、


Dim InitBool As Boolean
を定義しておいて、コントロールの初期化が終わってから
Trueにして、最初の一回だけSetRotationが実行されるようにすれば良いと思います。

しかし、

    Public Sub New()

          InitBool = True

    End Sub

のようにすると、初期化の前にtrueになってしまいます。

Protected Overrides Sub VerticalLabel_Load(sender As Object, e As EventArgs)

のようなものがあれば良いのですが
これはないようです。
どこに
          InitBool = True
を記述すれば良いでしょうか?

引用返信 編集キー/
■96425 / inTopicNo.8)  Re[6]: Labelを90度回転する方法
□投稿者/ Hongliang (1119回)-(2020/11/21(Sat) 21:34:25)
> Private Sub SetRotationの計算(SinやCosの計算)は最初に一回行えば良く、OnPaintのたびに実行するのは非効率だと思います。
> そのため、
> Private Sub SetRotationが最初に一度だけ実行されるようにしたいのですが、

RotateAngleやNewTextが変わった際も計算しないといけませんよね?

Private _IsCalculated As Boolean
Private Sub SetRotation(g As Graphics)
    If _IsCalculated Then
        Return
    End If
    _IsCalculated = True
    以下略
End Sub
として、RotateAngle/NewTextのSetで
_IsCalculated = False
Invalidate()
という風にすればいいかと思います。

引用返信 編集キー/
■96426 / inTopicNo.9)  Re[7]: Labelを90度回転する方法
□投稿者/ Georgia (6回)-(2020/11/21(Sat) 21:45:10)
ありがとうございます。

ご提示のコードでうまくいきました。

ただ、別の問題が発生しました。


Labelの初期位置は正しいのですが
デザイナ上で
90→270→360→90というように
回転角を変えていくとなぜか、Labelの位置が
最初の90度では正しかったのが、変更後の90度ではおかしな位置に来てしまいます。

恐らく、
g.TranslateTransform
g.RotateTransform
を何度も実行しているので、オフセット位置の遍歴が残っているのだと思います。

g.ResetTransform()を使えばいけると思って、
Call SetRotation(g)
の前などに入れてみたのですが
改善されませんでした。


どうすれば良いでしょうか?


引用返信 編集キー/
■96427 / inTopicNo.10)  Re[8]: Labelを90度回転する方法
□投稿者/ Hongliang (1120回)-(2020/11/21(Sat) 22:35:56)
とりあえず明らかに間違っているのは、horizShift/vertShiftを設定しているところで、numQuadrantsが1 or 4の時に、どちらか片方しか設定していない点です。
これだとnumQuadrantsが例えば2から1に変わるようにRotateAngleを変更したとき、numQuadrantsが2の時のvertShiftがそのまま残ってしまいます。
引用返信 編集キー/
■96428 / inTopicNo.11)  Re[9]: Labelを90度回転する方法
□投稿者/ Georgia (8回)-(2020/11/21(Sat) 22:59:49)
できました。
どうもありがとうございました!!!

解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ