我想修改一个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
答案 0 :(得分:2)
好的,经过一些试验和错误后,我设法为水平和垂直对齐提出了纯粹的数学解决方案。这些可能是最难的,绝对是最复杂的方法,但它们可以用一行代码编写。
注意:这只是一种解决方法,是为了尽可能少地使用代码行而创建的 和 删除“需要“重复代码。 ContentAlignment
和StringAlignment
之间没有正式转换。
水平和垂直的完整解决方案可以在底部找到。
运行此代码后(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之间的值(有关如何在Khan Academy (Modular Arithmetic)找到模数的详细解释)。
Log(x Mod 5, 2)
将返回基数为2的x Mod 5
的对数,n
中的2n = x Mod 5
(同样,Khan Academy (Intro to Logarithms)解释得非常好)。
由于x Mod 5
只能是0-4范围内的整数,Log(x Mod 5, 2)
只能返回0-2范围内的指数(n
)22 = 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)
将返回基数x
中16
的对数,n
中的16n = x
。对于二进制数{ {1}}将是一个整数和0-2个季度(例如n
,1
,1.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位。