将ContentAlignment转换为StringAlignment

时间:2017-06-29 04:34:57

标签: .net string vb.net button alignment

我想修改一个Button,但在正确设置文本对齐方面有些困难。它的文本对齐使用Sub AlocSubs() 枚举表示,所以我有:

ContentAlignment

还有一个Text属性。这个没有问题,我在属性窗口中输入的内容在按钮上正确显示,它仅用于完整性:

Private goTextAlign As ContentAlignment

Public Overrides Property TextAlign As ContentAlignment
    Get
        Return goTextAlign
    End Get
    Set(oValue As ContentAlignment)
        If oValue <> goTextAlign Then
            goTextAlign = oValue
            Me.Invalidate()     'Causes a paint message.
        End If
    End Set
End Property

现在,谈到绘画,我遇到了问题。我理解,我无法将<Category("Appearance")> Public Overrides Property Text() As String Get Return gsText End Get Set(ByVal sValue As String) If sValue <> gsText Then 'If the new text differs from the old gsText = sValue 'one indeed, store it. Me.Invalidate() 'Invalidate causes a paint message to be End If 'sent to the control. End Set End Property ContentAlignment一起使用,但需要创建一个DrawString对象。

StringFormat

如何从goTextAlign中提取和转换?

Protected Overrides Sub OnPaint(e As PaintEventArgs)
    Dim sSaveText As String         
    Dim oTextAlign As ContentAlignment
    sSaveText = Me.Text             'Save the current text.
    oTextAlign = Me.TextAlign
    gsText = String.Empty           'Blank the Text property.
    MyBase.OnPaint(e)               'Erase the client area.
    gsText = sSaveText              'Restore the Text property.
    goTextAlign = oTextAlign

    Using oBrush = New SolidBrush(ForeColor)
        Using oStringFormat = New StringFormat()

当然,我可以在那里使用常数:

            oStringFormat.Alignment = ?
            oStringFormat.LineAlignment = ?

            e.Graphics.DrawString(Me.Text, Me.Font, oBrush,
                Rectangle.Inflate(ClientRectangle, 0, 0), oStringFormat)
        End Using                  
    End Using                       
End Sub

但这不是我想要的,我想使用goTextAlign中提供的值。

当然,我可以从goTextAlign中选择捕获所有3 x 3种可能性,并为.Alignment和.LineAlignment指定适当的值,但是应该有一个更简洁的解决方案。

所以问题是:如何转换ContentAlignment,那么StringFormat很乐意接受它们?

为了完整性,类定义:

        Using oStringFormat = New StringFormat() With
            {.Alignment = StringAlignment.Center,
            .LineAlignment = StringAlignment.Center}

编辑:这有效,但很难看。

'In order to see the Text property in the appearance section of the properties 
'window, the Category attribute needs to be modified, which requires the 
'inclusion of the ComponentModel namespace.
Imports System.ComponentModel

Public Class FlatButton
    Inherits Button

    ...
End Class

2 个答案:

答案 0 :(得分:2)

好的,经过一些试验和错误后,我设法为水平和垂直对齐提出了纯粹的数学解决方案。这些可能是最难的,绝对是最复杂的方法,但它们可以用一行代码编写。

注意:这只是一种解决方法,是为了尽可能少地使用代码行而创建的 删除“需要“重复代码。 ContentAlignmentStringAlignment之间没有正式转换。

水平和垂直的完整解决方案可以在底部找到。


水平解(解释)

运行此代码后(online)

Console.WriteLine("-- ContentAlignment --")
For Each Name As String In [Enum].GetNames(GetType(System.Drawing.ContentAlignment))
    Console.WriteLine(Name & ": " & CType([Enum].Parse(GetType(System.Drawing.ContentAlignment), Name), Integer))
Next

Console.WriteLine()
Console.WriteLine("-- StringAlignment --")

For Each Name As String In [Enum].GetNames(GetType(System.Drawing.StringAlignment))
    Console.WriteLine(Name & ": " & CType([Enum].Parse(GetType(System.Drawing.StringAlignment), Name), Integer))
Next

我发现了枚举的值:

-- ContentAlignment --
TopLeft: 1
TopCenter: 2
TopRight: 4
MiddleLeft: 16
MiddleCenter: 32
MiddleRight: 64
BottomLeft: 256
BottomCenter: 512
BottomRight: 1024

-- StringAlignment --
Near: 0
Center: 1
Far: 2

现在,我必须找到一种方法将每个Left变为0,将Center变为1,将Right变为2。

因此,通过一些(不是那么随机的)试验和错误,我想出了这个公式:

Log(x Mod 5, 2)

由于x Mod 5只能是0-4范围内的整数,Log(x Mod 5, 2)只能返回0-2范围内的指数(n22 = 4 )。

由于ContentAlignment枚举仅由二进制数组成,n中的2n将始终为整数,因此为0,1或2 - 这正是我们所需要的StringAlignment枚举只有这三个值。 问题解决了!

在线测试: http://ideone.com/XKqeFw

'Horizontal version only. See the bottom of this answer for full code covering both horizontal and vertical.
Public Function ContentToStringAlignment(ByVal Alignment As ContentAlignment) As StringAlignment
    Return CType(Math.Log(CType(Alignment, Integer) Mod 5, 2), StringAlignment)
End Function


垂直解决方案(解释)

在我看来,垂直解决方案更加“狡猾”,但它确实有效。

由于垂直解决方案有点困难,因为整个范围(例如16-64)具有相同的对齐方式,我认为最简单的方法是如果我能以某种方式获得大于或等于想要的对齐,然后向下舍入。

这是我想出的公式:

Floor(Log(x, 16))
  • Log(x, 16)将返回基数x16的对数,n中的16n = x。对于二进制数{ {1}}将是一个整数和0-2个季度(例如n11.25等。)

  • 1.5会返回Floor(Log(x, 16)) 向下舍入 的结果(例如Log(x, 16))。

该公式将导致以下三个二进制数返回相同的数字。

1.5 -> 1

在线测试: http://ideone.com/yIlrJS


最终解决方案

最后,通过这些信息,我们现在可以创建一个相对较小的函数,为我们进行所需的转换:

+------------+--------------+-------------------+
|   Binary   |  Log(x, 16)  | Floor(Log(x, 16)) |
+------------+--------------+-------------------+
| 256        | 2            | 2                 |
| 512        | 2.25         | 2                 |
| 1024       | 2.50         | 2                 |
+------------+--------------+-------------------+

用法:

Public Function ContentToStringAlignment(ByVal Alignment As ContentAlignment, ByVal Vertical As Boolean) As StringAlignment
    If Vertical = True Then Return CType(Math.Floor(Math.Log(CType(Alignment, Integer), 16)), StringAlignment)
    Return CType(Math.Log(CType(Alignment, Integer) Mod 5, 2), StringAlignment)
End Function

在线测试: http://ideone.com/VFN8rS

答案 1 :(得分:2)

不使用对数的更好答案

这假设在ControlAlignment中只设置了一个位,但是使用Math.Log的解决方案也存在多位值的问题。

这完全是C#语法,对不起......我也希望这些内容在转录中没有丢失任何内容。

假设ca是初始ContentAlignment值,表示为十六进制数,它的格式为0xBMT,其中B,M和T代表底部,中间和顶部其中每个都有二进制形式0RCL,其中R,C,L表示右(远),中和左(近)

提取水平对齐

(StringAlignment)((((int)ca * 0x111) >> 7) / 3)

乘以0x111在B位置将B,M和T值加在一起(并因此将其作为单个位)。右移7给出了RCL0形式的二进制数,其值为8,4或2.除以3得到Far,Center和Near的正确值2,1或0。

提取垂直对齐

(StringAlignment)(((((((int)ca * 7) & 0x444) * 0x49) & 0x700) >> 7) / 3)

乘以7将RCL位添加(并因此将其作为单个位)到每个BMT十六进制数字内的R位置。与0x444进行AND运算仅保留每个BMT十六进制数字中的R位(现在是R,C和L的OR)。乘以0x49将M和T组的R位副本放在B组的R位之下。使用0x700进行AND运算可以删除垃圾,而7的移位除以3会与提取水平对齐方式相同。

ta

替换水平对齐
(ContentAlignment)((0x111 << (int)ta) & (((((int)ca * 7) & 0x444) * 7) >> 2))

通过为所有垂直对齐屏蔽新的水平对齐以及所有水平对齐的原始垂直对齐来产生结果。第一项取L位置的BMT位,并根据新的对齐方式将它们转换为L,C或R.第二项将L,C和R一起加入到B,M和T中的每一个的R位置,通过与0x444进行AND运算来清除垃圾,乘以7并向下移动2以产生所有L, C和R在适当的BMT组中开启。

ta

替换垂直对齐
(ContentAlignment)((0x7 << (4 * (int)ta)) & (((((int)ca * 0x111) & 0x700) * 0x111) >> 8))

类似于水平情况,第一项产生适合于所需垂直对齐的所有L,C和R.第二项将每个LCR比特的B,M和T混合到B组的LCR中。与0x700进行AND运算只保留B组,乘以0x111,右移8则产生所有三个BMT组中重复的原始L,C或R位。