Using TaskCompletionSource in wraping event handler


Using TaskCompletionSource you can wrap any operation in to task, so that you can do anything you can with the task object. The TaskCompletionSource class is very important and today’s post will be explain how to wrap button click event in to TaskCompletionSource. With this wrap we will see how complicated operation behind click button handler can be simplified.

The for this blog post is simple Windows Store app shown on the picture below.

screen_sample2

When the Play Slides is clicked, Image slide is started which start animation of images. XAML code behind this app is listed here:

<Page.Resources>
    <Storyboard x:Name="animImageSlideIn">
        <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateY)"
                                        Storyboard.TargetName="img">
            <EasingDoubleKeyFrame KeyTime="0" Value="900"/>
            <EasingDoubleKeyFrame KeyTime="0:0:2" Value="0" />
        </DoubleAnimationUsingKeyFrames>
    </Storyboard>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Width="200" Height="250" >
        <Button x:Name="playbtn" Height="50" Margin="0,5,0,5" Content="Play Slides" HorizontalAlignment="Center" Click="playbtn_Click"></Button>

        <Image x:Name="img" HorizontalAlignment="Center">
            <Image.RenderTransform>
                <CompositeTransform TranslateY="900" />
            </Image.RenderTransform>
        </Image>
    </StackPanel>
</Grid>

First, it will be presented  the implementation without TaskCompletionSource. The Play Slides Click event is the following code:

private async void  playbtn_Click(object sender, RoutedEventArgs e)
{
    int i = 1;

    EventHandler<object> handler = null;

    handler = delegate
    {
        if (i <= 3)
        {
            playbtn.Content = string.Format("Slide Image {0}",i);

            LoadSourceImage(i);

            animImageSlideIn.Begin();

            i++;
        }
        else
        {
            playbtn.Content = "Play Slides";
            animImageSlideIn.Completed -= handler;
            LoadSourceImage(0);

        }
    };

    animImageSlideIn.Completed += handler;
    handler(null,null);
}

As we can see from the listing above the code is pretty much long and little bit confused because we subscribe to the handler and call it as much as we reach the magic number of slides. Whe the number of slides is reached we unsubscribed from the handler and exit  the method.

Now implement the same functionality with the TaskCompletionSource class. The following listing shows the implementation:

private async void playbtn_Click(object sender, RoutedEventArgs e)
{

for(int i=1; i<=3; i++)
{
playbtn.Content = string.Format("Slide Image {0}",i);
LoadSourceImage(i);
await animImageSlideIn.RunAsync();
}

playbtn.Content = "Play Slides";
}

As we can see the implementation is very simple and concise. in for loop we call LoadSourceImage then asynchrony run animation.

The source code of the demo can be found by clicking the image below:

This blog post is inspired by Stephen Toub //build/ session.

Advertisement

Pausing and cancelling async method in C#


Responsiveness of your app is not just fee UI thread by implementing async. It is more that that. When a long operation is under process in your app, user sometimes wants to cancel it  or  pause the operation. Imagine your app processing hundreds of files or images. Such a operation can take more that few seconds and user must have option to cancel it. Canceling and pausing are very important feature for every app that implements long operations.

This blog post will present the way of using CancelationToken built in cancel feature in .NET, as well as a PauseToken custom implementation which is very similar to CancelationToken.

Original implementation of PauseToken is from the pfxteam blog which you can find here.

We will implement simple Windows Store app with cancel and pausing the async operation. The picture below shows the sample app:

screen_sample1

 

As you can see when the Start Process button is clicked it begins process of processing image files. There is also ProgressRing control which shows the progress and percentage of completeness. From the right side you can see two buttons. The Cancel button cancels the operation, and pause button pauses operation until the Pause button is clicked again.

The implementation behind Process button is the folowing:

private async  void btnProcess_Click(object sender, RoutedEventArgs e)
{
    //creating cancel and pause token sources
    m_pauseTokeSource = new PauseTokenSource();
    m_cancelationTokenSource = new CancellationTokenSource();

    //get al picture from picture library
    var picturesFolder = KnownFolders.PicturesLibrary;
    var fileList = await picturesFolder.GetFilesAsync();

    //set ProgressRing to active
    ring2.IsActive = true;

    try
    {
        //asynchrony process files, by passing pasue and calcel tokens

        await ProcessImages(fileList, m_pauseTokeSource.Token, m_cancelationTokenSource.Token);
    }
    catch (Exception)
    {
        //do nothing when somthing went wrong not when taks is canceled
    }
    finally
    {
        //make inactive ProgressRing
        ring2.IsActive = false;
    }
}

Click event implementation of the Start Process button 

First we create Cancel and Pause Source tokens. Gent the picture content in form of list of files. Then we call asynchonious ProcessImages method by passing list of images files, cancel and pause tokens. Process images is called within try catch finally blocks, because every cancel task throws exception.

Implementation ProcessImages async method

ProcesImage method is async method which accept cancelation and pasue token nad returns Task object.

public async Task ProcessImages(IEnumerable<StorageFile> images, PauseToken pauseToken, CancellationToken cancelToken)
{
    double count=images.Count();
    double current=0;
    foreach (var file in images)
    {
        //if the paise is active the code will wait here but not block UI thread
        await pauseToken.WaitWhilePausedAsync();

        ring2Text.Text = string.Format("{0}%",(int)(100*current / count));

        await ProcessAsync(file, cancelToken);
        current++;
    }
    ring2Text.Text = string.Format("100%");
}

In foreach loop first we await pauseToken.WaitWhilePausedAsync(); which wait if IsPause property of the Token class is true, otherwize there is no awaiting here. The next await is out Delay which takes cancelation token as parameters. When the Pause button is clicked, pauseToken is awaiting until the pause button is clicked again. In case of cancelation when the Cancel button is clicked the Cancel() method of the cancelationTokenSource is called and exception is thorwn. Then processImages method is interupted and finally blick progressring is disabled.
Pause and Cancel Click implementation are shown in the following listing:

private void btnPause_Click(object sender, RoutedEventArgs e)
{
 m_pauseTokeSource.IsPaused = !m_pauseTokeSource.IsPaused;
}

private void btnCancel_Click(object sender, RoutedEventArgs e)
{
 m_cancelationTokenSource.Cancel();
}

Complete source code can be downloaded from link below.

Using Dispatcher in Windows 8.1 Store Apps


If we want to inform the user about operation status which is running in the background by callback method, you have to be aware that the callback call is not in the UI thread. In this situation we cannot show Dialog message as ordinary, because we will go in trouble with cross-thread exception.

If we want to run eg. Dialog Message in Windows Store apps to inform user about operation status we need to run a “safe code”. This means we need to call Dispatcher, which cares that all calls related to UI components have to be from the main thread.

Lets assume we have callback method which gets call when user wants to see the payment status. Process of checking payment is not in UI thread, so the callback method of reporting status will also not be in the UI thread. In that situation, any code which touches UI must be executed with Dispatcher.

The following code  shows how to create MessageDialog and show it in Windows Store app, so that the user get information about payment status.

/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void PaymentCheckCompleted(object sender, PaymentEventArgs e)
{
    MessageDialog dlg = null;
    if (e == null)
    {
        dlg = new MessageDialog("Status is NULL", "SMS Provider");
    }
    else if (e.status == PaymentStatus.ok)
    {
        dlg = new MessageDialog("Status is OK", "SMS Provider");
    }
    else if (e.status == PaymentStatus.cancelled)
    {
        dlg = new MessageDialog("Status is CANCELED", "SMS Provider");
    }
    else if (e.status == PaymentStatus.failed)
    {
        dlg = new MessageDialog("Status is FAILED", "SMS Provider");
    }
    else if (e.status == PaymentStatus.pending)
    {
        dlg = new MessageDialog("Status is PENDING", "SMS Provider");
    }
    else
    {
        dlg = new MessageDialog("Status is UNKNOWN", "SMS Provider");
    }

    dlg.Commands.Add(new UICommand("OK"));
    dlg.DefaultCommandIndex = 0;
    dlg.CancelCommandIndex = 0;
    dlg.Options = MessageDialogOptions.None;

    this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => dlg.ShowAsync());

}

The last line in the example above shows how to use Dispatcher, and how to show dialog so that we never see cross-thread exception.

Developing Windows Store App Part 2: REST Service implementation


Today’s blog post will discass how to implement REST service by using ASP.NET Web API. To expose data to our Windows Store app, we need to implement some kind of REST or Soap service. For this sample we are going to create Rest service, which will expose basic data operation to our Windows 8 Store client.

1. Open you previous EFModel solution in Visual Studio 2012.

2. Right click at the solution and choose Add New Project

3. Select ASP.NET MVC 4 Web Application, Enter the name of the project.

4. Click OK button.

5. On the next Dialog select Web API, and click OK button.

6. Initial skeleton for RESTService is created.

7. Add Reference of EFModel project.

Now we are going to implement Controller.

8. Right Click on RestService project ->Add New Controller.

9. Enter CExplorer name, and click OK.

Implementation of the controller shows the next listing:

 public class CExplorerController : ApiController
    {
        // GET CExplorer
        [HttpGet]
        public List Place()
        {
            /// return new string[] { "value1", "value2" };
            ///
            try
            {
                using (CExplorerContext ctx = new CExplorerContext())
                {
                    var lst = ctx.Places.ToList();
                    return lst;
                }
            }
            catch (Exception)
            {

                throw;
            }

        }

        // GET CExplorer/5
        [HttpGet]
        public Place Place(int id)
        {
            try
            {
                using (CExplorerContext ctx = new CExplorerContext())
                {
                    return ctx.Places.Where(x => x.PlaceID == id).FirstOrDefault();
                }
            }
            catch (Exception)
            {

                throw;
            }
        }
        // GET CExplorer/5
        [HttpGet]
        public IEnumerable Search(string queryText)
        {
            try
            {
                //queryText = queryText;
                using (CExplorerContext ctx = new CExplorerContext())
                {

                    var lst11 = ctx.Dishes.Where(x => x.Name.ToLower().Contains(queryText.ToLower())).Select(x => x.DishID).ToList();
                    var lst22 = ctx.Towns.Where(x => x.Name.ToLower().Contains(queryText.ToLower())).Select(x => x.TownID).ToList();

                    var lst = ctx.Places.ToList();

                    var ll = from p in ctx.Places
                             where
                             lst11.Contains(p.DishID) ||
                             lst22.Contains(p.TownID) ||
                             p.Name.ToLower().Contains(queryText.ToLower()) ||
                             p.Slogan.ToLower().Contains(queryText.ToLower())

                             select p;

                    var rr = ll.ToList();
                    return rr;
                }
            }
            catch (Exception)
            {

                throw;
            }
        }

        // POST CExplorer
        [HttpPost]
        public void Place([FromBody]Place plc)
        {
            try
            {
                if (plc == null)
                    return;
                using (CExplorerContext ctx = new CExplorerContext())
                {
                    if (plc.PlaceID == -1)
                    {
                        ctx.Places.Add(plc);
                        ctx.SaveChanges();
                    }
                    else
                    {
                        var p = ctx.Places.Where(x => x.PlaceID == plc.PlaceID).FirstOrDefault();
                        if (p == null)
                            return;

                        p.Name = plc.Name;
                        p.Slogan = plc.Slogan;
                        p.Description = plc.Description;
                        p.DishID = plc.DishID;
                        p.TownID = plc.TownID;
                        p.Image = plc.Image;
                        ctx.SaveChanges();
                    }
                }
            }
            catch (Exception)
            {

                throw;
            }
        }

        // GET Town
        [HttpGet]
        public List Town()
        {
            /// return new string[] { "value1", "value2" };
            ///
            try
            {
                using (CExplorerContext ctx = new CExplorerContext())
                {
                    return ctx.Towns.ToList();
                }
            }
            catch (Exception)
            {

                throw;
            }

        }

        // GET Dish
        [HttpGet]
        public List Dish()
        {
            /// return new string[] { "value1", "value2" };
            ///
            try
            {
                using (CExplorerContext ctx = new CExplorerContext())
                {
                    // var ss= ctx.Dishes.ToString();
                    return ctx.Dishes.ToList();
                }
            }
            catch (Exception)
            {

                throw;
            }

        }
    }

10. Resolve using statement, that you can compile project.

11. Modify App_Start/WebApiConfig.cs file like the following listing:

public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                 name: "DefaultApi",
                 routeTemplate: "{controller}/Place/{id}",
                defaults: new { action = "Place", id = RouteParameter.Optional }
             );

            config.Routes.MapHttpRoute(
                name: "DefaultApi1",
                routeTemplate: "{controller}/Town",
               defaults: new { action = "Town" }
            );

            config.Routes.MapHttpRoute(
                name: "DefaultApi2",
                routeTemplate: "{controller}/Dish",
                defaults: new { action = "Dish" }
            );

            config.Routes.MapHttpRoute(
                 name: "DefaultApi3",
                 routeTemplate: "{controller}/Search/{queryText}",
                defaults: new { action = "Search", queryText = RouteParameter.Optional }
             );
        }
    }

Defining connection string. For propery connection string definition you need to provide valid SQL login. You can achive this by creating new Login at SQL Server database, similar like picture below.

12. Add connection string to WebConfig file similar like the following.

<connectionStrings>
  <add name="cexplorer_db" providerName="System.Data.SqlClient" connectionString="Data Source=.\sqlexpress;Initial Catalog=cevap_db;Persist Security Info=True;User ID=testuser;Password=123;"/>
  </connectionStrings>

13. Set RestService as Startup project, and hit F5.

14. When IE appear, enter the following url adress: http://localhost:4636/cexplorer/place, to get all Places from the db.

Note: This is exactly what we inserted in Place Table, while we run EFTest console app from the previos blog post.

Now we have all instrastructure to start developing Windows Store app Ćevap Explorer. Source code for this blog can be found here.