Keys missing as designer property of UC - vb.net

I'm starting to code a custom keyboard for my company, and I'm not sure why I don't have access to all keys in the designer. This seems like it should be very simple. One example key is Escape, but there are others.
New Project ->
right-click -> add new -> UserControl
UserControl1.vb
Public Class UserControl1
Private _myKey As Keys
Public Property MyKey() As Keys
Get
Return _myKey
End Get
Set(ByVal value As Keys)
_myKey = value
End Set
End Property
End Class
Build -> Form1 designer -> Drop UserControl1 onto Form1.vb
Form1.vb [Design]
->Select UserControl11
Properties -> Misc -> MyKey
try to set to Keys.Escape (or any number of others).
The available keys list is fairly verbose, but absolutely not complete.
What is the right way to do this? I would much prefer find a solution that allows me to do this via the designer than having to do this on load programmatically.
The below works, but again, I don't understand why I can't do this in the designer:
Public Class Form1
Public Sub New()
InitializeComponent()
UserControl11.MyKey = Keys.Escape
End Sub
End Class
I know I can define my own Enum and get it, but that's so much time for something that should probably just work with .net. What am I doing wrong here?

First off, great question with clear repro directions.
It's strange behavior for sure. Maybe someone has an explanation.
This workaround might do the trick though
Public Class UserControl1
Private _keyCode As Integer
Public Property KeyCode As Integer
Get
Return _keyCode
End Get
Set(ByVal value As Integer)
_keyCode = value
End Set
End Property
Public Property MyKey As Keys
Get
Return CType(_keyCode, Keys)
End Get
Set(ByVal value As Keys)
_keyCode = value
End Set
End Property
End Class
You can set KeyCode in the property pages to the integer value of Escape in System.Windows.Forms.Keys for instance, which is 27.
at which point, MyKey actually displays Escape. Not sure why it doesn't show up in the list.
The values can be found on MSDN: https://msdn.microsoft.com/en-us/library/system.windows.forms.keys(v=vs.71).aspx
Note: you can also just type Escape in MyKey. Adding KeyCode gives a nice other option though.

If you look at the System.Windows.Forms.Keys Enum in a decompiler, you will see that it is decorated with both a TypeConverter and Editor attribute. These attributes are controlling what you can do in the WinForms designer as they are inherited by your MyKey property.
<Flags, TypeConverter(GetType(KeysConverter)), Editor("System.Windows.Forms.Design.ShortcutKeysEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", GetType(UITypeEditor)), ComVisible(True)> _
Public Enum Keys
If you want to present the Keys Enum with the default Enum UI, you will need to decorate your property with its own TypeConverter and Editor attributes that tell the designer to use the default editor and the Enum type converter (this converter allows conversion between the Enum and String representation) instead of the ones declared on the Keys type.
<System.ComponentModel.TypeConverterAttribute(GetType(System.ComponentModel.EnumConverter))>
<System.ComponentModel.Editor(GetType(UITypeEditor), GetType(UITypeEditor))>
Public Property MyKey() As Keys

Related

Qualify Class Properties During/After Constructor (Sub New)

I'm looking for a way to get the value of a property during or after the New() sub. In general though I would like to know if there is a way to call some code automatically after a class has all of its properties fully initiated.
During the Sub New() properties are set to their initial values, rather than the ones that are set up at design time.
Essentially I'm wondering if it's possible to setup something similar to the "Form Shown" event, except for classes.
Code:
Public Class Test
inherits Component
Public Event Initiated()
Public Sub New()
MyBase.New()
RaiseEvent Initiated()
End Sub
Private Sub OnInitiated() Handles Me.Initiated
Debug.WriteLine(Max)
End Sub
Private _Max As Integer = 5
Public Property Max() As Integer
Get
Return _Max
End Get
Set(ByVal Value As Integer)
_Max = Value
End Set
End Property
End Class
Note: The value of the "Max" property is set to 3 in the design view.
The issue with using the constructor is that the designer code sets your properties well after it creates your object. But, the NET Framework includes the interface ISupportInitialize which is ideal for controls and components which need to do things such as qualify properties conditionally - for instance checking Value after Min and Max are set.
It is easy to use:
Imports System.ComponentModel
Public Class Test
Inherits Component
Implements ISupportInitialize
When you press enter on the last line, it will add 2 methods:
Public Sub BeginInit() Implements ISupportInitialize.BeginInit
Public Sub EndInit() Implements ISupportInitialize.EndInit
Which allows you to do this:
Public Sub New()
MyBase.New()
End Sub
Public Sub EndInit() Implements ISupportInitialize.EndInit
' do whatever you want to do
' all properties will be initialized at this time
' e.g. Max will be the IDE value, not 5
...
End Sub
The way it works is that VS will invoke this from the designer code after the control/component properties. If you open the designer code you will see something like this:
' ctl declarations
CType(Me.Test1, System.ComponentModel.ISupportInitialize).BeginInit()
' lots of code initializing controls
Me.Label1.Name = "Label1"
...
Me.Button1.Location = ...
...
Me.Test1.Max = 3 ' yours will be there somewhere
' then at the end:
CType(Me.Test1, System.ComponentModel.ISupportInitialize).EndInit()
So, you can add any code you need to run before anything is created in your BeginInit method, and code you need to run after all properties are initialized in EndInit.
BeginInit and EndInit will run every time the designer code is run. That is, every time at runtime and after there are enough changes to the form that it needs to be rebuilt. You do need to keep your component code fresh since VS is using a compiled version of it in the IDE when working on the project using it.
So, Rebuild often and Clean when it seems like it is not picking up changes.
The only thing I could see to answer your question is to set up a custom event in your class and fire it at the end of the constructor

Is there a keyword that can make a class variable readonly from outside the class but not on the inside?

Basically, the readonly keyword doesn't let me modify a field after I first create the class instance. I could use a property but in this case its just extra overhead. Is there a keyword to make a class field readonly from only outside the class?
make the field private, provide getter and setter for it.
Make the setter private.
This way the value can be seen from outside the class by the getter,but, cannot be set/written from outside the class.
this makes the property read-only from outside the class.
As others have stated, use a property. If you don't want to split the property into one Getter and one Setter then make the setter private.
Public Class Foo
Public Property Abc() As Object
Get
Return Me.m_Abc
End Get
Private Set(value As Object)
Me.m_Abc = value
End Set
End Property
Private m_Abc As Object
End Class
However: The common way is to set the access level of the field to Friend making it accessible within the same assembly, but not from outside the assembly.
Public Class Foo
Public ReadOnly Property Abc() As Object
Get
Return Me.m_Abc
End Get
End Property
Friend m_Abc As Object
End Class
No there isn't. This type is scenario is precisely why properties are provided in the first place. You get a whole lot of flexibility.
However, if you insist you want to use a read only field, you can use reflection to change the value:-
Public Class TestClass
Public ReadOnly MyNumber As Integer
Public Sub New()
'Readonly fields can only be changed this way
'in the constructor
Me.MyNumber = 900
End Sub
Public Sub ChangeNumber(ByVal num As Integer)
SetNumber(num)
End Sub
Private Sub SetNumber(ByVal num As Integer)
Dim fi = Me.GetType.GetField("MyNumber")
'Reflection can change the value of
'a read only field after construction
fi.SetValue(Me, num)
End Sub
End Class
Note that this is a very terrible thing. Reflection shouldn't be used for this sort of thing as you're going to take a performance hit. Just use properties and save yourself the trouble.

Property of type dictionary

Is there a way to declare a property of type dictionary of string, string in VB.Net.
I am using this on a usercontrol to add properties via the designer.
I tried the following:
Private v As Dictionary(Of String, String)
Public Property VList As Dictionary(Of String, String)
Get
Return v
End Get
Set(ByVal value As Dictionary(Of String, String))
v = value
End Set
End Property
But when I try this the string collection editor window opens up but the add & remove buttons are disabled. What is the correct way to declare this property?
I want to add the key & value via the designer.
The Dictionary does not have a built in UITypeEditor. There are many reasons why there isn't: there are 2 Types which are generic, it also doesnt have an Item accessor, there is no simple Add method, the key must be unique and there is no built in way to serialize a Dictionary "item".
The right way is to use a Collection class inheriting from Collection<T> so you can control access to the contents (note: this is from System.Collections.ObjectModel not the horrible VB Collection!). The fast way to setup a working interface is to use a List(Of myTypeClass), but this is dangerous in production code because it allows all sorts of actions on the innerlist which you likely do not want.
<Serializable><TypeConverter(GetType(FooConverter))>
Public Class FooBar
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
Public Property Name As String
<DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)>
Public Property Value As String
' simple ctor REQUIRED for the UITypeEditor
Public Sub New()
Name = ""
Value = ""
End Sub
' ctor for the TypeConverter (NOT included)
Public Sub New(n As String, v As String)
Name = n
Value = v
End Sub
Public Overrides Function ToString
Return Name
End Sub
End Class
' must be instanced
Private myFoo As New List(Of FooBar)
' list is an object so it cant be serialized, but the CONTENTS can be
<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>
Public Property FooList As List(of FooBar)
Get
If myFoo Is Nothing Then
myFoo = New List(of FooBar)
End If
Return myFoo
End Get
Set
' do nothing
End Set
End Sub
' for designer serialization
Private Function ShouldSerializeFooList As Boolean
Return myFoo.Count > 0 ' or myFoo IsNot Nothing
End Sub
public Sub ResetMyFolist
myFoo = New List(of FooBar)
End Sub
Caveats:
It is almost always better to write a class container for the Foobar items. Usually you would inherit from Collection<T>. List<T> as shown is a container and a collection, so the contents can be cleared, reset, modified etc when exposed as shown. They are fast and easy to implement though and the basic concept is the same.
If a Dictionary is really what you want, you can write your own UITypeEditor (not UIDesigner, this is not a control) but this would probably require a great deal of work on many levels. The reason there are not gobs of them flying around is that most people make do with one of the standard collections and simply enforce unique names in other ways. (Adding "Properties" to a usercontrol, suggests that really the key or name ought to be fixed and known to the app ahead of time so it knows what it is and what to do with it(?)).
Often VS can perform designer serialization on its own with simple properties like those in FooBar. However, since they are items in a collection, you will likely need to also write a TypeConverter which can return an InstanceDescriptor, to help VS instance them. But that is a different question.

VB.Net Create Database table from class property

I'm trying to create an inheritable class(OF t) in vb.net that I will pass it a class of objects. Inside the class of objects I want to use the class properties to create a corresponding database table. Like below
Public Class SampleClass
#Region "Properties"
Private newPropertyValue As String
Public Property NewProperty() As String
Get
Return newPropertyValue
End Get
Set(ByVal value As String)
newPropertyValue = value
End Set
End Property
#End Region
Public Sub New()
End Sub
End Class
I'm new to vb.net so I don't know my way around exactly.
I was looking into class attributes for this action but they do not fully make sense to me yet. Thanks in advance.
You will want to get well versed on something called Code First. This should get you started.

.net dynamic loading

I've seen some other responses about this and they talk about interfaces but I'm pretty sure you can do this with classes and base classes but I can't this to work.
Public Class Behavior
Private _name As String
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
Public Property EditorUpdate As Boolean
Public Sub New(ByVal name As String)
_name = name
EditorUpdate = False
End Sub
Public Overridable Sub Update()
End Sub
' runs right away in editor mode. also runs when in stand alone game mode right away
Public Overridable Sub Start()
End Sub
' runs after game mode is done and right before back in editor mode
Public Overridable Sub Finish()
End Sub
' runs right when put into game mode
Public Overridable Sub Initialize()
End Sub
' runs when the game is complete in stand alone mode to clean up
Public Overridable Sub Destroy()
End Sub
End Class
Public Class CharacterController
Inherits Behavior.Behavior
Public Sub New()
MyBase.New("Character Controller")
End Sub
Public Overrides Sub Update()
' TODO: call UpdateController()
' THINK: how can UpdateController() get the controller entity it's attached to?
' Behaviors need a way to get the entity they are attached to. Have that set when it's assigned in the ctor?
End Sub
End Class
Dim plugins() As String
Dim asm As Assembly
plugins = Directory.GetFileSystemEntries(Path.Combine(Application.StartupPath, "Plugins"), "*.dll")
For i As Integer = 0 To plugins.Length - 1
asm = Assembly.LoadFrom(plugins(i))
For Each t As Type In asm.GetTypes
If t.IsPublic Then
If t.BaseType.Name = "Behavior" Then
behaviorTypes.Add(t.Name, t)
Dim b As Behavior.Behavior
b = CType(Activator.CreateInstance(t), Behavior.Behavior)
'Dim o As Object = Activator.CreateInstance(t)
End If
End If
Next
Next
When it tries to convert whatever Activator.CreateInstance(t) returns to the base class of type Behavior I'm getting invalid cast exception. That type should be of CharacterController which is defined as a child of Behavior so why wouldn't it let me cast that? I've done something like this before but I can't find my code. What am I missing?
This may not be an answer to your question (it also might resolve your exception -- who knows), but it is something that needs to be pointed out. These lines:
If t.IsPublic Then
If t.BaseType.Name = "Behavior" Then
Should really be changed to one conditional like this one:
If t.IsPublic AndAlso (Not t.IsAbstract) AndAlso _
GetType(Behavior.Behavior).IsAssignableFrom(t) Then
Otherwise, if somebody defines a random type called "Behavior" in their own assembly and derives it from another type, your code will think it is a plugin. Additionally, if someone derives your Behavior type and then derives that type (two levels of inheritance) this code will incorrectly skip over that type. Using the IsAssignableFrom method is a quick and easy way to ensure that one type does actually derive from the specific type you want (instead of any type that shares the same name), even if there is another type in between your types in the inheritance tree. The additional check against t.IsAbstract will also ensure that you don't try to instantiate an abstract subtype of your base plugin type.
This works for me:
Dim ctor As Reflection.ConstructorInfo = _
t.GetConstructor(New System.Type() {})
Dim o As Object = ctor.Invoke(New Object() {})
Dim plugin As Plugin = TryCast(o, Plugin)
(If I find t, I invoke the parameterless constructor.)
[I just realized this is probably what Activator.CreateInstance does, so I replaced my code with yours and it worked your way -- so this probably won't help you]