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





5.00/5 (2投票s)
本文彻底改变了 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_SetLamp
或Shelly_ToggleLamp
中考虑了这些方面。
不同组件的项目定义
最初,每个组件都有自己的项目定义——我现在已将其合并到所有组件中。 因此,在 Definitions 文件中,只有 SceneActionDefinition
和派生自 SceneActionDefinition
的 ControlActionDefinition
。
这些定义的结构基本保持不变——只添加了一些其他必需的属性……而且…… ICustomTypeDescriptor
接口,它允许根据预选集在 PropertyGrid
中使单个属性可见或不可见。 我将在下面描述它是如何工作的。
此外,作为一种炫技,还添加了 UITypeEditor
Symbol2Property
,它会在 2 个属性前面显示图像符号。 为了使此功能生效,必须将包含的图形作为资源导入到项目中。 我也将在下面更详细地介绍这一点。
最终的惊喜是 TypeConverter SelectionOnOffConverter
,它现在代表 State
属性的 ON
和 OFF
,而不是 TRUE
和 FALSE
。 我也将在下面介绍这一点。
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 已经写了许多关于 TypeConverter
s 的文章——所以如果你有兴趣自己创建一个这样的东西,我建议你在搜索中输入它。
基本过程如下
CanConvertTo
方法确定可以创建哪种目标格式——在这种情况下是String
。ConvertTo
方法现在决定要输出的string
的内容。 在这里,TRUE
和FALSE
被转换为ON
和OFF
(用于输出)。CanConvertFrom
方法确定可以从哪种源格式进行转换——在这种情况下是String
。ConvertFrom
方法现在将传入的值,这里是ON
和OFF
,转换回布尔值TRUE
和FALSE
。
我承认这确实是一种炫技——但我觉得在上下文中这样做很不错……😉
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_Hidden
或 noSerialize
或两者都设置,相应的属性将在最后添加到 PropertyDescriptor
。
我还没有发现(除了我自己的控件之外)在任何地方使用过这种方法,但我认为它是一种优化控件(或组件)可用性的有用方法,尤其是在这种情况下。
其他更改
在 ShellyCom2
模块和 Definitions
模块内部,我出于 DRY(Don’t repeat yourself)的考虑进行了一些重组,不幸的是进行了一些重命名,但肯定有所扩展。 这里链接的存档基本上代表了我之前文章中先前工作的一个全新版本。
但我认为,所有下载了我先前作品的人都会对这里实现的更改感到满意……
最后——最后的几句话
我想再次感谢 @sean-ewington 在创建本文和之前文章中的帮助。
此外,Shelly 的支持和市场营销部门在设备扩展方面也对我提供了特别的帮助。
此外,这个论坛中丰富的知识在开发转换器和编辑器方面对我帮助很大。 这里使用的所有功能以前都以某种方式在其他文章中有所涵盖。 由于我使用了这些东西很长时间,并且已经修改了许多次,因此我已无法指明其来源。
在此,我想再次强调,我绝对乐于接受建议和想法,即灵感……