设计时控件的启用外观?

3

我定义了一个自定义按钮类,当按钮启用/禁用时设置背景颜色。

运行时的启用外观(A):

enter image description here

运行时的禁用外观(B):

enter image description here

设计时外观始终是(A),无论 Enabled 属性的值如何。

我希望我的控件在设计器中显示的方式与运行时完全一样。这可能吗?如果可以,怎么做?

这是我尝试过的(只包括相关代码的部分):

Public Class StyledButton : Inherits Button      
  Private p_fEnabled As Boolean

  <DefaultValue(True)>
  Public Shadows Property Enabled As Boolean
    Get
      Return p_fEnabled
    End Get
    Set(value As Boolean)
      p_fEnabled = value
      MyBase.Enabled = value
      UpdateVisualStyle()
    End Set
  End Property

  Private Sub UpdateVisualStyle()
    If Me.Enabled Then
      'set enabled appearance
    Else
      'set disabled appearance
    End If
  End Sub

End Class

你漏掉了最重要的部分... set enabled/disabled的过程最终需要通过Invalidate调用Paint。 - Ňɏssa Pøngjǣrdenlarp
@Plutonix:我尝试在UpdateVisualStyle的末尾调用Me.Invalidate(),还尝试了Me.Refresh()。但是,在设计时没有改变行为。还有其他想法吗? - Victor Zakharov
@Plutonix:我知道这个。这就是为什么我创建了自己的Enabled属性,它会遮盖原始的Enabled属性。因此理论上,设计师应该开始使用新属性,正确显示启用和禁用外观。 - Victor Zakharov
将名称更改为neoEnabled,它就可以工作了(使用Invalidate)。 - Ňɏssa Pøngjǣrdenlarp
我尝试使用VisualStyles做一些事情,但是在IDE中似乎没有任何作用......没有属性更改的监视器,我想。 - Ňɏssa Pøngjǣrdenlarp
显示剩余2条评论
2个回答

8
我将解释为什么它会表现出这种行为。控件在设计时和运行时的表现非常相似。它在Winforms设计器中提供了强大的所见即所得支持。但是某些属性在设计时非常棘手,例如您实际上不希望Visible属性生效。控件保持可见性非常重要,即使您在属性窗口中将Visible设置为False。
这是控件的设计器的核心角色。它拦截这些难以处理的属性并模拟它们。在属性网格中显示“预期”的值,但实际上不将其传递给控件的属性设置器。
Enabled属性属于这个类别。如果没有拦截,那么就无法再选择该控件了。其他的还有ContextMenu、AllowDrop、UserControl和Form的Location等。您的Shadows替换程序无法欺骗设计器,它使用反射按名称查找属性。因此,您的属性没有任何效果,您的属性设置器根本没有被调用。
您只能通过重写控件的OnPaint()方法来真正做到这一点,这样您就可以在设计时显示不同的颜色。还需要一个自定义的设计器来操作它。然而,一个重要的问题是,更换按钮的渲染器(实现OnPaint()方法的)并不简单。Microsoft决定将渲染器设置为“内部”,因此您无法覆盖它们。
这太麻烦了,我建议您放弃这个。

+1。谢谢汉斯,讲得非常好。这部分回答了我的问题:它使用反射按名称查找属性。虽然我希望设计师也能为禁用的控件和隐藏的控件(Visible = False)显示一些提示。可以是某种特殊颜色和/或图案的边框,带有特殊背景的叠加层,并且透明度为50%等等。但我猜微软没有像我看到的那样看待它。 :) 在我的当前程序中,禁用按钮意味着某个功能“未实现,但可以实现”。因为它是开发人员的工具,禁用状态作为扩展点。 - Victor Zakharov

2

阴影属性在运行时确实按设计工作,但在集成开发环境(IDE)中不起作用。您不想失去 Visible = False 的控件,并且当 Enabled = False 时,希望能够进入按钮事件。由于IDE没有打算绘制禁用的控件,因此更改属性时无需调用Invalidate。

既然它在运行时可以正常工作,在设计师中将其欺骗使用另一个类似原始属性的属性:

<Browsable(False), DebuggerBrowsable(DebuggerBrowsableState.Never),
          EditorBrowsable(False)>
Public Shadows Property Enabled As Boolean
    Get
        Return neoEnabled
    End Get
    Set(value As Boolean)
        neoEnabled = value
    End Set
End Property

一个具备适合IDE的正确名称的新属性。
<DisplayName("Enabled")>
Public Property neoEnabled As Boolean
    Get
        Return p_fEnabled
    End Get
    Set(value As Boolean)

        p_fEnabled = value
        UpdateVisualStyle()
        MyBase.Enabled = p_fEnabled

    End Set
End Property

不幸的是,在代码中,EnabledneoEnabled都将由Intellisense提供,但由于它们都具有相同的功能,这并不重要。测试代码:

Private Sub UpdateVisualStyle()
    If p_fEnabled Then
        ' other interesting stuff
        MyBase.BackColor = Color.Lime
    Else
        MyBase.BackColor = Color.LightGray
    End If

    MyBase.Invalidate()
End Sub

你可能比我更多地与它打交道,并提出了更为清晰的实现。


这将保留与 neoEnabled 状态相关联的背景颜色:

'
'StyledButton1
'
Me.StyledButton1.BackColor = System.Drawing.Color.LightGray
Me.StyledButton1.Enabled = False
Me.StyledButton1.neoEnabled = False

对比
Me.StyledButton1.BackColor = System.Drawing.Color.Lime
Me.StyledButton1.Enabled = False
Me.StyledButton1.neoEnabled = True

这样做不行,您所做的 BackColor 改变将被持久化。运行时它看起来总是被禁用。 - Hans Passant
我决定保留自定义属性名(EnabledCustom)并隐藏原来的Enabled。这样寻找Enabled的人仍然能够找到它,并且知道它是什么(Enabled属性的自定义实现)。+1并接受。 - Victor Zakharov
它有效地摧毁了BackColor作为独立属性,但基于新的Enabled属性进行持久化。"neoEnabled"是半开玩笑的。 - Ňɏssa Pøngjǣrdenlarp
1
@Plutonix:在我的实现中,有一个名为BackColorEnabled的新属性,还有ForeColorEnabled,而原始属性BackColor和ForeColor被隐藏了。禁用外观硬编码为灰色。我认为正确的解决方案是创建一个自定义设计类,它呈现控件处于禁用状态,但考虑到解决方法,这看起来太复杂了。如果有人感兴趣,我会在这里放一个链接:如何:扩展设计模式下控件的外观和行为 - Victor Zakharov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接