Showing posts with label XAML. Show all posts
Showing posts with label XAML. Show all posts

Monday, December 16, 2013

File System Watcher Integrate with Windows Workflow Foundation

Today I want to share how to integrate file system watcher in workflow. There are a few ways to do it. Option 1 (simple way) is whenever there is a file is dropped into a folder, the file system watcher will kick in and spawn a workflow instance to process your file. Option 2 (hard way) is to integrate file system watcher into the workflow which mean you need just 1 workflow instance to wait for file watcher event fire then process the file. I want to share the hard way.

Concept

Here is the flow of the concept. Picture is worth a thousand words.


Challenges

In order to implement the above flow, I need a custom workflow activity to handle the logic. I chose to use NativeActivity is because I need to use the bookmarking feature. Bookmarking can induce idle, I want to keep my workflow instance idle state whenever there is no file exists in the folder.

Next challenge is whenever an instance has gone idle, the activity context will be different when the file system watcher event kick in to wake the instance to resume the process. Therefore, I need a custom workflow instance extension to handle the bookmark resume by implementing IWorkflowInstanceExtension.

Implementation

First, prepare the custom workflow instance extension which can support the resume bookmark functionality.

public class FileReceiveExtension : IWorkflowInstanceExtension
{
    private WorkflowInstanceProxy _proxy;

    public IEnumerable<object> GetAdditionalExtensions()
    {
        return null;
    }

    public void SetInstance(WorkflowInstanceProxy instance)
    {
        _proxy = instance;
    }

    public void ResumeBookmark(Bookmark bookmark, FileInfo file)
    {
        IAsyncResult result = _proxy.BeginResumeBookmark(bookmark, file,
            (asyncResult) =>
            {
                    
            }, null);
    }
}

Next, create a custom NativeActivity then add the custom workflow instance extension into the activity cache metadata.

protected override void CacheMetadata(NativeActivityMetadata metadata)
{
    metadata.AddDefaultExtensionProvider<FileReceiveExtension>(() => new FileReceiveExtension());
    base.CacheMetadata(metadata);
}

Set the CanInduceIdle property value to true.

protected override bool CanInduceIdle
{
    get
    {
        return true;
    }
}


Write the custom activity implementation to create file system watcher and register the necessary event handler.

protected override void Execute(NativeActivityContext context)
{
    //Get the folder path from the activity parameter
    string monitoringPath = context.GetValue(this.MonitoringPath);

    FileSystemWatcher watcher = new FileSystemWatcher();
    watcher.Path = monitoringPath;
    watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
    watcher.Created += watcher_Created;
    watcher.Error += watcher_Error;
    watcher.EnableRaisingEvents = true;

    _extension = context.GetExtension<FileReceiveExtension>();
    _bookmark = context.CreateBookmark(BookmarkResumed);
}

Write the implementation of file system watcher created event.

private void watcher_Created(object sender, FileSystemEventArgs e)
{
    //Resume the bookmark and pass the FileInfo object as activity parameter
    _extension.ResumeBookmark(_bookmark, new FileInfo(e.FullPath));
}

Write the implementation of bookmark resume event.

private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value)
{
    FileInfo file = (FileInfo)value;

    //Call your own file processor method
    //Note: I am using async for better performance (Optional)
    Task task = _fileProcessor.ProcessAsync(file);

    task.ContinueWith((e) =>
    {
        if (e.IsFaulted)
        {
            Console.WriteLine(e.Exception);
        }
    });

    //Create a new bookmark immediately after a file is being processed
    _bookmark = context.CreateBookmark(BookmarkResumed);
}

Draw the workflow with the new custom activity. The Start and Stop receive activities are used to control my file system watcher process by making service call.



Start hosting the workflow service, then call the Start web method to test it out. You only need to call the method once to spawn one workflow instance only. Since I had created the custom file receive activity that accept monitoring path, you can spawn another workflow instance to have another instance of file system watcher which monitor different file path.


If you are interested with the source code, feel free to download it from HERE.



Friday, June 7, 2013

Windows Store App - Calendar Control (XAML)

Recently I have been researching and playing around with the Windows Store App development for the Windows 8 and RT. I am used to develop enterprise solution or business application and I thought of may be creating something useful that can be used with a tablet PC. When I was playing with the Windows Store App UI and the available controls, I realize that the most important or commonly used controls such as Calendar, DateTime Picker, GridView, etc are not available in Windows Store App. :(

Therefore, I have no choice but to create my own Calendar control. And, yeah, I know there are 3rd party Calendar controls available such as Telerik but I just want to try to create one by my own. I made it and today I want to share about the making of custom Calendar control in XAML for Windows Store App. This is how my Calendar control look like:



Concept

The logic of the calendar construction is to construct the previous month date boxes first. If the last day of the previous month is on Saturday (last day of the week), then skip making date boxes, otherwise create number of boxes until before the first day of current month.

Then, start appending the current month date boxes and if the row contain 7 boxes, then create a new row and then append more boxes until the last day of the month.

Finally, append the remaining boxes start with the first day of next month until the last day of the week.


Implementation

So, how to implement the above concept?
First, create a UserControl for the Calendar control and design the grid in such way:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
   
    <!-- Header -->
    <Rectangle Grid.Row="0" Grid.ColumnSpan="3"
                Style="{StaticResource CalendarHeaderBox}" />
    <TextBlock Grid.Column="1" Name="CurrentDateText"
                Style="{StaticResource CalendarHeader}" />
   
    <!-- Navigation Button -->
    <Button Name="PreviousButton" Grid.Column="0" Content="&lt;"
            HorizontalAlignment="Left" Margin="20"
            Tapped="PreviousButton_Tapped" />
    <Button Name="NextButton" Grid.Column="2" Content="&gt;"
            HorizontalAlignment="Right" Margin="20"
            Tapped="NextButton_Tapped" />
   
    <!-- Calendar Grid -->
    <Grid Grid.Row="1" Grid.ColumnSpan="3" Name="CalendarGrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
    </Grid>

</Grid>


And then, create another UserControl for the individual box of the day.

<Grid Name="ItemBox" Style="{StaticResource CalendarItemBox}">
    <Rectangle Stroke="Gainsboro" StrokeThickness="1" ></Rectangle>
    <TextBlock Name="ItemValue" Text="1" Style="{StaticResource CalendarItem}" />

</Grid>


This box user control contain very simple logic which is to display the box background color and text after the user control is loaded.

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
    ItemValue.Text = this.Text;
    ItemBox.Style = this.GridStyle;

    if (this.Value.CompareTo(DateTime.Today) == 0)
        ItemBox.Background = new SolidColorBrush(Colors.Orange);

}


Now back to the Calendar user control, create a method to display the day of the week (the blue color header boxes as you see from above screenshot). I will just skip the detail, and below is the code snippet of my logic to form the calendar with the boxes (user controls). If you want to see the full detail, scroll down to the end of this post and download my source code.

The calendar formation is divided into 3 parts: Previous Month + Current Month + Next Month.

Before that, I need to create a new event handler to handle the value change after the user click at the the box. Also, I need to create the properties to store the current calendar viewing month value and the selected date value.

public DateTime CurrentDate { get; set; }
public DateTime SelectedDate { get; set; }

private event EventHandler<TappedRoutedEventArgs> _selectionChange;
public event EventHandler<TappedRoutedEventArgs> SelectionChange
{
    add
    {
        _selectionChange += value;
    }
    remove
    {
        _selectionChange -= value;
    }
}

public void OnSelectionChange(object sender, TappedRoutedEventArgs e)
{
    if (_selectionChange != null)
        _selectionChange(sender, e);

}

Next, create the methods to form the Calendar.

This method is to construct the previous month calendar.
private void InitializePreviousMonthBoxes()
{
    DateTime previousMonthDate = this.CurrentDate.AddMonths(-1);
    DateTime previousMonthDateIteration = new DateTime(previousMonthDate.Year, previousMonthDate.Month, DateTime.DaysInMonth(previousMonthDate.Year, previousMonthDate.Month));
    CalendarGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });

    for (int dayOfWeek = (int)previousMonthDateIteration.DayOfWeek; dayOfWeek >= 0; dayOfWeek--)
    {

        CalendarItem item = new CalendarItem(previousMonthDateIteration, previousMonthDateIteration.Day.ToString(), Application.Current.Resources["CalendarOtherMonthItemBox"] as Style);
        item.PointerEntered += (sender, args) =>
        {
            ((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarMouseOverItemBox"] as Style;
        };

        item.PointerExited += (sender, args) =>
        {
            if (((CalendarItem)sender).Value == this.SelectedDate)
                ((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarSelectedItemBox"] as Style;
            else
                ((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarOtherMonthItemBox"] as Style;
        };

        //delegate the tapped event to selection change event
        item.Tapped += (sender, args) =>
        {
            //update the selected date value
            this.SelectedDate = ((CalendarItem)sender).Value;
            this.CurrentDate = this.CurrentDate.AddMonths(-1);
            InitializeCalendar();

            OnSelectionChange(sender, args);
        };

        item.SetValue(Grid.RowProperty, 1);
        item.SetValue(Grid.ColumnProperty, dayOfWeek);

        CalendarGrid.Children.Add(item);

        previousMonthDateIteration = previousMonthDateIteration.AddDays(-1);
    }

}


This method is to construct current month calendar.

private void InitializeCurrentMonthBoxes()
{
    int row = 1;
    int maxDay = DateTime.DaysInMonth(this.CurrentDate.Year, this.CurrentDate.Month);
    for (int day = 1; day <= maxDay; day++)
    {
        DateTime dateIteration = new DateTime(this.CurrentDate.Year, this.CurrentDate.Month, day);
        int dayOfWeek = (int)dateIteration.DayOfWeek;

        CalendarItem item = new CalendarItem(dateIteration, day.ToString(), Application.Current.Resources["CalendarItemBox"] as Style);
        item.PointerEntered += (sender, args) =>
        {
            ((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarMouseOverItemBox"] as Style;
        };

        item.PointerExited += (sender, args) =>
        {
            if (((CalendarItem)sender).Value == this.SelectedDate)
                ((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarSelectedItemBox"] as Style;
            else
                ((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarItemBox"] as Style;
        };

        //delegate the tapped event to selection change event
        item.Tapped += (sender, args) =>
        {
            //get the box day value
            ((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarSelectedItemBox"] as Style;

            //get the box before selected value and reset the color
            var selectedItem = (CalendarItem)CalendarGrid.Children.Single(x =>
                                    x.GetType() == typeof(CalendarItem) &&
                                    ((CalendarItem)x).Value == this.SelectedDate);

            selectedItem.GridStyle = Application.Current.Resources["CalendarItemBox"] as Style;

            //update the selected date value
            this.SelectedDate = ((CalendarItem)sender).Value;

            OnSelectionChange(sender, args);
        };

        //highlight selected date
        if (this.SelectedDate.CompareTo(dateIteration) == 0)
            item.GridStyle = Application.Current.Resources["CalendarSelectedItemBox"] as Style;

        item.SetValue(Grid.RowProperty, row);
        item.SetValue(Grid.ColumnProperty, dayOfWeek);

        CalendarGrid.Children.Add(item);

        if (dayOfWeek == 6 && day != maxDay)
        {
            row++;
            CalendarGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
        }
    }

}

This method is to construct next month calendar.

private void InitializeNextMonthBoxes()
{
    DateTime nextMonthDate = this.CurrentDate.AddMonths(1);
    DateTime nextMonthDateIteration = new DateTime(nextMonthDate.Year, nextMonthDate.Month, 1);

    int lastRow = CalendarGrid.RowDefinitions.Count - 1;

    if (nextMonthDateIteration.DayOfWeek != DayOfWeek.Sunday)
        for (int dayOfWeek = (int)nextMonthDateIteration.DayOfWeek; dayOfWeek < 7; dayOfWeek++)
        {
            CalendarItem item = new CalendarItem(nextMonthDateIteration, nextMonthDateIteration.Day.ToString(), Application.Current.Resources["CalendarOtherMonthItemBox"] as Style);
            item.PointerEntered += (sender, args) =>
            {
                ((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarMouseOverItemBox"] as Style;
            };

            item.PointerExited += (sender, args) =>
            {
                if (((CalendarItem)sender).Value == this.SelectedDate)
                    ((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarSelectedItemBox"] as Style;
                else
                    ((CalendarItem)sender).GridStyle = Application.Current.Resources["CalendarOtherMonthItemBox"] as Style;
            };

            //delegate the tapped event to selection change event
            item.Tapped += (sender, args) =>
            {
                //update the selected date value
                this.SelectedDate = ((CalendarItem)sender).Value;
                this.CurrentDate = this.CurrentDate.AddMonths(1);
                InitializeCalendar();

                OnSelectionChange(sender, args);
            };

            item.SetValue(Grid.RowProperty, lastRow);
            item.SetValue(Grid.ColumnProperty, dayOfWeek);

            CalendarGrid.Children.Add(item);

            nextMonthDateIteration = nextMonthDateIteration.AddDays(1);
        }

}

In the end, you need to execute these above 3 methods to form a complete calendar of the month. Finally, use the above custom control in the page like this with correct namespace:

<Page
    x:Class="CalendarControl.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CalendarControl"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:custom="using:CalendarControl.CustomControl"
    mc:Ignorable="d">

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBox Grid.Row="0" Name="SelectedDateText"
                 Width="1024" Margin="10" />
        <custom:Calendar Grid.Row="1" x:Name="MyCalendar"
                            Width="1024"
                            Height="768"
                            HorizontalAlignment="Center"
                            VerticalAlignment="Center"
                            Margin="10" SelectionChange="Calendar_SelectionChange" />
    </Grid>

</Page>


Then, how to get the selected date value? Just use the following code.

private void Calendar_SelectionChange(object sender, TappedRoutedEventArgs e)
{
    //if you get the out of context error, make sure to use the namespace x:
    //in your XAML - x:Name
    SelectedDateText.Text = MyCalendar.SelectedDate.ToString("yyyy-MM-dd");

}


If you are interested with my source code, feel free to download from HERE.
Customize the Calendar for your own purpose and please let me know or drop me a comment if you detect any bug with the Calendar control.

My next post is going to be DateTime Picker for Windows Store App (XAML). Stay tuned.




Send Transactional SMS with API

This post cover how to send transactional SMS using the Alibaba Cloud Short Message Service API. Transactional SMS usually come with One Tim...