chosing keyboard layout using vb.net code - vb.net

I have installed Phoenetic Key board for Urdu language, that I can select from Control Panel > Languages > Keyboard
Can I add languages and select keyboard using my vb.net code?
Thanks

The InputLanguage.CurrentInputLanguage property lets you switch keyboard layouts. Not so sure it can deal with multiple layouts for a single language but I don't really know what "Phoenetic Key board" really means. The underlying Windows api functions are LoadKeyboardLayout() and ActivateKeyboardLayout(), you could pinvoke them. GetKeyboardLayoutList() to get a list of installed layouts, you probably need that, GetKeyboardLayoutName() to get a description of a layout. Also covered by the InputLanguage class.
You normally leave it up to the user to select keyboard layouts, forcing your preference is pretty hostile to usability. Easy to do with the language bar.

You can change your application Input language this way:
InputLanguage.CurrentInputLanguage = InputLanguage.FromCulture(New System.Globalization.CultureInfo("ZH-CN"))
but if you don't have the required InputLanguage Installed you can install your cultures input language from code temporary by using windows api:
<DllImport("user32.dll")> _
Private Shared Function UnloadKeyboardLayout(hkl As IntPtr) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function LoadKeyboardLayout(pwszKLID As String, Flags As UInteger) As IntPtr
End Function
Public Class KeyboardHolder
Implements IDisposable
Private ReadOnly pointer As IntPtr
Public Sub New(klid As Integer)
pointer = LoadKeyboardLayout(klid.ToString("X8"), 1)
End Sub
Public Sub New(culture As CultureInfo)
Me.New(culture.KeyboardLayoutId)
End Sub
Public Sub Dispose()
UnloadKeyboardLayout(pointer)
GC.SuppressFinalize(Me)
End Sub
Protected Overrides Sub Finalize()
Try
UnloadKeyboardLayout(pointer)
Finally
MyBase.Finalize()
End Try
End Sub
End Class
and use it this way:
' install keyboard layout temporary
Dim keyboard As New KeyboardHolder(New System.Globalization.CultureInfo("ZH-CN"))
' after finishing what you want remove temporary added keyboard layout:
keyboard.Dispose()

Related

How to find the window MS Word in order to sub-class and override WndProc

I need to use custom event processing for ebedded Office applications in a WebBrowser ActiveX control. I have made great progress using the WebBrowser as a container for Word, Excel and PowerPoint and so far with some work I'm able to get almost all the behavior I require. However, the one thing I need to be able to do is to detect mouse clicks in a document and differentiate those that are on hyperlinks from those that are not.
I've been able to get close to a solution using a global hook but the process/logic I use for Word doesn't work for Excel.
In any case, after reading this I decided overriding WndProc might be a better approach. Using the following code I put together a NativeWindow sub-class and an override for the WndProc. Unfortunately, it is never called so I assume I'm not using the correct window handle to instantiate it.
I use the class like this:
wordDocument.Application.ActiveDocument.ActiveWindow.SetFocus()
hWndOffice = GetFocus()
officeWindow = New OfficeWindow(hWndOffice)
I'm using VB .NET but not VSTO...
Any ideas how to get the correct window handle?
Friend Class OfficeWindow
Inherits NativeWindow
Implements IDisposable
Public Sub New(handle As IntPtr)
Me.AssignHandle(handle)
End Sub
<System.Security.Permissions.PermissionSetAttribute(System.Security.Permissions.SecurityAction.Demand, Name:="FullTrust")> _
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Console.WriteLine(m.Msg)
Select Case m.Msg
Case &H200
Console.WriteLine("WM_MOUSEMOVE")
End Select
MyBase.WndProc(m)
End Sub
' Flag: Has Dispose already been called?
Dim disposed As Boolean = False
' Public implementation of Dispose pattern callable by consumers.
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
' Protected implementation of Dispose pattern.
Protected Overridable Sub Dispose(disposing As Boolean)
If disposed Then Return
If disposing Then
' Free any other managed objects here.
'
Me.DestroyHandle()
End If
' Free any unmanaged objects here.
'
disposed = True
End Sub
Protected Overrides Sub Finalize()
Dispose(False)
End Sub
End Class

How can I catch the autosize double-click event on a listview in VB.NET?

I am using Visual Studio 2008 and VB.NET. I've got a listview control on my form and I've added columns using the windows forms designer. As you know, if you double-click on the sizer or divider or whatever you want to call it between two columns, the column on the left will autosize (unless you disable that). How can I catch this specific event? The ColumnWidthChanged event and the DoubleClick event are likely candidates, but in the ColumnWidthChanged event, there's no way I can see to determine if it was an autosize. Similarly, there's no simple way to catch what was clicked exactly with the DoubleClick event. Does anyone have any ideas how I can catch this specific event type?
Detecting events on a listview's header is quite tricky.
You will need to create your own header to replace the one that it normally uses, and then listen to the appropriate messages. There aren't any specific ones for column resize handles, as far as I know.
The following class subclasses ListView and adds a handler that detects a double-click between columns. That is as close as it gets, I think.
I hope it will help you out somewhat.
Class MyListView
Inherits ListView
Protected Overrides Sub CreateHandle()
MyBase.CreateHandle()
New HeaderControl(Me)
End Sub
Private Class HeaderControl
Inherits NativeWindow
Private _parent As ListView = Nothing
<DllImport("User32.dll", CharSet := CharSet.Auto, SetLastError := True)> _
Public Shared Function SendMessage(hWnd As IntPtr, msg As Integer, wParam As IntPtr, lParam As IntPtr) As IntPtr
End Function
Public Sub New(parent As ListView)
_parent = parent
Dim header As IntPtr = SendMessage(parent.Handle, (&H1000 + 31), IntPtr.Zero, IntPtr.Zero)
Me.AssignHandle(header)
End Sub
Protected Overrides Sub WndProc(ByRef message As Message)
Const WM_LBUTTONDBLCLK As Integer = &H203
Select Case message.Msg
Case WM_LBUTTONDBLCLK
Dim position As Point = Control.MousePosition
Dim relative As Point = _parent.PointToClient(position)
Dim rightBorder As Integer = 0
For Each c As ColumnHeader In _parent.Columns
rightBorder += c.Width
If relative.X > (rightBorder - 6) AndAlso relative.X < (rightBorder + 6) Then
MessageBox.Show([String].Format("Double-click after column '{0}'", c.Text))
End If
Next
Exit Select
End Select
MyBase.WndProc(message)
End Sub
End Class
End Class
You will need to include a using System.Runtime.InteropServices; statement for this to work.

Managed method for SetParent() on form

How can I show a form as a child of a window that isn't in my program?
I have a window handle to what should be the parent, but I don't see any managed method for SetParent() on a form. Is there one? It also seems that the form.Show() method only accepts managed objects implementing IWin32Window.
If there isn't a managed method, what is the preferred method for declaring the API for maximum compatibility with future systems? Like this?:
<DllImport("user32.dll")> _
Private Shared Function SetParent(hWndChild As IntPtr, hWndNewParent As IntPtr) As IntPtr
End Function
Is it possible to build a class that implements IWin32Window and somehow wraps up a window? It would be handy do something like this, but I am not familiar with IWin32Window:
frmMyForm.Show(New NativeWindowWrapper(12345)) 'Where 12345 is the hWnd of the window I want to wrap
Oh wow, I just found the documentation on IWin32Window, and see that it is only one property... Handle. Yes, then of course I can easily make this NativeWindowWrapper class...
I haven't tested it yet, but I am sure it will work just fine...
Public Class NativeWindowWrapper
Implements IWin32Window
Private _Handle As IntPtr
Public ReadOnly Property Handle As System.IntPtr Implements System.Windows.Forms.IWin32Window.Handle
Get
Return _Handle
End Get
End Property
Public Sub New(ByVal Handle As IntPtr)
Me._Handle = Handle
End Sub
End Class

Raising events in a class library exposed to COM

I'm trying to write a wrapper to a service, which will be used by an existing VB6 project. I've got most of the basic framework working, except for one important aspect: I can reference the wrapper in a VB6 project and subs/function calls etc. work as expected, but events do not. The events are visible in the VB6 app, but they never fire.
VB.NET Code:
Public Event Action_Response(ByVal Status as String)
Public Function TestEvent()
RaiseEvent Action_Response("Test Done")
Return "Done"
End Function
VB6 Code:
Dim WithEvents my_Wrapper as Wrapper_Class
Private Sub cmdTest_Click()
Set my_Wrapper = New Wrapper_Class
Debug.Print my_Wrapper.TestEvent()
End Sub
Private Sub my_Wrapper_Action_Response(ByVal Status As String)
Debug.Print Status
Set my_Wrapper = Nothing
End Sub
So, the cmdTest button code prints 'Done' as expected, but the Action_Response event doesn't fire. Is there something else do I need to do to get the event to fire?
Its too much to write in a comment, so I'll make it an answer....
First, identify the .net class you want to expose to COM. I'll pick a class called CORE.
Create an interface that describes the EVENTS that the CORE object will source (ie generate).
<ComVisible(True)> _
<Guid("some guid here...use guidgen, I'll call it GUID1")> _
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface ICoreEvents
<System.Runtime.InteropServices.DispId(1)> _
Sub FileLoaded(ByVal Message As String)
End Interface
Next, Create an interface for the COM exposed properties and methods of your class.
<ComVisible(True)> _
<Guid("another GUID, I'll call it guid2")> _
<InterfaceType(ComInterfaceType.InterfaceIsDual)> _
Public Interface ICore
ReadOnly Property Property1() As Boolean
ReadOnly Property AnotherProperty() As ISettings
ReadOnly Property Name() As String
ReadOnly Property Phone() As String
End Interface
Now, create your actual .net class
<ComVisible(True)> _
<ClassInterface(ClassInterfaceType.None)> _
<ComDefaultInterface(GetType(ICore))> _
<ComSourceInterfaces(GetType(ICoreEvents))> _
<Guid("a third GUID, I'll call it GUID3")> _
Public Class Core
Implements ICore
<System.Runtime.InteropServices.ComVisible(False)> _
Public Delegate Sub OnFileLoaded(ByVal Message As String)
Public Event FileLoaded As OnFileLoaded
End Class
Now, when you need to raise the FileLoaded event, just RAISEEVENT FILELOADED(Message) from within your class. .NET will forward the event out to COM because you've hooked up the COMSourceInterfaces attribute.
The attribute is shorthand for much of of this, but unfortunately doesn't give you quite the control that you need to do certain things (for instance retain version compatibility on your com interfaces).

Handling VB.NET events in VB6 code

I have some VB6 code that instantiates a class which handles events that are being raised from a VB.NET component. The VB6 is pretty straightforward:
private m_eventHandler as new Collection
...
public sub InitSomething()
dim handler as EventHandler
set handler = new EventHandler
m_eventHandler.Add handler
...
m_engine.Start
end sub
Note that the event handler object has to live beyond the scope of the init method (which is why it is being stored in a Collection). Note also that m_engine.Start indicates the point in the program where the VB.NET component would start raising events.
The actual event handler (as requested):
Private WithEvents m_SomeClass As SomeClass
Private m_object as Object
...
Private Sub m_SomeClass_SomeEvent(obj As Variant)
Set obj = m_object
End Sub
Note that m_object is initialized when an instance of EventHandler is created.
The VB.NET code which raises the event is even simpler:
Public ReadOnly Property SomeProp() As Object
Get
Dim obj As Object
obj = Nothing
RaiseEvent SomeEvent(obj)
SomeProp = obj
End Get
End Property
My problem is that when I debug the VB6 program, the first time InitSomething gets called, the event will not be handled (the VB6 event handler is never entered). Subsequent calls to InitSomething does work.
Everything works as I would have expected when I run the program outside the debugger. At this point, I'm not even sure if this is something I should be worried about.
It may or may not be relevant but the VB.NET was converted from a VB6 using the Visual Studio code conversion tool (and subsequently manually cleaned up).
I've found that if you are writing .Net Components for Consumption in VB6 (or any other COM environment) the utilisation of Interfaces is absolutely criticial.
The COM templates that comes out of the box with VStudio leave a lot to be desired especially when you are trying to get Events to work.
Here's what I've used.
Imports System.Runtime.InteropServices
Imports System.ComponentModel
<InterfaceType(ComInterfaceType.InterfaceIsDual), Guid(ClientAction.InterfaceId)> Public Interface IClientAction
<DispId(1), Description("Make the system raise the event")> sub SendMessage(ByVal theMessage As String)
End Interface
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch), Guid(ClientAction.EventsId)> Public Interface IClientActionEvents
<DispId(1)> Sub TestEvent(ByVal sender As Object, ByVal e As PacketArrivedEventArgs)
End Interface
<ComSourceInterfaces(GetType(IClientActionEvents)), Guid(ClientAction.ClassId), ClassInterface(ClassInterfaceType.None)> _
Public Class ClientAction
Implements IClientAction
Public Delegate Sub TestEventDelegate(ByVal sender As Object, ByVal e As PacketArrivedEventArgs)
Public Event TestEvent As TestEventDelegate
public sub New()
//Init etc
end sub
public sub SendMessage(theMessage as string) implements IClientAction.SendMessage
onSendMessage(theMessage)
end sub
Protected Sub onSendMessage(message as string)
If mRaiseEvents Then
RaiseEvent TestEvent(Me, New PacketArrivedEventArgs(theMessage))
End If
End Sub
end Class
I've been able to get COM and .Net consumers of the Assembly/Component to work properly with events and be able to debug in and out of the component.
Hope this helps.
Just something to try - I have an inherent distrust of "As New .."
Can you try
private m_eventHandler as Collection
public sub InitSomething()
dim handler as EventHandler
set handler = new EventHandler
If m_eventHandler Is Nothing Then
Set m_eventHandler = New Collection
End if
m_eventHandler.Add handler
...
m_engine.Start
end sub
Alas, I've got no idea why this works in normal execution and not in debug except some vague suspicions that it's to do with .NET being unable to instantiate the VBA.Collection object (MS recommends that you write a quick VB6 component to do so), but since you're not creating collections in .NET code, it is still just a vague suspicion.