65.9K
CodeProject 正在变化。 阅读更多。
Home

智能家居 – 控制 Shelly® 设备(第四部分)- 使用 PropertyDescriptor 进行属性操作

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2024 年 1 月 17 日

CPOL

10分钟阅读

viewsIcon

4145

downloadIcon

178

本文彻底改变了 Shelly 设备管理方式,为初学者和高级用户提供了更新、更高效、功能更丰富的解决方案。 您将发现新的 ShellyCom2 功能,例如使用 PropertyDescriptor 进行属性操作,以及一套有价值的辅助工具和命令。

引言

本文的基础是我之前文章中的描述

在我上一篇文章中,我说过这个系列将结束……事情有时会与你想象的不同……

一方面,我获得了其他设备的访问权限(Shelly Button、Shelly Plug、Shelly 4pm、Shelly Bulb Vintage、Shelly Bulb Duo 和 Shelly Bulb Duo RGBW)并添加了它们。 对于 Shelly 灯泡,控制它们的文档并不特别好。 因此,我将在本文中专门用一个小章节来介绍这一点。

在此之后,我在现有的工具箱中实现了其他功能,或对旧功能进行了修改——也考虑了 DRY(不要重复自己)的概念。

此外,本着我喜欢折腾的本能,我更改了我操作的属性行为,使其只显示在上下文中适用的属性。 在本文中,我特别介绍了这些与 Shelly 设备没有直接关系,但可能也引起他人兴趣的内容——以我工具箱中的应用程序为例。 本文也可以称为 **使用 PropertyDescriptor 进行属性操作**。

下载包含所有当前组件的完整工具箱(VB.NET 和 C#),并且基本上完全取代了之前文章的下载内容。

ShellyCom2 中的新功能

这里添加了一些新功能和助手

助手
Shelly_CheckIp 检查传入的 IP 地址是否合法,是否存在,是否指向 Shelly 设备,并且该设备是否可用于该任务。 由于这里会检查属性的输入,因此所有违规行为都会作为消息框消息输出。
JsonPrettify 将传入的 JSON 字符串转换为更易读的格式
ConvertUnixTimestamp 将 Shelly 使用的 Unix 时间戳转换为 Microsoft 的 DateTime 格式
Shelly_GetStatusString 返回要传递的任何 Shelly 请求的状态字符串
Shelly_ShowDeviceInfo 提供传入 IP 地址的 Shelly 设备的 DeviceInfo 用于显示
CheckColorIsKnown 检查从 Shelly 设备传入的 Color.ARGB 值是否对应于已知的颜色分配

 

请求
Shelly_GetType 确定传入 IP 地址的 Shelly 类型。 这里存储的类型比我实际检查过的要多。 但是,传入的标识符是正确的……
Shelly_GetInfo 返回传入 IP 地址的 Shelly 设备的基本信息
Shelly_GetBaseType 返回传入 IP 地址的 Shelly 设备的基础类型(输入、单通道输出、多通道输出、调光器、灯)
Shelly_GetNrOutputs 返回传入 IP 地址的 Shelly 设备的可用输出数量
   
Shelly_DimmerRGBW_GetStatus 提供 RGBW 设备的的状态。

 

Commands
AssignAction 根据传输,选择相关的函数例程来执行命令。 由于此方法基本上在每个组件中使用,因此我将其从组件中移除,并将其存储在这里作为集合例程。
Shelly_SetRoller 为 Shelly Pro Dual Cover 准备的,因为这里可以配置和使用 2 个百叶窗
Shelly_ToggleRoller
Shelly_SetLamp + 1 个覆盖层 将相应的数据分配给传入 IP 地址的灯
Shelly_ToggleLamp + 1 个覆盖层 切换传入 IP 地址的灯的状态及其相应的数据

控制 Shelly 灯泡

不同的 Shelly 灯泡具有非常不同的行为和可能性

  • Shelly-Bulb Vintage 的行为基本上与 Shelly Dimmer 相同——因此可以以相同的方式对待。
  • Shelly-Bulb Duo 还提供了改变白色颜色的选项。
  • 最后,Shelly-Bulb RBGW 有 2 种操作模式。 在 Mode=White 模式下,它的行为与 Bulb Duo 完全相同,只是使用不同的属性(temp)和不同的值范围来表示色温(3000...6500)。 还有 Mode=Color 模式,可以使用 Windows RGB 颜色表控制灯,并调整其亮度。 还可以选择效果。 我已在相关方法 Shelly_SetLampShelly_ToggleLamp 中考虑了这些方面。

不同组件的项目定义

最初,每个组件都有自己的项目定义——我现在已将其合并到所有组件中。 因此,在 Definitions 文件中,只有 SceneActionDefinition 和派生自 SceneActionDefinitionControlActionDefinition

这些定义的结构基本保持不变——只添加了一些其他必需的属性……而且…… ICustomTypeDescriptor 接口,它允许根据预选集在 PropertyGrid 中使单个属性可见或不可见。 我将在下面描述它是如何工作的。

此外,作为一种炫技,还添加了 UITypeEditor Symbol2Property,它会在 2 个属性前面显示图像符号。 为了使此功能生效,必须将包含的图形作为资源导入到项目中。 我也将在下面更详细地介绍这一点。

最终的惊喜是 TypeConverter SelectionOnOffConverter,它现在代表 State 属性的 ONOFF,而不是 TRUEFALSE。 我也将在下面介绍这一点。

SelectionOnOffConverter

此转换器将 bool 变量的标准输出从 True/False 更改为 On/Off

首先是它的代码

Imports System
Imports System.ComponentModel

Public Class SelectionOnOffConverter
    Inherits BooleanConverter

    Public Overloads Overrides Function CanConvertTo_
    (ByVal context As ITypeDescriptorContext, ByVal destinationType As Type) As Boolean
        If destinationType Is GetType(String) Then Return True

        'allow converting to String
        Return MyBase.CanConvertTo(context, destinationType)
    End Function

    Public Overloads Overrides Function ConvertTo_
           (ByVal context As ITypeDescriptorContext, _
            ByVal culture As System.Globalization.CultureInfo, _
            ByVal value As Object, ByVal destinationType As Type) As Object
        If destinationType Is GetType(String) Then
            'converting to String
            Dim result As Boolean
            Try
                result = CBool(value)
            Catch ex As Exception
                result = False
            End Try

            If Not result Then
                Return "Off"
            Else
                Return "On"
            End If
        End If

        Return MyBase.ConvertTo(context, culture, value, destinationType)
    End Function

    Public Overloads Overrides Function CanConvertFrom_
           (ByVal context As ITypeDescriptorContext, _
            ByVal sourceType As Type) As Boolean
        If sourceType Is GetType(String) Then Return True

        'allow converting from String
        Return MyBase.CanConvertFrom(context, sourceType)
    End Function

    Public Overloads Overrides Function ConvertFrom_
           (ByVal context As ITypeDescriptorContext, _
            ByVal culture As System.Globalization.CultureInfo, _
            ByVal value As Object) As Object
        If (TypeOf value Is String) Then
            'converting from String
            If value.ToString.ToLower.Trim = "off" Then
                Return False
            Else
                Return True
            End If
        End If

        Return MyBase.ConvertFrom(context, culture, value)
    End Function

End Class
using System;
using System.ComponentModel;

public class SelectionOnOffConverter : BooleanConverter
{
    public override bool CanConvertTo_
           (ITypeDescriptorContext context, Type destinationType)
    {
        if (destinationType == typeof(string))
            return true;

        // allow converting to String
        return base.CanConvertTo(context, destinationType);
    }

    public override object ConvertTo(ITypeDescriptorContext context,
           System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType == typeof(string))
        {
            // converting to String
            bool result;
            try
            { result = System.Convert.ToBoolean(value); }
            catch (Exception)
            { result = false; }

            if (!result)
                return "Off";
            else
                return "On";
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if (sourceType == typeof(string))
            return true;

        // allow converting from String
        return base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context,
                    System.Globalization.CultureInfo culture, object value)
    {
        if ((value is string))
        {
            // converting from String
            if (value.ToString().ToLower().Trim() == "off")
                return false;
            else
                return true;
        }

        return base.ConvertFrom(context, culture, value);
    }
}

PropertyGrid 本身基本上将显示的每个变量(无论其类型如何)表示为文本(即 string)。 因此,转换器会从每个属性生成一个 string。 对于系统中所有已知的变量,都有一个相应的转换器。 但是……当然,您也可以指定为某个属性使用完全不同的转换器,甚至您自己创建的转换器。 在我这个系列的上一篇文章中,我已经介绍了 DropDownConverter 作为一种工具。

CodeProject 已经写了许多关于 TypeConverters 的文章——所以如果你有兴趣自己创建一个这样的东西,我建议你在搜索中输入它。

基本过程如下

  • CanConvertTo 方法确定可以创建哪种目标格式——在这种情况下是 String
  • ConvertTo 方法现在决定要输出的 string 的内容。 在这里,TRUEFALSE 被转换为 ONOFF(用于输出)。
  • CanConvertFrom 方法确定可以从哪种源格式进行转换——在这种情况下是 String
  • ConvertFrom 方法现在将传入的值,这里是 ONOFF,转换回布尔值 TRUEFALSE

我承认这确实是一种炫技——但我觉得在上下文中这样做很不错……😉

Symbol2Property UITypeEditor

对于某些属性,在 PropertyGrid 中属性本身前面会显示一个符号——例如,对于 Color,会显示一个带有选中颜色的矩形。

现在我想:为什么不在 IP 地址前面显示一个 WiFi 符号,在 ShellyType 前面显示所选设备的缩略图呢?这项任务由 UITypeEditor Symbol2Property 完成。

CodeProject 上也有很多关于创建自定义 UITypeEditor 的文章,如果您有兴趣自己创建类似的东西,我也建议您在搜索中输入。

这是它的代码

Public Class Symbol2Property
    Inherits System.Drawing.Design.UITypeEditor

    ' paint a Symbol in front of the Property
    Public Overloads Overrides Function GetPaintValueSupported_
           (ByVal context As System.ComponentModel.ITypeDescriptorContext) As Boolean
        Return True
    End Function

    ' this method paints the Symbol
    Public Overloads Overrides Sub PaintValue_
           (ByVal e As System.Drawing.Design.PaintValueEventArgs)

        Dim PropertyName As String = e.Context.PropertyDescriptor.Name

        Dim myImage As System.Drawing.Image
        Dim myRect As New Rectangle()
        myRect = e.Bounds
        myRect.Inflate(-2, -2)

        If PropertyName = "IpAdress" Then
            myImage = My.Resources.Resources.Wifi
            e.Graphics.DrawImage(myImage, myRect)

        ElseIf PropertyName = "ShellyType" Then
            Dim myShellyType As Shelly.ShellyType = _
                [Enum].Parse(GetType(Shelly.ShellyType), e.Value.ToString)

            Select Case myShellyType
                Case Shelly.ShellyType.Shelly_Plus1, Shelly.ShellyType.Shelly_1, _
                ShellyType.Shelly_1_g3, ShellyType.Shelly_1L
                    myImage = My.Resources.Resources.Shelly_1
                Case Shelly.ShellyType.Shelly_Plus2, _
                     ShellyType.Shelly_2, ShellyType.Shelly_25
                    myImage = My.Resources.Resources.Shelly_2
                Case Shelly.ShellyType.Shelly_Dimmer12
                    myImage = My.Resources.Resources.Shelly_Dimmer
                Case Shelly.ShellyType.Shelly_Bulb, ShellyType.Shelly_Bulb_Duo, _
                ShellyType.Shelly_Bulb_RGBW, ShellyType.Shelly_Bulb_Vintage
                    myImage = My.Resources.Resources.Shelly_Bulb
                Case Shelly.ShellyType.Shelly_Plug
                    myImage = My.Resources.Resources.Shelly_Plug
                Case Shelly.ShellyType.Shelly_i4Plus, ShellyType.Shelly_i3
                    myImage = My.Resources.Resources.Shelly_I4
                Case Else
                    myImage = My.Resources.Resources.unknown
            End Select

            e.Graphics.DrawImage(myImage, myRect)
        End If

    End Sub

End Class
public partial class Symbol2Property : System.Drawing.Design.UITypeEditor
{
    // paint a Symbol in front of the Property
    public override bool GetPaintValueSupported_
           (System.ComponentModel.ITypeDescriptorContext context)
    { return true; }

    // this method paints the Symbol
    public override void PaintValue(System.Drawing.Design.PaintValueEventArgs e)
    {
        string PropertyName = e.Context.PropertyDescriptor.Name;

        System.Drawing.Image myImage;
        Rectangle myRect = new Rectangle();
        myRect = e.Bounds;
        myRect.Inflate(-2, -2);

        if (PropertyName == "IpAdress")
        {
            //myImage = My.Resources.Resources.Wifi;
            myImage = global::ShellyConnect.Properties.Resources.Wifi;
            e.Graphics.DrawImage(myImage, myRect);
        }
        else if (PropertyName == "ShellyType")
        {
            Shelly.ShellyType myShellyType = (Shelly.ShellyType)System.Enum.Parse_
                                             (typeof(Shelly.ShellyType),
                                              System.Convert.ToString(e.Value), true);

            switch (myShellyType)
            {
                case Shelly.ShellyType.Shelly_Plus1:
                case Shelly.ShellyType.Shelly_1:
                case ShellyType.Shelly_1_g3:
                case ShellyType.Shelly_1L:
                    {
                        myImage = global::ShellyConnect.Properties.Resources.Shelly_1;
                        break;
                    }

                case Shelly.ShellyType.Shelly_Plus2:
                case ShellyType.Shelly_2:
                case ShellyType.Shelly_25:
                    {
                        myImage = global::ShellyConnect.Properties.Resources.Shelly_2;
                        break;
                    }

                case Shelly.ShellyType.Shelly_Dimmer12:
                    {
                        myImage = global::ShellyConnect.Properties.Resources.Shelly_Dimmer;
                        break;
                    }

                case Shelly.ShellyType.Shelly_Bulb:
                case ShellyType.Shelly_Bulb_Duo:
                case ShellyType.Shelly_Bulb_RGBW:
                case ShellyType.Shelly_Bulb_Vintage:
                    {
                        myImage = global::ShellyConnect.Properties.Resources.Shelly_Bulb;
                        break;
                    }

                case Shelly.ShellyType.Shelly_Plug:
                    {
                        myImage = global::ShellyConnect.Properties.Resources.Shelly_Plug;
                        break;
                    }

                case Shelly.ShellyType.Shelly_i4Plus:
                case ShellyType.Shelly_i3:
                    {
                        myImage = global::ShellyConnect.Properties.Resources.Shelly_I4;
                        break;
                    }

                default:
                    {
                        myImage = global::ShellyConnect.Properties.Resources.unknown;
                        break;
                    }
            }

            e.Graphics.DrawImage(myImage, myRect);
        }
    }
}

首先,必须将我的 ZIP 存档中包含的图像作为项目资源添加,以便编辑器可以访问它们并在后续项目中集成。 在 C# 示例中,编辑器中的访问路径可能需要调整(在我的情况下,解决方案本身称为 ShellyConnect——对您来说名称肯定不同。 VB 在这方面有点友好 😉)。

关于我的编辑器

首先,“GetPaintValueSupported”方法告知编辑器应绘制符号——因此只需返回 TRUE

PaintValue 方法现在负责绘图本身。 所有图形绘制方法都可以在此方法中使用(因此您可以完全自由地绘制)——我在这里使用 DrawImage 方法。

现在稍微谈谈传递给该方法的一些 PaintValueEventArgs,因为它们本质上是关键。

这用于使用 e.Context.PropertyDescriptor.Name 传递当前活动属性的名称。

此外,使用 e.Value 传递属性的内容,并使用 e.Bounds 传递可用绘图区域的大小(我将其宽度和高度都减小了 2 像素,这样图像就不会画到边缘——那看起来不好看)。

现在我只是根据属性名称及其内容输出相应的图形作为符号。

ICustomTypeDescriptor 接口

实现 ICustomTypeDescriptor 接口可以在设计时访问属性行为。 我利用这一点,根据所选的 Shelly 设备和所选的操作,只使那些有意义且根据所选设备可用的属性可见(在某些情况下也可序列化)。

以下是一些不同的表示

  

  

该接口包含许多我必须创建但实际上不使用的方法——这无法避免……对我来说,只有接口中的两个 GetProperties 方法是相关的——在这里,我调用我自己的方法 FilterProperties,我首先以代码的形式呈现它

Private Function FilterProperties(ByVal origProperties As PropertyDescriptorCollection) _
        As PropertyDescriptorCollection

    Dim myPD As PropertyDescriptor
    Dim myList As New List(Of PropertyDescriptor)
    Dim set_Hidden, noSerialize As Boolean

    For i As Integer = 0 To origProperties.Count - 1
        myPD = origProperties.Item(i)

        set_Hidden = False
        noSerialize = False

        Select Case myPD.Name
            Case "ShellyText"
                If myShellyText = "" _
                Or ShellyDevice = Shelly.ShellyType.None Then set_Hidden = True
            Case "OutputNr"
                If ShellyDevice = Shelly.ShellyType.Shelly_1 _
                Or ShellyDevice = Shelly.ShellyType.Shelly_1L _
                Or ShellyDevice = Shelly.ShellyType.Shelly_1_g3 _
                Or ShellyDevice = Shelly.ShellyType.Shelly_Bulb _
                Or ShellyDevice = Shelly.ShellyType.Shelly_Bulb_RGBW _
                Or ShellyDevice = Shelly.ShellyType.Shelly_Bulb_Duo _
                Or ShellyDevice = Shelly.ShellyType.Shelly_Bulb_Vintage _
                Or ShellyDevice = Shelly.ShellyType.Shelly_Dimmer12 _
                Or ShellyDevice = Shelly.ShellyType.Shelly_Plus1 _
                Or ShellyDevice = Shelly.ShellyType.Shelly_Pro1 _
                Or ShellyDevice = Shelly.ShellyType.Shelly_Plug _
                Or ShellyDevice = Shelly.ShellyType.Shelly_Motion12 _
                Or ShellyDevice = Shelly.ShellyType.Shelly_i3 _
                Or ShellyDevice = Shelly.ShellyType.Shelly_i4Plus _
                Or ShellyDevice = Shelly.ShellyType.None _
                Or myAction = ActionDefinition.SetRoller _
                Or myAction = ActionDefinition.ToggleRoller Then set_Hidden = True
            Case "ActionS1"
                If Shelly_GetBaseType(ShellyDevice) <> ShellyBaseType.Outs1 _
                   Then set_Hidden = True
            Case "ActionS2"
                If Shelly_GetBaseType(ShellyDevice) <> ShellyBaseType.Outs2 _
                   Then set_Hidden = True
            Case "ActionD"
                If Shelly_GetBaseType(ShellyDevice) <> ShellyBaseType.Dimmer _
                   Then set_Hidden = True
            Case "ActionL"
                If Shelly_GetBaseType(ShellyDevice) <> ShellyBaseType.Lamp _
                   Then set_Hidden = True
            Case "State"
                If myAction <> Shelly.ActionDefinition.SetOut _
                Or myAction = Shelly.ActionDefinition.none Then set_Hidden = True
            Case "RollerAction"
                If myAction <> Shelly.ActionDefinition.SetRoller _
                Or myAction = Shelly.ActionDefinition.none Then set_Hidden = True
            Case "RollerPosition"
                If myAction <> Shelly.ActionDefinition.SetRoller _
                Or myAction = Shelly.ActionDefinition.none Then set_Hidden = True
                'Or RollerAction <> RollerMode.toPosition _
            Case "Value"
                If myAction <> Shelly.ActionDefinition.SetRoller _
                And myAction <> Shelly.ActionDefinition.ToggleRoller _
                Or myAction = Shelly.ActionDefinition.none Then set_Hidden = True
            Case "Brightness"
                    If ShellyDevice <> Shelly.ShellyType.Shelly_Dimmer12 _
                     And ShellyDevice <> Shelly.ShellyType.Shelly_Bulb _
                     And ShellyDevice <> Shelly.ShellyType.Shelly_Bulb_Vintage _
                     And ShellyDevice <> Shelly.ShellyType.Shelly_Bulb_RGBW _
                     And ShellyDevice <> Shelly.ShellyType.Shelly_Bulb_Duo _
                     And ShellyDevice <> Shelly.ShellyType.Shelly_RGBW2 _
                     Or myAction = Shelly.ActionDefinition.none Then set_Hidden = True
            Case "ColorTone"
                    If ShellyDevice <> Shelly.ShellyType.Shelly_Dimmer12 _
                    And ShellyDevice <> Shelly.ShellyType.Shelly_Bulb _
                    And ShellyDevice <> Shelly.ShellyType.Shelly_Bulb_RGBW _
                    And ShellyDevice <> Shelly.ShellyType.Shelly_Bulb_Duo _
                    And ShellyDevice <> Shelly.ShellyType.Shelly_RGBW2 _
                    Or myColor <> Drawing.Color.White _
                    Or ShellyDevice = Shelly.ShellyType.None _
                       Then set_Hidden = True : noSerialize = True
            Case "Color"
                    If ShellyDevice <> Shelly.ShellyType.Shelly_Bulb _
                    And ShellyDevice <> Shelly.ShellyType.Shelly_Bulb_RGBW _
                    And ShellyDevice <> Shelly.ShellyType.Shelly_RGBW2 _
                    Or ShellyDevice = Shelly.ShellyType.None _
                       Then set_Hidden = True : noSerialize = True
            Case "ColorEffect"
                    If ShellyDevice <> Shelly.ShellyType.Shelly_Bulb_RGBW _
                    And ShellyDevice <> Shelly.ShellyType.Shelly_RGBW2 _
                    Or myColor = Drawing.Color.White _
                    Or ShellyDevice = Shelly.ShellyType.None _
                       Then set_Hidden = True : noSerialize = True

        End Select

        If set_Hidden Then      ' make Property unvisible inside the Property-Grid
            myPD = TypeDescriptor.CreateProperty(myPD.ComponentType, myPD, New Attribute() _
            {New BrowsableAttribute(False), _
             New EditorBrowsableAttribute(EditorBrowsableState.Never)})
        End If
        If noSerialize Then     ' this Property will not be serialized
            myPD = TypeDescriptor.CreateProperty(myPD.ComponentType, myPD, New Attribute() _
            {New DesignerSerializationVisibilityAttribute_
            (DesignerSerializationVisibility.Hidden)})
        End If
        myList.Add(myPD)
    Next

    Dim myPDListe(myList.Count - 1) As PropertyDescriptor
    myList.CopyTo(myPDListe)

    Return New PropertyDescriptorCollection(myPDListe)
End Function
private PropertyDescriptorCollection FilterProperties_
        (PropertyDescriptorCollection origProperties)
{
    PropertyDescriptor myPD;
    List<propertydescriptor> myList = new List<propertydescriptor>();
    bool set_Hidden, noSerialize;

    for (int i = 0; i <= origProperties.Count - 1; i++)
    {
        myPD = origProperties[i];

        set_Hidden = false;
        noSerialize = false;

        switch (myPD.Name)
        {
            case "ShellyText":
                {
                 if (myShellyText == "" | ShellyDevice == Shelly.ShellyType.None)
                    set_Hidden = true;
                 break;
                }

            case "OutputNr":
                {
                    if (ShellyDevice == Shelly.ShellyType.Shelly_1
                      | ShellyDevice == Shelly.ShellyType.Shelly_1L
                      | ShellyDevice == Shelly.ShellyType.Shelly_1_g3
                      | ShellyDevice == Shelly.ShellyType.Shelly_Bulb
                      | ShellyDevice == Shelly.ShellyType.Shelly_Bulb_RGBW
                      | ShellyDevice == Shelly.ShellyType.Shelly_Bulb_Duo
                      | ShellyDevice == Shelly.ShellyType.Shelly_Bulb_Vintage
                      | ShellyDevice == Shelly.ShellyType.Shelly_Dimmer12
                      | ShellyDevice == Shelly.ShellyType.Shelly_Plus1
                      | ShellyDevice == Shelly.ShellyType.Shelly_Pro1
                      | ShellyDevice == Shelly.ShellyType.Shelly_Plug
                      | ShellyDevice == Shelly.ShellyType.Shelly_Motion12
                      | ShellyDevice == Shelly.ShellyType.Shelly_i3
                      | ShellyDevice == Shelly.ShellyType.Shelly_i4Plus
                      | ShellyDevice == Shelly.ShellyType.None
                      | myAction == ActionDefinition.SetRoller
                      | myAction == ActionDefinition.ToggleRoller)
                        set_Hidden = true;
                    break;
                }

            case "ActionS1":
                {
                    if (ShellyCom2.Shelly_GetBaseType(ShellyDevice) != ShellyBaseType.Outs1)
                        set_Hidden = true;
                    break;
                }

            case "ActionS2":
                {
                    if (ShellyCom2.Shelly_GetBaseType(ShellyDevice) != ShellyBaseType.Outs2)
                        set_Hidden = true;
                    break;
                }

            case "ActionD":
                {
                    if (ShellyCom2.Shelly_GetBaseType(ShellyDevice) != ShellyBaseType.Dimmer)
                        set_Hidden = true;
                    break;
                }

            case "ActionL":
                {
                    if (ShellyCom2.Shelly_GetBaseType(ShellyDevice) != ShellyBaseType.Lamp)
                        set_Hidden = true;
                    break;
                }

            case "State":
                {
                    if (myAction != Shelly.ActionDefinition.SetOut | 
                        myAction == Shelly.ActionDefinition.none)
                        set_Hidden = true;
                    break;
                }

            case "RollerAction":
                {
                    if (myAction != Shelly.ActionDefinition.SetRoller | 
                        myAction == Shelly.ActionDefinition.none)
                        set_Hidden = true;
                    break;
                }

            case "RollerPosition":
                {
                    if (myAction != Shelly.ActionDefinition.SetRoller
                      | myAction == Shelly.ActionDefinition.none)
                        set_Hidden = true;
                    break;
                    //| RollerAction != RollerMode.toPosition
                }

            case "Value":
                {
                    if (myAction != Shelly.ActionDefinition.SetDimmer
                      & myAction != Shelly.ActionDefinition.SetLamp
                      & myAction != Shelly.ActionDefinition.SetRoller
                      & myAction != Shelly.ActionDefinition.ToggleRoller
                      | myAction == Shelly.ActionDefinition.none)
                        set_Hidden = true;
                    break;
                }

            case "Brightness":
                {
                    if (ShellyDevice != Shelly.ShellyType.Shelly_Dimmer12
                      & ShellyDevice != Shelly.ShellyType.Shelly_Bulb
                      & ShellyDevice != Shelly.ShellyType.Shelly_Bulb_Vintage
                      & ShellyDevice != Shelly.ShellyType.Shelly_Bulb_RGBW
                      & ShellyDevice != Shelly.ShellyType.Shelly_Bulb_Duo
                      & ShellyDevice != Shelly.ShellyType.Shelly_RGBW2
                      | myAction == Shelly.ActionDefinition.none)
                        set_Hidden = true;
                    break;
                }

            case "ColorTone":
                {
                    if (ShellyDevice != Shelly.ShellyType.Shelly_Dimmer12
                      & ShellyDevice != Shelly.ShellyType.Shelly_Bulb
                      & ShellyDevice != Shelly.ShellyType.Shelly_Bulb_RGBW
                      & ShellyDevice != Shelly.ShellyType.Shelly_Bulb_Duo
                      & ShellyDevice != Shelly.ShellyType.Shelly_RGBW2
                      | myColor != System.Drawing.Color.White
                      | ShellyDevice == Shelly.ShellyType.None)
                        { set_Hidden = true; noSerialize = true; }
                        break;
                }

            case "Color":
                {
                    if (ShellyDevice != Shelly.ShellyType.Shelly_Bulb
                      & ShellyDevice != Shelly.ShellyType.Shelly_Bulb_RGBW
                      & ShellyDevice != Shelly.ShellyType.Shelly_RGBW2
                      | ShellyDevice == Shelly.ShellyType.None)
                        { set_Hidden = true; noSerialize = true; }
                        break;
                }

            case "ColorEffect":
                {
                    if (ShellyDevice != Shelly.ShellyType.Shelly_Bulb_RGBW
                     & ShellyDevice != Shelly.ShellyType.Shelly_RGBW2
                     | myColor == System.Drawing.Color.White
                     | ShellyDevice == Shelly.ShellyType.None)
                        { set_Hidden = true; noSerialize = true; }
                        break;
                }
        }

        if (set_Hidden)
            myPD = TypeDescriptor.CreateProperty_
                   (myPD.ComponentType, myPD, new System.Attribute[]
            { new BrowsableAttribute(false), _
              new EditorBrowsableAttribute(EditorBrowsableState.Never) });
        if (noSerialize)
            myPD = TypeDescriptor.CreateProperty_
                   (myPD.ComponentType, myPD, new System.Attribute[]
            { new DesignerSerializationVisibilityAttribute_
            (DesignerSerializationVisibility.Hidden) });
        myList.Add(myPD);
    }

    PropertyDescriptor[] myPDListe = new PropertyDescriptor[myList.Count - 1 + 1];
    myList.CopyTo(myPDListe);

    return new PropertyDescriptorCollection(myPDListe);
}</propertydescriptor></propertydescriptor>

整个过程都与 SceneActionDefinition 相关,该接口属于它。

我用相应的注释文本(依赖属性)标识了那些行为/可见性会被我操纵的属性。

这一切是如何工作的?

重要的是,可以影响 PropertyGrid 的属性必须具有 RefreshProperties(RefreshProperties.All) 属性。 当该属性的值更改时,这会导致 PropertyGrid 重新绘制。

该类的 PropertyDescriptorCollection 从接口传递到我的 FilterProperties 方法(因此它包含类的所有属性),并且我的方法返回我修改后的 PropertyDescriptorCollection。 我在许多“自定义”控件上使用了此方法来隐藏从继承的基类传递但我在我的控件中不使用的属性。 嗯……基本上这里也发生了类似的事情。

例如,某些 Shelly 设备只有一个输出,甚至没有输出——在这种情况下,我会隐藏“OutputNr”属性。

或者对于操作:这里使用的 ActionsDefinition 是一个 ENum——我无法根据情况影响其内容。 但是,我可以处理多个 Enum 和关联的属性。 根据 Shelly 设备(ShellyBaseType),会在属性之间进行切换,每个属性使用一个简化的 ENum,最后所有这些部分 ENum 都被转换为实际的 Enum(但后者发生在类中)。

因此,所有类的属性都会在一个循环中进行遍历,并检查在什么条件下会对它们执行某些操作。 根据循环中设置的布尔值 set_HiddennoSerialize 或两者都设置,相应的属性将在最后添加到 PropertyDescriptor

我还没有发现(除了我自己的控件之外)在任何地方使用过这种方法,但我认为它是一种优化控件(或组件)可用性的有用方法,尤其是在这种情况下。

其他更改

ShellyCom2 模块和 Definitions 模块内部,我出于 DRY(Don’t repeat yourself)的考虑进行了一些重组,不幸的是进行了一些重命名,但肯定有所扩展。 这里链接的存档基本上代表了我之前文章中先前工作的一个全新版本。

但我认为,所有下载了我先前作品的人都会对这里实现的更改感到满意……

最后——最后的几句话

我想再次感谢 @sean-ewington 在创建本文和之前文章中的帮助。

此外,Shelly 的支持和市场营销部门在设备扩展方面也对我提供了特别的帮助。

此外,这个论坛中丰富的知识在开发转换器和编辑器方面对我帮助很大。 这里使用的所有功能以前都以某种方式在其他文章中有所涵盖。 由于我使用了这些东西很长时间,并且已经修改了许多次,因此我已无法指明其来源。

在此,我想再次强调,我绝对乐于接受建议和想法,即灵感……

© . All rights reserved.