LightSwitch Custom Control: Autocomplete Textbox

29. August 2010

1. Create a lightswitch application or open your existing lightswitch application.

2. Add a new Silverlight class project (VB or C#), 

     File, New, Add Project and select Silverlight class project, name it something like LightSwitch.CustomControls

3. Create an silverlight user control called LSAutocomplete

 

 

4. Add an AutocompleteBox from toolbox to user control (If you do not have AutocompleteBox please download it here  )

 

 

5. Your xaml should be something like below

 <sdk:AutoCompleteBox   HorizontalAlignment="Stretch" Margin="0,0,0,0" x:Name="LSAutocomplete" VerticalAlignment="Stretch" />

6. Press F7 to bring code editor  window and Add following properties

  private object   _ItemsSource;
        public object ItemsSource
        {
            get
            {
                return _ItemsSource;
            }
            set
            {
                if (_ItemsSource != value)
                {
                    _ItemsSource = value;
                     LSAutocomplete.ItemsSource = (IEnumerable) _ItemsSource;
                }
                
            }
        }


        private string _SelectedItemPath;
        public string SelectedItemPath
        {
            get
            {
                return _SelectedItemPath;
            }
            set
            {
                if (_SelectedItemPath != value)
                {

                    var bindingPath = "Screen." + value;
                    LSAutocomplete.SetBinding(AutoCompleteBox.SelectedItemProperty, new Binding() { Path = new PropertyPath(bindingPath), Mode = BindingMode.TwoWay });
                   
                    _SelectedItemPath = value;
                }

            }
        }

7. Build your LightSwitch.CustomControls project

8. Create 2 tables similar to the following in your sql database

 

Customer Table

CustomerID

CustomerName

CountryID

 

Country Table

CountryID

CountryName

 

Add one to many relation (Country Table’s CountryID to Customers Table CountryID) and fill some countries manually(or create screen in LightSwitch to fill in)

9. Create a list and detail screen in lightswitch, refer to how do I videos below.

http://msdn.microsoft.com/en-us/lightswitch/cf7b8a6e-c3fd-4d1f-88d4-095a1d02f6f1.aspx

 

10. Change Country ‘s control to Custom Control and click Customer Control’s change button on properties panel and add reference as follow.

 

 

11. Find the control you built in Add Custom Control window and then press OK

 

 

12. Add countries list to screen , click add Data Item, select queries and countries, press OK 

 

13. Now, add following code in CustomerDetail_Loaded event

 

partial void CustomerListDetail_Loaded()
        {

            IContentItemProxy proxy = this.FindControl("Country1");  //make sure name of control is country1
            proxy.Invoke(() =>
            {
                var cc = (LightSwitch.CustomControls.LSAutoComplete)proxy.Control;
                cc.SelectedItemPath = "CustomerCollection.SelectedItem.Country";
                cc.ItemsSource = this.CountryCollection;
              
            }
            );
        }

 

You can always change the summary field to Display different column  in autocomplete box.

 

 

 

 

14. That’s All, if everything goes well, you should see something like below. Please find attached code and SQL scripts to generate required tables.

 

Sources.zip (1.68 kb)

.Net 4, C#, Silverlight, LightSwitch

Silverlight 4: Developing with Managed Extensibility Framework (MEF) preview 9, MVVM pattern(Visual Basic)

9. April 2010

What is MVVM, Managed Extensibility Framework (MEF)

1.       Create a Silverlight Application(Version 4). Add web project to it when prompted (call it MySLApplication).

2.       Add a folder called Models in your server project(MySLApplication.Web)

3.       Add an interface called IModel which will be interface for your data Model.

Public Interface IModel

    Function GetData() As List(Of String)

    Sub AddData(ByVal Title As String)

End Interface

4.       Add 2 references to MyApplication.Web, Download MEF Preview 9(link at top) if you have not done already and decompress it, add reference to System.ComponentModel.Composition(which will be in bin\sl\ folder)  

 

5.       Add a class implementing IModel as follow in same Model Folder you can add another class in same file to instantiate MyDataModel


<Export(GetType(IModel))>

Public Class MyDataModel

    Implements IModel

 

    Private ReturnData As New List(Of String)

    Public Sub New()

        'Adding data mnaully but you might get it form your database

        ReturnData.Add("Learn Silverlight")

        ReturnData.Add("MEF in Silverlight")

 

    End Sub

    Public Sub AddData(ByVal Title As String) Implements IModel.AddData

        ReturnData.Add(Title)

    End Sub

 

    Public Function GetData() As List(Of String) Implements IModel.GetData

        Return ReturnData

    End Function

End Class

 

 

Now we have data model ready which is going to be consumed by  a listbox in our silverlight application. We are going to implement Model View View Model pattern to display our data.

6.       So in silverlight client project(MyApplication), Add a folder called ViewModels

7.       Now we are adding a base class for our ViewModels implementing INotifyPropertyChanged. So add a class called ViewModelBase in ViewModels Folder as below

Imports System.ComponentModel

Public Class ViewModelBase

    Implements INotifyPropertyChanged

    Sub New()

        'Anything we want to intialize for all our viewmodels

    End Sub

    Protected Sub OnPropertyChanged(ByVal PropertyName As String)

        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(PropertyName))

    End Sub

 

    Public Event PropertyChanged(ByVal sender As Object, ByVal e As _

System.ComponentModel.PropertyChangedEventArgs) Implements _

System.ComponentModel.INotifyPropertyChanged.PropertyChanged

End Class

 

 

8.  In ViewModels folder lets create a class called MainPageViewModel which we are going to bind to our MainPage

Imports System.ComponentModel.Composition

'probably using constant is better for attribute value

<Export("MainPageViewModel")>

Public Class MainPageViewModel

    Inherits ViewModelBase

    'Name MyModel can be anything, only interface IModel is used to find data

    <Import()>

    Public Property MyModel As IModel

    Sub New()

        CompositionInitializer.SatisfyImports(Me)

        Books = MyModel.GetData()

    End Sub

    Public Property Books As List(Of String)

End Class

9.   

10.   Open MainPage.xaml file and add a list box with binding it to Books

<ListBox Height="100" ItemsSource="{Binding Books}" HorizontalAlignment="Left" Margin="132,98,0,0" Name="ListBox1" VerticalAlignment="Top" Width="120" />

11.   Open MainPage.xaml.cs and modify Sub New as following

Public Sub New()

        InitializeComponent()

        CompositionInitializer.SatisfyImports(Me)

    End Sub

    <Import("MainPageViewModel")>

    Public WriteOnly Property MainPageViewModel As Object

        Set(ByVal value As Object)

            Me.DataContext = value

        End Set

    End Property

 

12.   Build your application and run it, you will be able to see 2 books listed in listbox

 

 

 

In my next blog I will write about how to handle commands in ViewModel and  also about WCF RIA WCF services.

 Download source application below.

MySLApplication.zip (1.02 mb)

.Net 4, Pattern, Silverlight, VB.NET

Simple Random Password Generator

14. March 2010

Here is simple random password generator:

Use it following way:

Dim Password As String = String.Format("{0}{1}{2}", GRP(CharType.SpecialCharacters, 2), _
GRP(CharType.Numbers, 2), GRP(CharType.UpperCase, 2))

Private Function GRP(ByVal StringType As CharType, ByVal Length As Integer) As String

        Dim FindForm As String = ""

        Select Case StringType

            Case CharType.UpperCase

                FindForm = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

            Case CharType.LowerCase

                FindForm = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

            Case CharType.Numbers

                FindForm = "0123456789"

            Case CharType.SpecialCharacters

                FindForm = "!@#$%^&*"

        End Select

 

        Dim rand = New Random()

        Dim randomstr = Enumerable.Repeat(0, Length).Select(Function(f) FindForm(rand.Next(0, FindForm.Length)))

        Return String.Concat(randomstr.ToArray)

    End Function

   

    Enum CharType

        UpperCase

        LowerCase

        Numbers

        SpecialCharacters

    End Enum

 

VB.NET

Domain Driven Design: Designing Data Layer and Business Logic

6. March 2010
Data Layer is mainly a set of Data Access Logic Components(logic to access data from your data store),Helper/Utilities(for manipulation/transformation), Service Agents( when you are using external service that help to shape your database to application needs).

In Business Logic, you are going to design business entities(like customer, supplier etc)  thats represents custom objects  required by your application which are needed to be mapped to your data. So if you are going for Domain Driven Design, you are going to have entities, values(domain properties), aggregate roots(set of entities that group related child entities), Repositories(responsible for retrieving/storing aggregate roots) and domain services(business process).

Check following link for the source of above paragraph


Now main job of DAL involves around connections, queries and mapping above entities to real data structure in your database. I suggest you to look at ADO.NET Entity Framwork  to find about a new technology available to access database.  Business Logic contains entities(for eg. Customer Class), which are similar to DAO objects(create in DAL) but customized with business needs with business workflows and business components. 

So you have to create DAO classes in Data Access Layer which are consumed by your business logic to create business entities. This might create a problem of dependency, the way entities are created shows data access layer and business layer are tightly coupled and dependant of each other. To save yourself from this circular reference you can use a technique called Inversion of Control where you depend on separated interfaces to communicate. Following article discuss about it in more detail(though the article may not look relevant to you at first but might be useful):


I strongly suggest you to read "Pattern of Enterprise Application Architecture" written by Martin Flower if you want to understand all these things in more

General, Pattern

Single Instance Application in Visual Basic without using Application Framework

13. February 2010

A simple method using semaphore class(creating object to satisfy one request) in Application Startup's event(Go to Project Properties, Application Tab, View Application Events). 


Partial Friend Class MyApplication
        Dim AmINew As Boolean
        Dim sp As New System.Threading.Semaphore(0, 1, "MyUniqueApplicationName", AmINew)
        Private Sub CheckingPrevInstance(ByVal s As Object, ByVal e As ApplicationServices.StartupEventArgs) Handles Me.Startup
            If AmINew Then
                MessageBox.Show("Yes")
            Else
                MessageBox.Show("I cannot co-exist ")
                e.Cancel = True
            End If
        End Sub
    End Class

VB.NET ,

VB.Net: Sort as Windows Explorer without StrCmpLogicalW

29. November 2009

Its quite insteresting to see that windows explorer sorts filenames etc in Native Sort where string are sorted by taking care of string and numbers as numbers. It uses StrCmpLogicalW shell function to compare by default. Siganture for this function in vb.net is

 

<System.Runtime.InteropServices.

 

DllImport("shlwapi.dll", charset:=Runtime.InteropServices.CharSet.Unicode)> _
Public Shared Function StrCmpLogicalW(ByVal strA As String, ByVal strB As String) As Int32
End Function

 

 

I tried another way of sorting by not using above mentioned function, following is an example if it interests you.

Just put a button in the form to test it

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim Filenames() As String = New String() {"3string", "2string", "st2ring", "20string", "string2", "st3ring", "string3", "string20", "st20ring"}

        Array.Sort(Filenames, New CustomComparer)

       MessageBox.Show(String.Join(",", Filenames))

    End Sub

End Class

Public Class CustomComparer

    Implements IComparer(Of String)

    Private Position As Integer

    Private Order As Integer = 1

    Public Sub New(Optional ByVal Ascending As Boolean = True)

        If Not Ascending Then

            Order = -1

        End If

    End Sub

Public Function Compare(ByVal x As String, ByVal y As String) As Integer Implements System.Collections.Generic.IComparer(Of String).Compare

        Dim res1 = System.Text.RegularExpressions.Regex.Split(x, "(\d+)", System.Text.RegularExpressions.RegexOptions.IgnoreCase).ToList

        Dim res2 = System.Text.RegularExpressions.Regex.Split(y, "(\d+)", System.Text.RegularExpressions.RegexOptions.IgnoreCase).ToList

        res1.RemoveAll(Function(item) item = String.Empty)

        res2.RemoveAll(Function(item) item = String.Empty)

        Position = 0

        For Each xstr In res1

            If res2.Count > Position Then

                If Not IsNumeric(xstr) AndAlso Not IsNumeric(res2(Position)) Then

                    Dim intresult As Integer = String.Compare(xstr, res2(Position), True)

                    If intresult <> 0 Then

                        Return intresult * Order

                    Else

                        Position += 1

                    End If

                ElseIf IsNumeric(xstr) And Not IsNumeric(res2(Position)) Then

                    Return -1 * Order

                ElseIf Not IsNumeric(xstr) And IsNumeric(res2(Position)) Then

                    Return 1 * Order

                ElseIf IsNumeric(xstr) And IsNumeric(res2(Position)) Then

                    Dim res As Integer = Decimal.Compare(Decimal.Parse(xstr), Decimal.Parse(res2(Position)))

                    If res = 0 Then

                        Position += 1

                    Else

                        Return res * Order

                    End If

               End If 

            Else

                Return -1 * Order

            End If

        Next

        Return 1 * Order
    
End Function
End Class

VB 2010: Collection Initializer for Stack and Queue

1. November 2009

VB 2010 has provided a great way of intilizing collections, thats helps our code to be more redable, compact etc.

 

 

Dim users As New List(Of String) From {"Jack", "Ferdinand"}

 

 

But if I type
Dim BillPayers As New Queue(Of String) From {"George", "Anthony"}
I get an instant error
System.Collections.Generic.Queue(Of String)' with a collection initializer because it does not have an accessible 'Add' method.
Wow! Thats helpful, it says we cannot use collecion intializer since Queue does not have an Add method. Now here comes extension method to rescue

 

Module

Extensions
    <Extension()> Sub Add(Of t)(ByVal InitializableQ As Queue(Of t), ByVal ToAddValue  As t)
      InitializableQ.Enqueue(ToAddValue)
      End Sub
End
Module

 

 

Voila, You are done!!

Keep testing...........

VB.NET , ,

How to bind Data to User Control using Visual Basic .Net

26. October 2009

Following example shows an usercontrol whose property, PropertyValue is bindable. This value is shown in usercontrol using a TextBox(tbPropertyValue). To use this control create a user control in your project, copy/paste the following code, build your application, drop the usercontrol from toolbox to your application, use databindings property to assign binding field to PropertyValue.

Imports System.ComponentModel
Friend Class BindableControl
    Public Event ValueChanged As PropertyChangedEventHandler

    <System.ComponentModel.Bindable(True, BindingDirection.TwoWay)> _
    Public Property PropertyValue() As String
        Get
            Return tbPropertyValue.Text
        End Get
        Set(ByVal value As String)
            Dim ppe As PropertyChangedEventArgs = New PropertyChangedEventArgs("PropertyValue")
            OnValueChanged(ppe)
            tbPropertyValue.Text = value
        End Set
    End Property

    Public Sub OnValueChanged(ByVal e As System.ComponentModel.PropertyChangedEventArgs)
        RaiseEvent ValueChanged(Me, e)
    End Sub
    Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles tbPropertyValue.TextChanged
        PropertyValue = tbPropertyValue.Text
    End Sub
End Class

VB.NET ,

Get a list of Installed Programs using WMI Query and Uninstall

13. October 2009

Following code shows how to get a list of programs into listview using wmi query. Drop 2 buttons and a listview into a Windows Form and test it


Public Class Form1

 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

  ListView1.View = View.Details
  ListView1.Columns.AddRange(New ColumnHeader() {New ColumnHeader() With {.Text = "Caption", .Width = 150}, _
  New ColumnHeader() With {.Text = "Vendor", .Width = 150}, _
  New ColumnHeader() With {.Text = "Version", .Width = 200}})

 End Sub

 Private Sub btnGetProgram_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnGetProgram.Click

  Dim searcher As New ManagementObjectSearcher("root\cimv2", "SELECT * FROM Win32_Product")
  For Each queryObj As ManagementObject In searcher.Get()
   Dim li As ListViewItem = ListView1.Items.Add(queryObj.Item("Caption").ToString)
   li.SubItems.Add(queryObj.Item("Vendor").ToString)
   li.SubItems.Add(queryObj.Item("Version").ToString)
  Next
 End Sub

 Private Sub btnDelete_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnDelete.Click
  Dim searcher As New ManagementObjectSearcher("root\cimv2", String.Format("SELECT * FROM Win32_Product where caption='{0}' and Version ='{1}'", ListView1.SelectedItems(0).Text, ListView1.SelectedItems(0).SubItems(2).Text))

  For Each queryObj As ManagementObject In searcher.Get()

   queryObj.InvokeMethod("Uninstall", Nothing)
  Next
 End Sub


End Class

 

 

VB.NET ,

Edit SubItems in ListView Control

22. July 2009

This is just a simple example of how to edit Sub Items in ListView. If you need such things frequently then just create an user control inheriting ListView and incorporate the idea, you can read column names dynamically and create context menu as shown in the example, you can find source project at the bottom of this post, please download it. I am providing 2 methods

1. Using ContextMenu

   Private WithEvents EditBox As New TextBox

    Private WithEvents EditMenu As New ContextMenu

    Private SelectedItem As ListViewItem

    Private EditingcolumnIndex As Integer = 0

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        For i As Integer = 1 To ListView1.Columns.Count - 1

            Dim EditCoulmn1 As New MenuItem(String.Format("Edit {0}", ListView1.Columns(i).Text), AddressOf EditMenu_click)

            EditMenu.MenuItems.Add(EditCoulmn1)

        Next

    End Sub

 

    Private Sub EditMenu_click(ByVal S As Object, ByVal e As EventArgs)

        Dim clickedmenu As MenuItem = CType(S, MenuItem)

        EditingcolumnIndex = (From c As ColumnHeader In ListView1.Columns Where c.Text = clickedmenu.Text.Replace("Edit ", String.Empty) Select c).First.Index

        EditBox.Bounds = SelectedItem.SubItems(EditingcolumnIndex).Bounds

        EditBox.Text = SelectedItem.SubItems(EditingcolumnIndex).Text

        EditBox.Visible = True

        EditBox.Select()

    End Sub

    Private Sub Editbox_Leave() Handles EditBox.Leave

        SetValues()

    End Sub

    Private Sub EditBox_KeyUp(ByVal s As Object, ByVal e As KeyEventArgs) Handles EditBox.KeyUp

        If e.KeyCode = Keys.Enter Then

            SetValues()

        ElseIf e.KeyCode = Keys.Escape Then

            EditBox.Visible = False

        End If

    End Sub

    Private Sub SetValues()

        SelectedItem.SubItems(EditingcolumnIndex).Text = EditBox.Text

        EditBox.Visible = False

    End Sub

    Private Sub ListView1_MouseClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseClick

        Dim item = ListView1.GetItemAt(e.X, e.Y)

        If item IsNot Nothing Then

            If e.Button = Windows.Forms.MouseButtons.Right Then

                SelectedItem = item

                EditMenu.Show(ListView1, e.Location)

            End If

        End If

        EditBox.Visible = False

        ListView1.Controls.Add(EditBox)

    End Sub

2. HitTest Method

Public Class Form1

    Private WithEvents EditBox As New TextBox

    Private SelectedItem As ListViewItem.ListViewSubItem

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        EditBox.Visible = False

        ListView1.Controls.Add(EditBox)

        ListView1.FullRowSelect = True

    End Sub

Private Sub Editbox_Leave() Handles EditBox.Leave

        SetValues()

    End Sub

    Private Sub EditBox_KeyUp(ByVal s As Object, ByVal e As KeyEventArgs) Handles EditBox.KeyUp

        If e.KeyCode = Keys.Enter Then

            SetValues()

        ElseIf e.KeyCode = Keys.Escape Then

            EditBox.Visible = False

        End If

    End Sub

    Private Sub SetValues()

        SelectedItem.Text = EditBox.Text

        EditBox.Visible = False

    End Sub

    Private Sub ListView1_MouseClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles ListView1.MouseClick

        Dim info As ListViewHitTestInfo = ListView1.HitTest(e.X, e.Y)

        If info.Item IsNot Nothing AndAlso info.SubItem.Bounds.X > 10 Then

 

            EditBox.Bounds = info.SubItem.Bounds

            EditBox.Text = info.SubItem.Text

            SelectedItem = info.SubItem

            EditBox.Visible = True

            EditBox.Select()

 

 

        End If

    End Sub

End Class

 

 

 

EditSubItemHitTest.zip (118.95 kb)

 

EditSubItem.zip (121.08 kb)

VB.NET ,