Tuesday, May 5, 2009

Auto Complete Textbox for Vb.Net Desktop Applications

Hi all,

Ever tried the google toolbar? Whenever you type something, the results are displayed related to the type text just below the textbox. Have you ever wondered if you can include the same thing in your project.


Edited on < 10 AM EST, 19th May 2009: >
Click here for the complete project


I tried to achieve the following :

If I enter a file name, then all the file names which has the text entered by me should appear in a pop up just below my textbox. If somebody cannot access the code at the codeplex, or if somebody cannot download, I have added the code right here :

I applied the following trick :

I loaded few paths to a collection. Created a textbox, and a combobox just behind the textbox so that it should not be visible, but its drop down should be. If I enter some name, and if the text is present in the combobbox items, then the combobox dropdown should display all the items which has that text. Ofcourse, the items should be grouped accordingly.

First step, add a class "AutoCompleteEntry". This will be the class which will be bound to the collection as Combobox Items.





Imports System.IO

Public Class AutoCompleteEntry
Public type As String

Public information As FileInfo

Public Sub New(ByVal type As String, ByVal fileInformation As FileInfo)
Me.type = type
information = fileInformation
End Sub

Public Property FileType() As String
Get
Return type
End Get
Set(ByVal value As String)
type = value
End Set
End Property

Public Property FileInformation() As FileInfo
Get
Return information
End Get
Set(ByVal value As FileInfo)
information = value
End Set
End Property

Public ReadOnly Property ParentFolder() As String
Get
Return information.Name
End Get
End Property

Public ReadOnly Property Name() As String
Get
Return information.Name
End Get
End Property

Public ReadOnly Property Size() As String
Get
Return information.Length & " bytes"
End Get
End Property

End Class



2nd Step, create a Xaml File with the Name of SearchControl.Xaml and add the following code there.



< UserControl x:Class="SearchControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
< UserControl.Resources>
< DataTemplate x:Key="cmbTemplate">
< StackPanel Orientation="Horizontal" Width="250">
< TextBlock Text="{Binding Path=Name}" Width="100" />
< TextBlock Text="{Binding Path=ParentFolder}" Width="70" HorizontalAlignment="Left" Margin="5,0,0,0"/>
< TextBlock Text="{Binding Path=Size}" Width="80" HorizontalAlignment="Left" Margin="5,0,0,0"/>
< /StackPanel>
< /DataTemplate>

< /UserControl.Resources>
< Grid>

< ComboBox Margin="2,28,0,0" Name="cmbAutoComplete"
ItemsSource="{Binding}" ItemTemplate="{StaticResource cmbTemplate}" MaxDropDownHeight="200" Height="24" VerticalAlignment="Top">
< ComboBox.GroupStyle>
< GroupStyle>
< GroupStyle.HeaderTemplate>
< DataTemplate>
< StackPanel>
< Border BorderThickness="1" BorderBrush="Black">
< TextBlock Text="{Binding Items[0].FileType}" Background="AliceBlue"/>
< /Border>
< /StackPanel>
< /DataTemplate>
< /GroupStyle.HeaderTemplate>

< /GroupStyle>
< /ComboBox.GroupStyle>
< /ComboBox>
< TextBox Margin="2,28,0,0" Name="TextBox1" TextChanged="TextBox1_TextChanged" Height="24" VerticalAlignment="Top" />
< Label Margin="22,2,0,0" Name="Label1" Height="23" VerticalAlignment="Top" FontWeight="Bold" Foreground="DarkBlue" HorizontalAlignment="Left" Width="119">Enter Text Criteria< /Label>
< /Grid>
< /UserControl>




3rd Step, copy the following code and paste in the SearchControl.Xaml file.


Imports System.Collections.ObjectModel
Imports System.ComponentModel

''' < summary >
''' This class takes the path of the file which user can search and sees how big file it is.
''' < /summary >
''' < remarks>< /remarks>

Partial Public Class SearchControl

Private _threshold As Integer = 2

Private path As String = "C:\Vikas-Data\" ' set the path here which you want to search

Private items As ObservableCollection(Of AutoCompleteEntry)
Private view As IcollectionView

Public Sub New()

' This call is required by the Windows Form Designer.
InitializeComponent()

' Add any initialization after the InitializeComponent() call.


items = New ObservableCollection(Of AutoCompleteEntry)

LoadPaths(New IO.DirectoryInfo(path))

view = CollectionViewSource.GetDefaultView(items)
view.GroupDescriptions.Add(New PropertyGroupDescription("FileType"))

cmbAutoComplete.DataContext = items

End Sub

Private Sub LoadPaths(ByVal dInfo As IO.DirectoryInfo)
Dim info As IO.DirectoryInfo

For Each info In dInfo.GetDirectories()
LoadPaths(info)
Next info


For Each fInfo As IO.FileInfo In dInfo.GetFiles()
items.Add(New AutoCompleteEntry(dInfo.FullName, fInfo))
Next fInfo

End Sub

Private Sub TextBox1_TextChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.TextChangedEventArgs)
Dim text As String


text = TextBox1.Text

If (text.Length < _threshold) Then

Else
view.Filter = New Predicate(Of Object)(AddressOf MyPredicate)

If (cmbAutoComplete.HasItems) Then
cmbAutoComplete.IsDropDownOpen = True
End If
End If
End Sub

Private Function MyPredicate(ByVal item As Object) As Boolean
Dim text As String
Dim itemEntry As AutoCompleteEntry

text = TextBox1.Text
itemEntry = TryCast(item, AutoCompleteEntry)

If (itemEntry.Name.IndexOf(text) > -1) Then
Return True
End If

Return False

End Function

End Class



The output is like following screenshot :



I entered "VB" and I had various file names in the collection which contain vb so it displayed file names grouped by the folder names.

Hope this article will help you!

Thanks,
Vikas

Friday, April 24, 2009

Use WPF Themes and styles


WPF, the future of Window Forms and Programming, has some fantastic flexibility to create excellent styles and apply with an immense ease. Here is an example, we can apply the themes to our WPF UIs, and the theme will take care of styles of the components used in the window. See the following link:

WPF Themes Home. Samantha is great :)

Hope this helps!

Thanks,
Vikas

Monday, April 6, 2009

Ghost Instance Problem in office application

Hi All,

A number of office programmers face the Ghost Instance issue while developing office applications. The ghost instance is an instance of office application which is not closed properly. In this scenario, you will be able to see the application entry in the Porcess list of task manager but it will not be visible for the user. I will explain the context by using an example of powerpoint Addin(Obviously in dot net).

Q. How does it occur?

Ans. I create a PowerPoint(aka, PP)Addin, and in that PP Addin I try to create an excel(aka, XL) Application object. Now if I do extensive programming using XL objects(like Range, Shapes, Worksheet etc), these InterOP variables stay in Memory and not handled by the CLR. You will need to explicitly release every COM Object by using System.Runtime.InteropServices.Marshal.ReleaseCOMObject(theObjectToBeReleased) method. But, even after releasing the objects few of the instances keep on running in memory. So if the variables are running in the memory, and I try to close the excel, the excel gets closed but its process keeps on running in the task Manager List. When we hit close button(the cross button in TopLeftHand corner) in Excel, the CLR first closes all the AddIns opened, and then closes the excel window. If there is any COM running in the memory the AddIns get removed but the CLR is unable to close the Excel properly. The result is Ghost Instance. In case of Ghost Instance running in the task manager, if we open any Excel File by double clicking the file, it will open the file but it will Not load any Addins. Reason is barely simple, the Addins were already closed by CLR but not the Excel Application Instance. So the file is actually opened in the previous Excel Ghost Instance.

Q. What is the solution?
Ans. One solution to the problem is: Go to task Manager, Process Tab, and kill the Excel.EXE instance.
The other solution is to include the following approach in AddInShutDown event of your code. This work around uses the Process Class.

1. Try to get the list of Processes by GetProcessByName method :
Process[] processes = Process.GetProcessesByName("EXCEL")
2. Run through Each Process in the processes Array and check if process.MainWindowHandle.ToInt64() is equal to 0. If this is 0 then you will need to kill the process right away.
3. The only thing you will need to take care is, to create a time Lag so that all the AddIns should shut down first then you will need to call the above steps.
4. Creating a Time lag means, you can use a separate EXE, which will run the above steps say 5 seconds after the Excel Closing event was fired.

Hope this helps :)

Thanks,
Vikas

Friday, April 3, 2009

Problem in using Information.TypeName Property

Hi All,

Whenever we debug the code in Dot Net while using InterOP, it is very frequent to notice a proxy object rather than the actual InterOP object. When you try to get the properties of an InterOP Variable, it shows you some proxy object, and not the actual InterOP object. For example, if I declare a variable like following :

Dim xlApp as XL.Application (The excel Application object)
set xlApp = ME.Globals.ThisAddin.Application

Now, in the debug mode, after executing the above line, if I want to see what values are stored in xlApp variable, the dot net editor will show the variable as a Runtime.Proxy object most of the times. The reason is, that normally, the InterOP layer creates a wrapper object to avoid any problems running in non EN-US locales.

Due to this behavior, I faced one problem. I was not able to obtain the Type Name of application' selection object because the selection object was coming as a proxy wrapper. I tried something like this :

Dim selectionType as string

selectionType = Information.TypeName(xlApp.Selection)

The above line was giving a System.Reflection.TargetInvocationException. I had to change my project's setting to ensure that the InterOp variables are not proxy objects. For that, I went to MyProject folder in my solution, opened the AssemblyInfo.vb file, change the attribute < Assembly: ExcelLocale1033(True) > set to < Assembly: ExcelLocale1033(False) > and saved the file. And see the difference, it didn't give me a proxy object :)

See the following thread For the example:
MSDN VSTO Thread Link.