Populating AutoCompleteTextbox via WCF Service Asynchronously in Silverlight

So here in this article we will use a WCF web Service to pull the data and populate in textbox, and also apply custom filtering to populate only those data for which user is requesting.



Introduction:

In the previous article, we learned the basics of autocompletetextbox and also saw how to perform custom filtering.

There we used the ItemsSource property to fill the AutoCompleteBox with a collection of suggestions. For this to work we must have the complete list and it must be a manageable size. If we need to pull the information from somewhere else or the list is large enough that it isn't practical to load the whole thing at once, you'll need to take a different approach to filling the AutoCompleteBox.

So here in this article we will use a WCF web Service to pull the data and populate in textbox, and also apply custom filtering to populate only those data for which user is requesting.

In many scenarios, it often happens that we need a potentially time-consuming step to get the list of suggestions, such as performing a series of calculations or querying a web service. In this situation, you need to launch an asynchronous process, which we can achieve with multithreading, but some Silverlight features provide built-in asynchronous support.

So let us start writing a application to achieve this.

Step 1: Create a Silverlight application and name it "AutoCompleteViaService"

1.gif

Step 2: Then we need to go to project which is hosting our Silverlight application and add a WCF service.

2.gif

Step 3:
Give a name to this WCF service.

3.gif

Step 4: We will re-use the class  from the last article that we created and put in CountryAutoComplete.svc. So open CountryAutoComplete.svc.cs and add country class:

[DataContract]
    public class Country
    {
        [DataMember]
        public string countryname { get; set; }
        [DataMember]
        public string countrycode { get; set; }

        public Country(string countryName, string countryCode)
        {
            countryname = countryName;
            countrycode = countryCode;
        }
        public override string ToString()
        {
            return countryname;
        }

        public Country() { }

    }

Step 5: We will expose CountryAutoComplete class as service by putting [ServiceContract] on it, and define a method which will be exposed in this service contract through [OperationContract]. The following shows how our class will look.

//WCF service contract to expose out class as service
    [ServiceContract]

    //Although the WCF model is designed to behave consistently across hosting environments and transports,
    //there are often scenarios where an application does not require this degree of flexibility.
    //WCF's ASP.NET compatibility mode is suitable for scenarios that do not require the ability to host
    //outside of IIS or to communicate over protocols other than HTTP, but that use all of features of the
    //ASP.NET Web application platform.
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

    public class CountryAutoComplete
    {
        //Exposes our method which user will access via service.
        [OperationContract]
        public string[] GetCountryMatches(string inputText)
        {
            // Get the country (for example, from a server-side database).
            Country[] Countries = GetCountry();

            // Create a collection of matches.
            List<string> countryMatches = new List<string>();
            foreach (Country country in Countries)
            {
                // See if this is a match.
                if ((country.countryname.StartsWith(inputText)) ||
                        (country.countrycode.Contains(inputText)))
                    countryMatches.Add(country.countryname);
            }

            return countryMatches.ToArray();
        }

        private Country[] GetCountry()
        {
            return new Country[]
            {
                    new Country("Australia","111_AUS"),
                    new Country("America","222_US"),
                    new Country("Somalia","333_SOM"),
                    new Country("South Arfica","444_SA"),
             };
        }
    }
Step 6: Note that we have added a using for the System.ServiceModel.Activation namespace. The reason why is that it contains the types and enumerations required to construct and modify the dynamic creation of service hosts in a managed hosting environment.

Step 7: Now it's time for configuring web.config to create service endpoints and expose metadata over wire.

  1. Open web.config file in WCF configuration wizard.

    4.gif

  2. Click on create for new service and a window will open.

    5.gif
     

  3. Browse service which lies in Bin folder of your web host project. Click OPEN and move to next window wizard.

    6.gif

  4. In the next window, it will auto pick the contract will is in this service type.

    7.gif

  5. For the next windows, just accept default values and don't give any address, and click to FINISH in last. At last, it should look like this;

    8.gif

  6. Now, we will create a metadata endpoint to expose service metadata.

    Click on Create a New Service Endpoint.

    Click on Browse, and when the window comes up, click on GAC.

    9.gif

    Search for System.ServiceModel and click it.

    You will see IMetadatExchange; select it.

    10.gif

  7. Give it an address name, mex

    11.gif

  8. Now change the Binding of metadata exchange to mexHttpBinding.

    12.gif

  9. Now create a new Binding configuration

    13.gif

  10. Select customBinding and name is as customBinding0.

    14.gif

  11. Now create new service behavior configuration; name it CountryAutoCompleteBehavior.

    Add servicemetadata and servicedebug as element extensions

    Click on servicemetadata and set it HttpGetEnabled as TRUE.
     

  12. Now go back to endpoint and select contract, AutoCompleteViaService.Web.CountryAutoComplete endpoint, and change its binding to CustomBinding and binding configuration to our newly created custom configuration, customBinding0.

    Also click on AutoCompleteViaService.Web.CountryAutoComplete service and set its behaviorConfiguration to our newly created service behavior name, CountryAutoCompleteBehavior

    So the whole configuration looks like;

    15.gif

    By this we have configured WCF service and it is ready to be exposed.

Step 8: Now set AutoCompleteViaService.Web as startup project and press CTLR + F5. It will launch blank AutoCompleteViaServiceTestPage.html

Step 9: Go to AutoCompleteViaService and click on Service and add service reference.

Step 10: Add service reference window will pop-up. Click on Discover to search any existing service in the solution. You will find that CountryAutoComplete.svc is an available service at this address.
http://localhost:1592/CountryAutoComplete.svc

Give it the name CountryAutoReference.

16.gif

Step 11: Now we have service reference in out client project which is AutoCompleteViaService.

Open main.xaml.cs , and add service refeence in code behind

using AutoCompleteViaService.CountryAutoReference;

Step 12: In main.xaml , create a textbox as like below

<StackPanel x:Name="LayoutRoot">
            <input:AutoCompleteBox x:Name="autoCountry" Margin="10" HorizontalAlignment="Left" Width="200"
                    Populating="autoCountry_Populating" FilterMode="None">               
            </input:AutoCompleteBox>

            <TextBlock x:Name="lblStatus" Margin="10"></TextBlock>                 
        </StackPanel>



The Populating event fires whenever the AutoCompleteBox is ready to search for results. By default, this happens every time the user presses a key and changes the current text entry.

When the Populating event fires, you have two choices: set the ItemsSource property immediately or launch an asynchronous process to do it. Setting the ItemsSource property immediately makes sense if you have the list of suggestions on hand or you can generate them quickly. The list of suggestions will then appear in the drop-down list right away.

But in many situations, you'll need a potentially time-consuming step to get the list of suggestions, such as performing a series of calculations or querying a web service. In this situation, you need to launch an asynchronous process.

Step 13: So now we will write handler for autoCountry_Populating.

Note : When using an asynchronous operation, you need to explicitly cancel the normal processing in the Populating event handler, by setting PopulatingEventArgs.Cancel to true. You can then launch the asynchronous operation. Following is code which will get the suggestion list asynchronously from a web service.

private void autoCountry_Populating(object sender, System.Windows.Controls.PopulatingEventArgs e)
        {
            // Signal that the task is being performed asynchronously.
            e.Cancel = true;

            // Create the web service object.
            CountryAutoCompleteClient service = new CountryAutoCompleteClient();

           
// Make sure the dyanmic URL is set (for testing).

            EndpointAddress address = new EndpointAddress("http://localhost:" +
               HtmlPage.Document.DocumentUri.Port + "/CountryAutoComplete.svc");
            service.Endpoint.Address = address;

            // Attach an event handler to the completion event.
            service.GetCountryMatchesCompleted += new EventHandler<GetCountryMatchesCompletedEventArgs(service_GetCountryMatchesCompleted);           

            // Call the web service (asynchronously).
            service.GetCountryMatchesAsync(e.Parameter);           
            lblStatus.Text = "Calling web service ...";

        }

To check for correct endpoint adddress, open ServiceReferences.ClientConfig and check the address

<configuration>
    <
system.serviceModel>
        <
bindings>
            <
customBinding>
                <
binding name="CustomBinding_CountryAutoComplete">
                    <textMessageEncoding messageVersion="Default" writeEncoding="utf-8" />
                    <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647"
/>
                </binding>
            </
customBinding>
        </
bindings>
        <
client>
            <
endpoint address="http://localhost:1592/CountryAutoComplete.svc"
                binding="customBinding" bindingConfiguration="CustomBinding_CountryAutoComplete"
                contract="CountryAutoReference.CountryAutoComplete" name="CustomBinding_CountryAutoComplete" />
        </client>
    </
system.serviceModel>

</configuration>

Step 14: On the web server, the code in a GetCountryMatches () web method runs and retrieves the matches:

[OperationContract]
        public string[] GetCountryMatches(string inputText)
        {
            // Get the country (for example, from a server-side database).
            Country[] Countries = GetCountry();

            // Create a collection of matches.
            List<string> countryMatches = new List<string>();
            foreach (Country country in Countries)
            {
                // See if this is a match.
                if ((country.countryname.StartsWith(inputText)) ||
                        (country.countrycode.Contains(inputText)))
                    countryMatches.Add(country.countryname);
            }
 
            return countryMatches.ToArray();
        }

        private Country[] GetCountry()
        {
            return new Country[]
            {
                    new Country("Australia","111_AUS"),
                    new Country("America","222_US"),
                    new Country("Somalia","333_SOM"),
                    new Country("South Arfica","444_SA"),
             };
        }




Step 15: When the asynchronous operation finishes and we receive the result in our Silverlight application, we fill the ItemsSource property with the list of suggestions. Then, we must call the PopulateComplete() method to notify the AutoCompleteBox that the new data has arrived. Here's the callback handler that does the job;

void service_GetCountryMatchesCompleted(object sender, GetCountryMatchesCompletedEventArgs e)
        {
            // Check for a web service error.
            if (e.Error != null)
            {
                lblStatus.Text = e.Error.Message;
                return;
            }

            lblStatus.Text = "Response received.";

            // Set the suggestions.
            autoCountry.ItemsSource = e.Result;

            // Notify the control that the data has arrived.
            autoCountry.PopulateComplete();
        }

Step 16 : We are now ready to call WCF service to populate autoComplete textbox. So press F5 and type code in textbox to retrieve country name, say type 1 to get Australia; check yourself.

17.gif

Try 2 to get America.

18.gif

Conclusion:

SO here in this article we tried to call WCF service asynchronously for faster access to populate autocompletetextbox. We can extend this logic to other controls as well.

Hope you like this article.

Cheers.