智能家居 – 控制 Shelly® 设备(第三部分)





5.00/5 (1投票)
本文将介绍 ShellySceneComponent 和 ShellyScenesComponent,它们允许用户为 Shelly 设备定义包含多个操作的场景,这些操作可分配给一个或多个控件。
引言
本文的基础是我上一篇文章的描述:控制 Shelly 设备 - 第 1 部分。
这是一系列文章 - 其中还包括:控制 Shelly 设备 - 第 2 部分。
在本文中,我将介绍另外两个组件,一些我认为有用的功能,然后,遵循 DRY(Don't Repeat Yourself,不要重复自己)原则,我稍微修改了前两篇文章中的基本例程。因此,如果您感兴趣,下载本文的存档并替换之前的组件是有意义的。出于兼容性考虑,我在此点上保持先前文章不变。
新组件是
ShellySceneComponent
– 在这里您可以定义一个场景,该场景可以包含许多操作,但只分配给一个控件(按钮)。ShellyScenesComponent
– 在这里您可以定义许多场景,每个场景可以包含许多操作,并分配给相应的控件(按钮)。
这两个组件都可以与标准控件配合使用,无需调整。
作为一项功能,我添加了一个 TypeConverter
,它允许您使用列表选择而不是输入来设置 IpAdress
属性,该属性访问项目中已知且预定义的地址。
我还修改了 ShellyCom
例程,并添加了另一个修改版本的例程,称为 ShellyCom2
。我个人认为 ShellyCom2
版本更优雅。
ShellySceneComponent
此组件允许您将任意数量的操作分配给一个控件,然后可以通过单击触发这些操作。此组件的基本功能与上一篇文章《控制 Shelly 设备 - 第 2 部分》中介绍的 ShellyActionComponent
没有显著区别。只需选择一个控件,然后为其分配任意数量的操作(参见截图)。
此组件不会导致关联控件的动画。
ShellyScenesComponent
此组件允许您将任意数量的操作分配给多个控件,然后可以通过单击相应的控件来触发这些操作。此组件的基本功能与之前提到的组件没有显著区别 - 唯一的区别是在一个集合中调用另一个集合(参见截图)。
我在这里展示了这个组合的结构代码
<TypeConverter(GetType(ExpandableObjectConverter))>
Partial Public Class SceneAssignmentDefinition
'if the code-line with the TypeConverter is activated this Property works with already defined IP addresses
'provided inside the File Definitions. The Property "myIpAdresses" is part of this behaviour.
'This makes the application (if you want) simpler and less error-prone
<TypeConverter(GetType(DropDownConverter)), DropDownConverterData("myIpAdresses")>
<Category("Shelly"), Description("IpAdress of the Shelly-Device to work with")>
<RefreshProperties(RefreshProperties.All)>
Property IpAdress As String
Get
Return myIPAdress
End Get
Set(value As String)
If System.Net.IPAddress.TryParse(value, myIP) Then
myShellyType = ShellyCom.Shelly_GetType(value)
If myShellyType <> Shelly.ShellyType.None Then myIPAdress = value
If myShellyType = Shelly.ShellyType.Shelly_Dimmer2 Then myOutput = 0
End If
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never)>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
ReadOnly Property myIpAdresses As String()
Get
Return Shelly.My.IpAdresses ' availible adresses are defined in File: Definitions
End Get
End Property
Private myIPAdress As String = ""
Private myIP As System.Net.IPAddress
<Category("Shelly"), Description("shows the Type of the connected Shelly-Device")>
ReadOnly Property ShellyType As String
Get
Return myShellyType.ToString
End Get
End Property
Private myShellyType As Shelly.ShellyType
<Category("Shelly"), Description("Output-Number of the Shelly-Device to work with")>
<DefaultValue(0)>
Property OutputNr As Integer
Get
Return myOutput
End Get
Set(value As Integer)
If (value >= 0) And (value <= 1) Then
myOutput = value
End If
End Set
End Property
Private myOutput As Integer = 0
<Category("Shelly"), Description("the Value which is assigned to the Shelly-Device")>
<DefaultValue(0)>
Property Value As Integer
Get
Return myValue
End Get
Set(value As Integer)
If (value >= 0) And (value <= 100) Then
myValue = value
End If
End Set
End Property
Private myValue As Integer = 0
<Category("Shelly"), Description("the Action which happens with a Control-Click Event")>
<DefaultValue(GetType(Shelly.ActionDefinition), "none")>
Property Action As Shelly.ActionDefinition
Get
Return myAction
End Get
Set(value As Shelly.ActionDefinition)
myAction = value
End Set
End Property
Private myAction As Shelly.ActionDefinition = Shelly.ActionDefinition.none
Public Sub New()
End Sub
Public Overrides Function toString() As String
Return myIPAdress + ", " + myAction.ToString + ", O:" + myOutput.ToString.Trim + ", Val:" + myValue.ToString.Trim
End Function
End Class
<TypeConverter(GetType(ExpandableObjectConverter))>
Partial Public Class ControlAssignmentDefinition
<Category("Control"), Description("the Control which should do the Action(s)")>
Property SelectedControl As Control
Get
Return mySelectedControl
End Get
Set(value As Control)
mySelectedControl = value
End Set
End Property
Private mySelectedControl As Control = Nothing
Public savedBackColor As Color
Public savedForeColor As Color
<Category("Control"), Description("says that this Scene was last activated")>
<DefaultValue(False)>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
Property isActivated As Boolean
Get
Return myActivated
End Get
Set(value As Boolean)
myActivated = value
End Set
End Property
Private myActivated As Boolean = False
<Category("Control-Settings"), Description("Enables the Animation of this Control")>
<DefaultValue(True)>
Property EnableAnimation As Boolean
Get
Return my_EnableAnimation
End Get
Set(value As Boolean)
my_EnableAnimation = value
End Set
End Property
Private my_EnableAnimation As Boolean = True
<Category("Control"), Description("Assignment to the Shelly-Devices")>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
ReadOnly Property ShellyActions As ScenesCollection
Get
Return myShellyActions
End Get
End Property
Private myShellyActions As New ScenesCollection
Public Overrides Function toString() As String
If mySelectedControl IsNot Nothing Then Return mySelectedControl.Name
Return "[-]"
End Function
End Class
[TypeConverter(typeof(ExpandableObjectConverter))]
public partial class SceneAssignmentDefinition
{
// if the code-line with the TypeConverter is activated this Property works with already defined IP addresses
// provided inside the File Definitions. The Property "myIpAdresses" is part of this behaviour.
// This makes the application (if you want) simpler and less error-prone
[TypeConverter(typeof(DropDownConverter))]
[DropDownConverterData("myIpAdresses")]
[Category("Shelly")]
[Description("IpAdress of the Shelly-Device to work with")]
[RefreshProperties(RefreshProperties.All)]
public string IpAdress
{
get { return myIPAdress; }
set
{
if (System.Net.IPAddress.TryParse(value, out myIP))
{
myShellyType = ShellyCom.Shelly_GetType(value);
if (myShellyType != Shelly.ShellyType.None)
myIPAdress = value;
if (myShellyType == Shelly.ShellyType.Shelly_Dimmer2)
myOutput = 0;
}
}
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string[] myIpAdresses
{
get { return Shelly.My.IpAdresses; } // availible adresses are defined in File: Definitions
}
private string myIPAdress = "";
private System.Net.IPAddress myIP;
[Category("Shelly")]
[Description("shows the Type of the connected Shelly-Device")]
public string ShellyType
{
get { return Convert.ToString(myShellyType); }
}
private Shelly.ShellyType myShellyType;
[Category("Shelly")]
[Description("Output-Number of the Shelly-Device to work with")]
[DefaultValue(0)]
public int OutputNr
{
get { return myOutput; }
set
{
if ((value >= 0) & (value <= 1))
myOutput = value;
}
}
private int myOutput = 0;
[Category("Shelly")]
[Description("the Value which is assigned to the Shelly-Device")]
[DefaultValue(0)]
public int Value
{
get { return myValue; }
set
{
if ((value >= 0) & (value <= 100))
myValue = value;
}
}
private int myValue = 0;
[Category("Shelly")]
[Description("the Action which happens with a Control-Click Event")]
[DefaultValue(typeof(Shelly.ActionDefinition), "none")]
public Shelly.ActionDefinition Action
{
get { return myAction; }
set { myAction = value; }
}
private Shelly.ActionDefinition myAction = Shelly.ActionDefinition.none;
public SceneAssignmentDefinition()
{
}
public override string ToString()
{
return myIPAdress + ", " + Convert.ToString(myAction) + ", O:" + myOutput.ToString().Trim() + ", Val:" + myValue.ToString().Trim();
}
}
[TypeConverter(typeof(ExpandableObjectConverter))]
public partial class ControlAssignmentDefinition
{
[Category("Control")]
[Description("the Control which should do the Action(s)")]
public Control SelectedControl
{
get { return mySelectedControl; }
set { mySelectedControl = value; }
}
private Control mySelectedControl = null/* TODO Change to default(_) if this is not a reference type */;
public Color savedBackColor;
public Color savedForeColor;
[Category("Control")]
[Description("says that this Scene was last activated")]
[DefaultValue(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool isActivated
{
get { return myActivated; }
set { myActivated = value; }
}
private bool myActivated = false;
[Category("Control-Settings")]
[Description("Enables the Animation of this Control")]
[DefaultValue(true)]
public bool EnableAnimation
{
get { return my_EnableAnimation; }
set { my_EnableAnimation = value; }
}
private bool my_EnableAnimation = true;
[Category("Control")]
[Description("Assignment to the Shelly-Devices")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ScenesCollection ShellyActions
{
get { return myShellyActions; }
}
private ScenesCollection myShellyActions = new ScenesCollection();
public override string ToString()
{
if (mySelectedControl != null)
return mySelectedControl.Name;
return "[-]";
}
}
最初,这是两个集合的两个基类。
如您所见,ScenesCollection
已集成到 ControlAssignmentDefinition
类中。
这些是关联的集合
Partial Public Class ActionCollection
Inherits CollectionBase
Public Sub Add(ByVal item As ControlAssignmentDefinition)
Dim myControl As Control = item.SelectedControl
If myControl IsNot Nothing Then AddHandler item.SelectedControl.Click, AddressOf ControlClickHandler
List.Add(item)
End Sub
Public Sub Insert(ByVal item As ControlAssignmentDefinition)
Dim myControl As Control = item.SelectedControl
If myControl IsNot Nothing Then AddHandler item.SelectedControl.Click, AddressOf ControlClickHandler
List.Add(item)
End Sub
Public Sub Remove(ByVal index As Integer)
If (index > -1) Then
Dim item As ControlAssignmentDefinition = List.Item(index)
Dim myControl As Control = item.SelectedControl
If myControl IsNot Nothing Then RemoveHandler item.SelectedControl.Click, AddressOf ControlClickHandler
List.RemoveAt(index)
End If
End Sub
Public Property Item(ByVal index As Integer) As ControlAssignmentDefinition
Get
Return List(index)
End Get
Set(ByVal value As ControlAssignmentDefinition)
List(index) = value
End Set
End Property
Public ReadOnly Property Item(ByVal ControlName As String) As ControlAssignmentDefinition
Get
For i As Integer = 0 To List.Count - 1
If Item(i).SelectedControl.Name = ControlName Then Return Item(i)
Next
Return Nothing
End Get
End Property
Public Shadows Sub Clear()
For i As Integer = 0 To List.Count - 1
RemoveHandler Item(i).SelectedControl.Click, AddressOf ControlClickHandler
Next
List.Clear()
End Sub
Overrides Function ToString() As String
Return "[...]"
End Function
Public Sub Dispose()
Me.Clear()
End Sub
Property Enabled As Boolean
Get
Return my_Enabled
End Get
Set(value As Boolean)
my_Enabled = value
End Set
End Property
Private my_Enabled As Boolean = True
Private Sub ControlClickHandler(sender As System.Object, e As System.EventArgs)
If Not my_Enabled Then Exit Sub
Dim myControl As Control = sender
Dim myItem As ControlAssignmentDefinition = Item(myControl.Name)
If myItem IsNot Nothing Then
For i As Integer = 0 To List.Count - 1
Item(i).isActivated = False
Next
myItem.isActivated = True
For i As Integer = 0 To myItem.ShellyActions.Count - 1
Dim myAction As SceneAssignmentDefinition = myItem.ShellyActions.Item(i)
If myAction IsNot Nothing Then
If myAction.Action <> Shelly.ActionDefinition.none Then
Select Case myAction.Action
Case Shelly.ActionDefinition.SetOut
Dim myState As Boolean = myAction.Value > 0
ShellyCom.Shelly_SetOutput(myAction.IpAdress, myAction.OutputNr, myState)
Case Shelly.ActionDefinition.ToggleOut
ShellyCom.Shelly_ToggleOutput(myAction.IpAdress, myAction.OutputNr)
Case Shelly.ActionDefinition.SetDimmer
ShellyCom.Shelly_SetDimmer(myAction.IpAdress, myAction.Value)
Case Shelly.ActionDefinition.SetRoller
ShellyCom.Shelly_SetRoller(myAction.IpAdress, myAction.Value)
Case Shelly.ActionDefinition.ToggleRoller
ShellyCom.Shelly_ToggleRoller(myAction.IpAdress)
Case Shelly.ActionDefinition.none
' do nothing - only for display Values
End Select
End If
End If
Next
End If
End Sub
End Class
Partial Public Class ScenesCollection
Inherits CollectionBase
Public Sub Add(ByVal item As SceneAssignmentDefinition)
List.Add(item)
End Sub
Public Sub Insert(ByVal item As SceneAssignmentDefinition)
List.Add(item)
End Sub
Public Sub Remove(ByVal index As Integer)
If (index > -1) Then
Dim item As SceneAssignmentDefinition = List.Item(index)
List.RemoveAt(index)
End If
End Sub
Public Property Item(ByVal index As Integer) As SceneAssignmentDefinition
Get
Return List(index)
End Get
Set(ByVal value As SceneAssignmentDefinition)
List(index) = value
End Set
End Property
Public Shadows Sub Clear()
List.Clear()
End Sub
Overrides Function ToString() As String
Return "[...]"
End Function
Public Sub Dispose()
List.Clear()
End Sub
End Class
public class ActionCollection : System.Collections.Generic.List<ControlAssignmentDefinition>
{
public void Add(ControlAssignmentDefinition item)
{
Control myControl = item.SelectedControl;
if (myControl != null)
item.SelectedControl.Click += ControlClickHandler;
base.Add(item);
}
public void Insert(ControlAssignmentDefinition item)
{
Control myControl = item.SelectedControl;
if (myControl != null)
item.SelectedControl.Click += ControlClickHandler;
base.Add(item);
}
public void Remove(int index)
{
if ((index > -1))
{
ControlAssignmentDefinition item = (ControlAssignmentDefinition)this[index];
Control myControl = item.SelectedControl;
if (myControl != null)
item.SelectedControl.Click -= ControlClickHandler;
base.RemoveAt(index);
}
}
public ControlAssignmentDefinition Item(int index)
{ return (ControlAssignmentDefinition)this[index]; }
public ControlAssignmentDefinition Item(String ControlName)
{
for (int i = 0; i <= Count - 1; i++)
{
ControlAssignmentDefinition myItem = (ControlAssignmentDefinition)this[i];
if (myItem.SelectedControl.Name == ControlName)
return myItem;
}
return null;
}
public new void Clear()
{
for (int i = 0; i <= Count - 1; i++)
Item(i).SelectedControl.Click -= ControlClickHandler;
base.Clear();
}
public override string ToString()
{ return "[...]"; }
public void Dispose()
{ this.Clear(); }
public bool Enabled
{
get { return my_Enabled; }
set { my_Enabled = value; }
}
private bool my_Enabled = true;
private void ControlClickHandler(System.Object sender, System.EventArgs e)
{
if (!my_Enabled)
return;
Control myControl = (Control)sender;
ControlAssignmentDefinition myItem = Item(myControl.Name);
if (myItem != null)
{
for (int i = 0; i <= Count - 1; i++)
Item(i).isActivated = false;
myItem.isActivated = true;
for (int i = 0; i <= myItem.ShellyActions.Count - 1; i++)
{
SceneAssignmentDefinition myAction = myItem.ShellyActions.Item(i);
if (myAction != null)
{
if (myAction.Action != Shelly.ActionDefinition.none)
{
switch (myAction.Action)
{
case ActionDefinition.SetOut:
{
bool myState = myAction.Value > 0;
ShellyCom.Shelly_SetOutput(myAction.IpAdress, myAction.OutputNr, myState);
break;
}
case ActionDefinition.ToggleOut:
{
ShellyCom.Shelly_ToggleOutput(myAction.IpAdress, myAction.OutputNr);
break;
}
case ActionDefinition.SetDimmer:
{
ShellyCom.Shelly_SetDimmer(myAction.IpAdress, myAction.Value);
break;
}
case ActionDefinition.SetRoller:
{
ShellyCom.Shelly_SetRoller(myAction.IpAdress, myAction.Value);
break;
}
case ActionDefinition.ToggleRoller:
{
ShellyCom.Shelly_ToggleRoller(myAction.IpAdress);
break;
}
case ActionDefinition.none:
{ break; }
}
}
}
}
}
}
}
public partial class ScenesCollection : System.Collections.Generic.List<SceneAssignmentDefinition>
{
public void Add(SceneAssignmentDefinition item)
{ base.Add(item); }
public void Insert(SceneAssignmentDefinition item)
{ base.Add(item); }
public void Remove(int index)
{
if ((index > -1))
{ base.RemoveAt(index); }
}
public SceneAssignmentDefinition Item(int index)
{ return (SceneAssignmentDefinition)this[index]; }
public new void Clear()
{ base.Clear(); }
public override string ToString()
{ return "[...]"; }
public void Dispose()
{ base.Clear(); }
}
在 ActionCollection
内部,会评估相应控件的单击事件(ControlClickHandler
),并执行从属集合中分配的操作。
但是,这里再次提供了使触发控件动起来的可能性。这仍然是通过组件本身的计时器完成的。
TypeConverter (DropDownConverter)
在此我展示了如何在属性中使用转换器
<TypeConverter(GetType(DropDownConverter)), DropDownConverterData("myIpAdresses")>
<Category("Shelly"), Description("IpAdress of the Shelly-Device to work with")>
<RefreshProperties(RefreshProperties.All)>
Property IpAdress As String
Get
Return myIPAdress
End Get
Set(value As String)
If System.Net.IPAddress.TryParse(value, myIP) Then
myShellyType = ShellyCom.Shelly_GetType(value)
If myShellyType <> Shelly.ShellyType.None Then myIPAdress = value
If myShellyType = Shelly.ShellyType.Shelly_Dimmer2 Then myOutput = 0
End If
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never)>
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
ReadOnly Property myIpAdresses As String()
Get
Return Shelly.My.IpAdresses ' availible adresses are defined in File: Definitions
End Get
End Property
Private myIPAdress As String = ""
Private myIP As System.Net.IPAddress
[TypeConverter(typeof(DropDownConverter, DropDownConverterData("myIpAdresses")]
[Category("Shelly")]
[Description("IpAdress of the Shelly-Device to work with")]
[RefreshProperties(RefreshProperties.All)]
public string IpAdress
{
get { return myIPAdress; }
set
{
if (System.Net.IPAddress.TryParse(value, out myIP))
{
myShellyType = ShellyCom.Shelly_GetType(value);
if (myShellyType != Shelly.ShellyType.None)
myIPAdress = value;
if (myShellyType == Shelly.ShellyType.Shelly_Dimmer2)
myOutput = 0;
}
}
}
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string[] myIpAdresses
{
get { return Shelly.My.IpAdresses; } // availible adresses are defined in File: Definitions
}
private string myIPAdress = "";
private System.Net.IPAddress myIP;
带有 TypeConverter
赋值的行决定了在选择属性时是否激活组合框功能(其中提供了属性 myIpAddresses
传递的字符串数组供选择),或者是否注释掉了该行,执行属性的默认功能。
我想显示的 IP 地址在 Definitions
文件中指定。
我之前在 CodeProject 的问答部分已经发布过 DropDownConverter
一两次了。
Imports System.ComponentModel
Public Class DropDownConverter
Inherits StringConverter
Public Overloads Overrides Function GetStandardValuesSupported(ByVal context As ITypeDescriptorContext) As Boolean
Return True 'True - tells the PropertyGrid, that a Combobox shall be shown
End Function
Public Overloads Overrides Function GetStandardValuesExclusive(ByVal context As ITypeDescriptorContext) As Boolean
Return True
' True - allows only selection out of the Combobox
' False - allows free inputs also
End Function
Private EntriesArray(-1) As String 'saves the selectable entries for the DropDown
Public Overrides Function CanConvertFrom(context As System.ComponentModel.ITypeDescriptorContext, sourceType As System.Type) As Boolean
If sourceType Is GetType(String) Then Return True 'allows the converting from String
Return MyBase.CanConvertFrom(context, sourceType)
End Function
Public Overloads Overrides Function GetStandardValues(ByVal context As ITypeDescriptorContext) As StandardValuesCollection
Dim myPD As PropertyDescriptor = Nothing
Dim myAttribute As DropDownConverterDataAttribute = context.PropertyDescriptor.Attributes(GetType(DropDownConverterDataAttribute))
If myAttribute IsNot Nothing Then ' is the name of the array to be used passed as an Attribute ...?
myPD = TypeDescriptor.GetProperties(context.Instance)(myAttribute.DataArray)
Else ' The name of the array to be used is derived from the name of the Property ...?
' identify Host-Property and related options
Dim HostPropertyName As String = context.PropertyDescriptor.Name
Dim HostPropertyArrayName As String = HostPropertyName + "Array"
myPD = TypeDescriptor.GetProperties(context.Instance)(HostPropertyArrayName)
End If
If myPD IsNot Nothing Then
If myPD.PropertyType Is GetType(String()) Then
EntriesArray = myPD.GetValue(context.Instance)
ElseIf myPD.PropertyType Is GetType(List(Of String)) Then
Dim myList As List(Of String) = myPD.GetValue(context.Instance)
ReDim EntriesArray(myList.Count - 1)
For i As Integer = 0 To myList.Count - 1
EntriesArray(i) = myList(i)
Next
ElseIf myPD.PropertyType Is GetType(Collection) Then
Dim myCollection As Collection = myPD.GetValue(context.Instance)
ReDim EntriesArray(myCollection.Count - 1)
For i As Integer = 0 To myCollection.Count - 1
EntriesArray(i) = myCollection.Item(i + 1)
Next
End If
End If
Return New StandardValuesCollection(EntriesArray) ' exports our specified options
End Function
End Class
<AttributeUsage(AttributeTargets.[Property] Or AttributeTargets.Method)> _
Public Class DropDownConverterDataAttribute
Inherits Attribute
Public Sub New(DataArray_PropertyName As String)
my_DataArray = DataArray_PropertyName
End Sub
Public ReadOnly Property DataArray() As String
Get
Return my_DataArray
End Get
End Property
Private my_DataArray As String
End Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
public class DropDownConverter : StringConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true; // True - tells the PropertyGrid, that a Combobox shall be shown
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{ return true; }
private string[] EntriesArray = new string[0]; // saves the selectable entries for the DropDown
public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Type sourceType)
{
if (sourceType == typeof(string))
return true; // allows the converting from String
return base.CanConvertFrom(context, sourceType);
}
public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
{
PropertyDescriptor myPD = null;
DropDownConverterDataAttribute myAttribute = (DropDownConverterDataAttribute)context.PropertyDescriptor.Attributes[typeof(DropDownConverterDataAttribute)];
if (myAttribute != null)
myPD = TypeDescriptor.GetProperties(context.Instance)[myAttribute.DataArray];
else
{
// identify Host-Property and related options
string HostPropertyName = context.PropertyDescriptor.Name;
string HostPropertyArrayName = HostPropertyName + "Array";
myPD = TypeDescriptor.GetProperties(context.Instance)[HostPropertyArrayName];
}
if (myPD != null)
{
if (myPD.PropertyType == typeof(string[]))
EntriesArray = (string[]) myPD.GetValue(context.Instance );
else if (myPD.PropertyType == typeof(List<string>))
{
List<string> myList = (List<string>) myPD.GetValue(context.Instance);
EntriesArray = new string[myList.Count - 1 + 1];
for (int i = 0; i <= myList.Count - 1; i++)
EntriesArray[i] = myList[i];
}
}
return new TypeConverter.StandardValuesCollection(EntriesArray); // exports our specified options
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)]
public class DropDownConverterDataAttribute : Attribute
{
public DropDownConverterDataAttribute(string DataArray_PropertyName)
{ my_DataArray = DataArray_PropertyName; }
public string DataArray
{
get { return my_DataArray; }
}
private string my_DataArray;
}
尽管是我自己创建的转换器,但我只能简单介绍一下内部功能。
在下半部分,我创建了自己的 Attribute
,它允许转换器指定数据源。转换器本身现在访问此属性的内容,并通过 PropertyDescriptor
将其转发到属性。同时,转换器会告知 PropertyGrid
应为输出选择一个组合框。
我可以将 StringArray
和 List(of String) 类型传递给转换器本身,在 VB.NET 中,还可以将集合类型作为数据源。
ShellyCom 和 ShellyCom2
这两个库在基本功能上没有区别。区别在于状态输出。
对于 ShellyCom
,有用于输入和输出状态的独立变量。对于 ShellyCom2
,我定义了与实际输入和输出大小相同的数组。
DRY – Don’t Repeat Yourself(不要重复自己)
由于组件的功能不同,会重复使用各种 ENums
。
我现在已将它们移至 Shelly
命名空间中的 Definitions
文件。这也会改变在组件中使用时的寻址方式。
最后 - 结束语
这便是我的迷你系列暂时告一段落。但是,我乐于接受建议或启发,也许最终还会有第 4 部分。
如果我使用额外的 Shelly 设备或获得它们的访问权限,我会相应地扩展/调整库。
我想再次感谢 @sean-ewington 在创作本文及前几篇文章中的帮助。