How to Convert this generic method from C# to VB.Net - c#

I have the following code block in C#
private void Synchronize<T>(TextSelection selection, DependencyProperty property, Action<T> methodToCall)
{
object value = selection. GetPropertyValue(property) ;
if ( value != DependencyProperty. UnsetValue) methodToCall((T) value) ;
}
That I have converted to VB.
Private Sub Synchronize(Of T)(ByVal selection As TextSelection, ByVal [property] As DependencyProperty, ByVal methodToCall As Action(Of T))
Dim value As Object = selection.GetPropertyValue([property])
If value IsNot DependencyProperty.UnsetValue Then
methodToCall(DirectCast(value, T))
End If
End Sub
The calling method look like:
Synchronize(Of Double)(selection, TextBlock.FontSizeProperty, AddressOf SetFontSize)
Synchronize(Of FontWeight)(selection, TextBlock.FontSizeProperty, AddressOf SetFontWeight)
Synchronize(Of FontStyle)(selection, TextBlock.FontStyleProperty, AddressOf SetFontStyle)
Synchronize(Of FontFamily)(selection, TextBlock.FontFamilyProperty, AddressOf SetFontFamily)
Synchronize(Of TextDecorationCollection)(selection, TextBlock.TextDecorationsProperty, AddressOf SetTextDecoration)
My problem is with the DirectCast call; if my delegate argument can be a simple type (integer, double, etc) or an object. DirectCast doesn't like the simple data types an InvalidCastException is thrown when I try to cast to a double. Does anyone have a suggested solution to this problem? I've also tried TryCast, but it doesn't like my (of T) and says it must be class contstrained.
Thanks all!
Ryan

Try CType() instead of DirectCast().

Looking at the errors you're seeing, it sounds like you may need to add a constraint on your T handle limit it to a class in order for the TryCast to be used.
I'm not really familiar with VB's TryCast method (or DirectCast, for that matter), but something like this might help [note that (Of T) -> (Of T as Class) ]:
Private Sub Synchronize(Of T as Class)(ByVal selection As TextSelection, ByVal [property] As DependencyProperty, ByVal methodToCall As Action(Of T))
Dim value As Object = selection.GetPropertyValue([property])
If value IsNot DependencyProperty.UnsetValue Then
methodToCall(TryCast(value, T))
End If
End Sub

Use CType instead of TryCast/DirectCast. It should work like the casting in C# :
methodToCall(CType(value, T))

You can put multiple constraints on t
I believe the syntax is something like:
Private Sub Synchronize(Of T as {Class, Integer, Double})

Related

Linq Select compiling differently between C# and VB

I have the following C# code which compiles correctly:
private string formatterCSharp(int number)
{
return "n" + number;
}
private void testInCSharp()
{
IEnumerable<int> list = new List<int>();
IEnumerable<string> formatted = list.Select(formatterCSharp);
}
As you can see, formatted should contain the contents of list, with formatterCSharp applied to each.
When I try to replicate this code in VB, I come up with this:
Private Function formatterVisualBasic(ByVal number As Integer) As String
Return "n" + number
End Function
Private Sub testInVB()
Dim list As IEnumerable(Of Integer) = New List(Of Integer)
Dim formatted As IEnumerable(Of String) = list.Select(formatterVisualBasic)
End Sub
However I get two compilation errors on the Select statement in VB.
BC30455
Argument not specified for parameter 'number' of 'Private Function formatterVisualBasic(number As Integer) As String'.
BC30518
Overload resolution failed because no accessible '[Select]' can be called with these arguments:
Extension method 'Public Function [Select](Of TResult)(selector As Func(Of Integer, TResult)) As IEnumerable(Of TResult)' defined in 'Enumerable': Type parameter 'TResult' cannot be inferred.
Extension method 'Public Function [Select](Of TResult)(selector As Func(Of Integer, Integer, TResult)) As IEnumerable(Of TResult)' defined in 'Enumerable': Type parameter 'TResult' cannot be inferred.
Try
Dim formatted As IEnumerable(Of String) = list.Select(AddressOf formatterVisualBasic)
In VB.net you can't specify function name to pass it like that.
https://msdn.microsoft.com/en-us/library/y72ewk2b.aspx
Okay first off the select query is formed differently in VB.NET, here's how it should look:
Dim formatted As IEnumerable(Of String) = list.Select(Function(x As String) x = formatterVisualBasic(mynumber))
Secondly, if you want to get the same exception as in your C# code, you must add parentheses to the function call formatterVisualBasic, so that it is recognized as a function
Dim formatted As IEnumerable(Of String) = list.Select(Function(x As String) x = formatterVisualBasic())

Need a VB.net Combobox derived class for pattern matched or contains autocomplete functionality [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
Ive searched everywhere on the net for a solution to this common pain. Surely someone out there has a solution or can help me implement one ????
Basically the default combobox in vb.net autocompletes using a limited "StartsWith" pattern match (for example, when you type any letter, only the listitems.text that start with that letter will be filtered and shown in the dropdownlist.
There isnt an obviously simple switch to change the way these are filtered and so I assume that the best way to solve this issue is to derive/inherit the combobox class and modify it to use String.contains() ?? Only issue i have never done a derived class and would really appreciate some help with this specific problem.
Also, I found a C# solution that seems to exactly solve this issue (http://www.codeproject.com/Tips/631196/ComboBox-with-Suggest-Ability-based-on-Substring-S) and ive tried everything to convert or code something similar with little success :(
Im amazed this is still unsolved, since so many people on the net have asked for a solution to this limitation !!!!
Yes it can be done in VB.NET. I was on the same thought process of that you could just try converting the code in the link you posted from C# to VB.NET as the previous comments suggested. However when I tried doing so the resulting VB wouldn't compile. After fixing numerous build errors and some syntax errors I present to you the VB version of the SuggestComboBox and how it is used. The only reason why I'm posting this answer is because the conversion from C# to VB wasn't straightforward, and the license the source is released under allows me to do so.
Source
License
You need to add the control via the code behind of the form, I couldn't find a designer portion in the source (blame my lacking knowledge of C# on that).
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.Linq
Imports System.Linq.Expressions
Imports System.Windows.Forms
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim testbox As New AutoCompleteComboBox.SuggestComboBox
Me.Controls.Add(testbox)
testbox.DataSource = New List(Of String) From {"Janean Mcgaha", "Tama Gaitan", "Jacque Tinnin", "Elvira Woolfolk", "Fransisca Owens", "Minnie Ardoin", _
"Renay Bentler", "Joye Boyter", "Jaime Flannery", "Maryland Arai", "Walton Edelstein", "Nereida Storrs", _
"Theron Zinn", "Katharyn Estrella", "Alline Dubin", "Edra Bhatti", "Willa Jeppson", "Chelsea Revel", _
"Sonya Lowy", "Danelle Kapoor"}
End Sub
End Class
Namespace AutoCompleteComboBox
Public Class SuggestComboBox
Inherits ComboBox
#Region "fields and properties"
Private ReadOnly _suggLb As New ListBox() With {.Visible = False, .TabStop = False}
Private ReadOnly _suggBindingList As New BindingList(Of String)()
Private _propertySelector As Expression(Of Func(Of ObjectCollection, IEnumerable(Of String)))
Private _propertySelectorCompiled As Func(Of ObjectCollection, IEnumerable(Of String))
Private _filterRule As Expression(Of Func(Of String, String, Boolean))
Private _filterRuleCompiled As Func(Of String, Boolean)
Private _suggestListOrderRule As Expression(Of Func(Of String, String))
Private _suggestListOrderRuleCompiled As Func(Of String, String)
Public Property SuggestBoxHeight() As Integer
Get
Return _suggLb.Height
End Get
Set(value As Integer)
If value > 0 Then
_suggLb.Height = value
End If
End Set
End Property
''' <summary>
''' If the item-type of the ComboBox is not string,
''' you can set here which property should be used
''' </summary>
Public Property PropertySelector() As Expression(Of Func(Of ObjectCollection, IEnumerable(Of String)))
Get
Return _propertySelector
End Get
Set(value As Expression(Of Func(Of ObjectCollection, IEnumerable(Of String))))
If value Is Nothing Then
Return
End If
_propertySelector = value
_propertySelectorCompiled = value.Compile()
End Set
End Property
'''<summary>
''' Lambda-Expression to determine the suggested items
''' (as Expression here because simple lamda (func) is not serializable)
''' <para>default: case-insensitive contains search</para>
''' <para>1st string: list item</para>
''' <para>2nd string: typed text</para>
'''</summary>
Public Property FilterRule() As Expression(Of Func(Of String, String, Boolean))
Get
Return _filterRule
End Get
Set(value As Expression(Of Func(Of String, String, Boolean)))
If value Is Nothing Then
Return
End If
_filterRule = value
_filterRuleCompiled = Function(item) value.Compile()(item, Text)
End Set
End Property
'''<summary>
''' Lambda-Expression to order the suggested items
''' (as Expression here because simple lamda (func) is not serializable)
''' <para>default: alphabetic ordering</para>
'''</summary>
Public Property SuggestListOrderRule() As Expression(Of Func(Of String, String))
Get
Return _suggestListOrderRule
End Get
Set(value As Expression(Of Func(Of String, String)))
If value Is Nothing Then
Return
End If
_suggestListOrderRule = value
_suggestListOrderRuleCompiled = value.Compile()
End Set
End Property
#End Region
''' <summary>
''' ctor
''' </summary>
Public Sub New()
' set the standard rules:
_filterRuleCompiled = Function(s) s.ToLower().Contains(Text.Trim().ToLower())
_suggestListOrderRuleCompiled = Function(s) s
_propertySelectorCompiled = Function(collection) collection.Cast(Of String)()
_suggLb.DataSource = _suggBindingList
AddHandler _suggLb.Click, AddressOf SuggLbOnClick
AddHandler ParentChanged, AddressOf OnParentChanged
End Sub
''' <summary>
''' the magic happens here ;-)
''' </summary>
''' <param name="e"></param>
Protected Overrides Sub OnTextChanged(e As EventArgs)
MyBase.OnTextChanged(e)
If Not Focused Then
Return
End If
_suggBindingList.Clear()
_suggBindingList.RaiseListChangedEvents = False
_propertySelectorCompiled(Items).Where(_filterRuleCompiled).OrderBy(_suggestListOrderRuleCompiled).ToList().ForEach(AddressOf _suggBindingList.Add)
_suggBindingList.RaiseListChangedEvents = True
_suggBindingList.ResetBindings()
_suggLb.Visible = _suggBindingList.Any()
If _suggBindingList.Count = 1 AndAlso _suggBindingList.[Single]().Length = Text.Trim().Length Then
Text = _suggBindingList.[Single]()
[Select](0, Text.Length)
_suggLb.Visible = False
End If
End Sub
#Region "size and position of suggest box"
''' <summary>
''' suggest-ListBox is added to parent control
''' (in ctor parent isn't already assigned)
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Overloads Sub OnParentChanged(sender As Object, e As EventArgs)
Parent.Controls.Add(_suggLb)
Parent.Controls.SetChildIndex(_suggLb, 0)
_suggLb.Top = Top + Height - 3
_suggLb.Left = Left + 3
_suggLb.Width = Width - 20
_suggLb.Font = New Font("Segoe UI", 9)
End Sub
Protected Overrides Sub OnLocationChanged(e As EventArgs)
MyBase.OnLocationChanged(e)
_suggLb.Top = Top + Height - 3
_suggLb.Left = Left + 3
End Sub
Protected Overrides Sub OnSizeChanged(e As EventArgs)
MyBase.OnSizeChanged(e)
_suggLb.Width = Width - 20
End Sub
#End Region
#Region "visibility of suggest box"
Protected Overrides Sub OnLostFocus(e As EventArgs)
' _suggLb can only getting focused by clicking (because TabStop is off)
' --> click-eventhandler 'SuggLbOnClick' is called
If Not _suggLb.Focused Then
HideSuggBox()
End If
MyBase.OnLostFocus(e)
End Sub
Private Sub SuggLbOnClick(sender As Object, eventArgs As EventArgs)
Text = _suggLb.Text
Focus()
End Sub
Private Sub HideSuggBox()
_suggLb.Visible = False
End Sub
Protected Overrides Sub OnDropDown(e As EventArgs)
HideSuggBox()
MyBase.OnDropDown(e)
End Sub
#End Region
#Region "keystroke events"
''' <summary>
''' if the suggest-ListBox is visible some keystrokes
''' should behave in a custom way
''' </summary>
''' <param name="e"></param>
Protected Overrides Sub OnPreviewKeyDown(e As PreviewKeyDownEventArgs)
If Not _suggLb.Visible Then
MyBase.OnPreviewKeyDown(e)
Return
End If
Select Case e.KeyCode
Case Keys.Down
If _suggLb.SelectedIndex < _suggBindingList.Count - 1 Then
_suggLb.SelectedIndex += 1
End If
Return
Case Keys.Up
If _suggLb.SelectedIndex > 0 Then
_suggLb.SelectedIndex -= 1
End If
Return
Case Keys.Enter
Text = _suggLb.Text
[Select](0, Text.Length)
_suggLb.Visible = False
Return
Case Keys.Escape
HideSuggBox()
Return
End Select
MyBase.OnPreviewKeyDown(e)
End Sub
Private Shared ReadOnly KeysToHandle As List(Of Keys) = New List(Of Keys) From {Keys.Down, Keys.Up, Keys.Enter, Keys.Escape}
Protected Overrides Function ProcessCmdKey(ByRef msg As Message, keyData As Keys) As Boolean
' the keysstrokes of our interest should not be processed be base class:
If _suggLb.Visible AndAlso KeysToHandle.Contains(keyData) Then
Return True
End If
Return MyBase.ProcessCmdKey(msg, keyData)
End Function
#End Region
End Class
End Namespace

creating a class that holds generic delegates

I'm trying to create a class thats holds two (or more) generic delegates as properties. This way I can pass the object to a method and use only one parameter on that method. The problem is that I get a warning to specify a type, but I don't know the type yet. The whole point is to defer type declaration until the object gets instantiated.
Here's some dummy code dummy code to show what I want to do.
Public Function Method1()
Dim _container as Container = new Container()
_container.Property1 = //Here create delegate with type string
_container.Property2 = //Here create delegate with type integer
Method3(_container)
End Function
Public Function Method2()
Dim _container as Container = new Container()
_container.Property1 = //Here create delegate with type Integer
_container.Property2 = //Here create delegate with type integer
Method3(_container)
End Function
Public Function Method3(container as Container)
//execute type specific code and the delegates
//Throw exception when type is not supported (yet)
end Function
public Class Container
Property Property1 as MyDel(of T)
Property Property2 as MyDel(of T)
end Class
Public delegate function Mydel(of T)()
That's it. The point of this being that when adding new information/functionality to the system it is easy to create a new method called Method4, which uses arguments as needed (and of course create the method that will be executed as delegate).
I would like to put two delegates in one class because their is a connection between them and things belonging together should be put together in one class; just like a Person class can hold a name and address.
This code doesn't work, because Container needs a Type T, which means Method3 needs a specific type, but i can't do that because then I can't call Method3 with different arguments as I showed. I tried using a wrapper class, but a type specification is needed all the time.
I know you can do this
Public Property TestProp() As [Delegate]
Get
End Get
Set(ByVal Value As [Delegate])
End Set
End Property
[source:http://www.xtremedotnettalk.com/showthread.php?t=96800]
But that is not generic. What am I missing?
BTW, this is an optimization for code calling Method3 with two arguments. That seemed to work fine. Then I thought trying to use only one argument, which would make the system easier to understand.
My old code looks something like this:
Public function OldMethod1()
Dim del1 as Mydel(of Integer) = AddressOf SomeMethod
Dim del2 as MyDel(of String) = AddressOf SomeOtherMethod
oldMethod3(del1, del2)
end Function
Public function OldMethod2()
Dim del1 as Mydel(of String) = AddressOf AnOtherMethod
Dim del2 as MyDel(of String) = AddressOf AgainSomeOtherMethod
oldMethod4(del1, del2)
end Function
Public Function oldMethod3(del1 as Mydel(of Integer), del2 as Mydel(of string))
//execute delegates
end Function
Public Function oldMethod4(del1 as Mydel(of string), del2 as Mydel(of string))
//execute delegates
end Function
When reading this code I saw that it is only the type of the parameter the determines the execution flow. So if you can determine the subtype of the generic you know what to do. That seemed to be possible (use typeOf and or GetType), so the next thing to do is to create a generic parameter object that could be a substitute for 'del1' (I now realize I took it even took one step further and created the Container class to hold both the parameters).
Any answer in C# or VB.net will do. I use both.
The question is a little confusing, so this may not be an answer, but it's too long for a comment! I think what you want to do is use Action(Of T) as the type for Container.Property1 and Container.Property2.
Something like this (this will run in LINQPad using Language: VB Program):
Sub Main
Dim c1 = New Container(Of String, String)()
c1.Property1 = AddressOf Method1
c1.Property2 = AddressOf Method1
Method3(c1)
Dim c2 = New Container(Of String, Integer)()
c2.Property1 = AddressOf Method1
c2.Property2 = AddressOf Method2
Method3(c2)
End Sub
Public Sub Method1(x As String)
Console.WriteLine("Method1: {0}", x)
End Sub
Public Sub Method2(x As Integer)
Console.WriteLine("Method2: {0}", x)
End Sub
Public Sub Method3(Of T1, T2)(container as Container(Of T1, T2))
Console.WriteLine("Method3 got {0}, {1}", GetType(T1), GetType(T2))
' Not sure how you would actually call the delegates
If GetType(T1) = GetType(String) Then
container.Property1.DynamicInvoke("Hello")
ElseIf GetType(T1) = GetType(Integer) Then
container.Property1.DynamicInvoke(123)
End If
If GetType(T2) = GetType(String) Then
container.Property2.DynamicInvoke("World")
ElseIf GetType(T2) = GetType(Integer) Then
container.Property2.DynamicInvoke(456)
End If
End Sub
Public Class Container(Of T1, T2)
Public Property Property1 As Action(Of T1)
Public Property Property2 As Action(Of T2)
End Class
This produces the following results:
Method3 got System.String, System.String
Method1: Hello
Method1: World
Method3 got System.String, System.Int32
Method1: Hello
Method2: 456

Optional Parameters with default values in VB6

I am trying to recreate the following C# code in VB6:
private void ChangeTab(string tabName, bool clearAll = true)
{
Yadyyada(tabName);
if (clearAll)
{
DoMoreStuff();
}
}
Here is what I have so far:
Private Sub ChangeTab(ByVal tabName As String, Optional ByVal clearAll As Boolean)
Yadyyada(tabName)
If clearAll = True Then
DoMoreStuff
End If
End Sub
So far so good apart from the default parameter. Can I assign clearAll a default value of true in the method signature in the same way I can in C# or do I just need to do this at the start of the method?
Thanks
Wow this takes me back.. can I ask why you're converting backwards technology-wise?
Anyway, you can use the Optional keyword:
Private Sub ChangeTab(ByVal tabName As String, Optional ByVal clearAll As Boolean = True)
Your issue is using ByVal. From memory, everything in VB6 was ByVal unless explicitly stated.
EDIT: I'm wrong. Default was ByRef.. it's been so long!
Yes, you could do the same thing as in C#
Private Sub ChangeTab(ByVal tabName As String, Optional ByVal clearAll As Boolean = True)
Debug.Print "Value for clearAll=" & clearAll
End Sub
calling with
ChangeTab("AName")
will print True
Try:
Private Sub ChangeTab(ByVal tabName As String, Optional clearAll As Boolean = True)
Call Yadyyada(tabName)
If clearAll Then
DoMoreStuff
End If
End Sub
See http://msdn.microsoft.com/en-us/library/aa266305%28v=vs.60%29.aspx
You can use IsMissing Function like this
Private Sub ChangeTab(ByVal tabName As String, Optional ByVal clearAll As Boolean)
Yadyyada(tabName)
If IsMissing(clearAll) = True Or clearAll = True Then
DoMoreStuff
End If
End Sub
My Mistake! Setting a default true value for optional parameter and check for this in code is the best solution!
Private Sub ChangeTab(ByVal tabName As String, Optional ByVal clearAll As Boolean = True)
Yadyyada(tabName)
If clearAll = True Then
DoMoreStuff
End If
End Sub

What is the VB equivalent of this C# syntax, dealing with delegates?

Is it possible to translate the following C# code into VB.NET, using VB 9.0?
delegate Stream StreamOpenerDelegate(String name);
void Exec1()
{
WorkMethod( x => File.OpenRead(x));
}
void Exec2()
{
StreamOpenerDelegate opener = x => return File.OpenRead(x) ;
WorkMethod(opener);
}
Can I do something like this?:
Private Delegate Function StreamOpenerDelegate(ByVal name As String) As Stream
Private Sub WorkMethod(ByVal d As StreamOpenerDelegate)
''
End Sub
Private Sub Exec1()
Me.WorkMethod(Function (ByVal x As String)
Return File.OpenRead(x)
End Function)
End Sub
Private Sub Exec2()
Dim opener As StreamOpenerDelegate = Function (ByVal x As String)
Return File.OpenRead(x)
End Function
Me.WorkMethod(opener)
End Sub
I'm trying to write some documentation, but I don't know VB syntax. Often I use Reflector to translate it, but I'm not sure it's working in this case. I'm also not clear on where I would need line continuation characters.
ANSWER
In VB9, it's not possible to have multi-line lambdas (or Sub lambdas, which I did not ask about). In VB9, all lambdas return a value, and must be a single expression. This changes in VB10. VB10 will allow the above syntax, but VB9 will not. In VB9, if the logic involves multiple code lines, it must not be a lambda; you must put it into a named Function and reference it explicitly. Like this:
Private Delegate Function StreamOpenerDelegate(ByVal name As String) As Stream
Private Sub WorkMethod(ByVal d As StreamOpenerDelegate)
''
End Sub
Function MyStreamOpener(ByVal entryName As String) As Stream
'' possibly multiple lines here
Return File.OpenRead(entryName)
End Function
Private Sub Exec1()
Me.WorkMethod(AddressOf MyStreamOpener)
End Sub
site: Mike McIntyre's blog
This should work:
Private Sub Exec1()
Me.WorkMethod(Function (x) File.OpenRead(x))
End Sub
Private Sub Exec2()
Dim opener As StreamOpenerDelegate = Function (x) File.OpenRead(x)
Me.WorkMethod(opener)
End Sub
You need the line continuation character to split a single line statement into multiple lines, like so:
Private Sub Exec1()
Me.WorkMethod(Function (x) _
File.OpenRead(x))
End Sub
Private Sub Exec2()
Dim opener As StreamOpenerDelegate = Function (x) _
File.OpenRead(x)
Me.WorkMethod(opener)
End Sub
In any case, in VS2010 there is implicit line continuation after certain characters. So I wouldn't worry about it too much.

Categories

Resources