Saturday 6 December 2014

VBA Download Files

With nothing but an excel spreadsheet which contained a file url and the name it had to be changed to i needed to download 78 files and rename them. not something i'm going to do manually and did i mention Powershell was not an option, well I ended up falling back on the age old macro.

Sub DownloadFiles()
    Dim rng As Range
    Set rng = Application.Selection
    
    Dim ir As Integer
    
    For ir = 1 To rng.Rows.Count
        Dim ext As String
        Dim fileName As String
        Dim myURL As String
        
        'Extract url of file
        myURL = rng.Rows(ir).Hyperlinks(1).Address
        
        'Get file name from s column of corresponding row to selection
        fileName = Range("S" & rng.Rows(ir).Row)
        
        'Get file extension from en
        ext = Right(myURL, Len(myURL) - InStrRev(myURL, ".") + 1)

        'check to make sure file needs to be migrated
        If (Range("T" & rng.Rows(ir).Row) = "Yes") Then
       
       
            Debug.Print fileName & ext
            Dim WinHttpReq As Object
            Set WinHttpReq = CreateObject("Microsoft.XMLHTTP")
            WinHttpReq.Open "GET", myURL, False, "User", "Password"
            WinHttpReq.send
    
            myURL = WinHttpReq.responseBody
            If WinHttpReq.Status = 200 Then
                Set oStream = CreateObject("ADODB.Stream")
                oStream.Open
                oStream.Type = 1
                oStream.Write WinHttpReq.responseBody
                oStream.SaveToFile "C:\Files\" + fileName + ext, 1 ' 1 = no overwrite, 2 = overwrite
                oStream.Close
            End If
        End If
    Next
End Sub

and that's it, never thought i'd be writing with the Basic syntax, but here i am.

Saturday 29 November 2014

Settings flyout

the Settings flyout is a mechanism where to place navigation to pages that my not be selected frequently for example anything such as a settings page, to an about page etc

to get to the settings page you can swipe from the right or press the windows+c key to open the charms menu, there hit the settings icon. this will open the settings pane

By default not a lot going on here, but before we add our on links here what you need to understand is that when we add a link to the settings pane we do it through out the entire app; hence we have to ensure that our application only adds content to the settings pane once, more on this later for now open up your app.xaml.cs file.

find the onlaunched method, once you have it go to the very end and add your settings logic

protected override void OnLaunched(LaunchActivatedEventArgs e)
{

#if DEBUG
    if (System.Diagnostics.Debugger.IsAttached)
    {
        this.DebugSettings.EnableFrameRateCounter = true;
    }
#endif

    Frame rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == null)
    {
        // Create a Frame to act as the navigation context and navigate to the first page
        rootFrame = new Frame();
        // Set the default language
        rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];

        rootFrame.NavigationFailed += OnNavigationFailed;

        if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            //TODO: Load state from previously suspended application
        }

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;
    }

    if (rootFrame.Content == null)
    {
        // When the navigation stack isn't restored navigate to the first page,
        // configuring the new page by passing required information as a navigation
        // parameter
        rootFrame.Navigate(typeof(MainPage), e.Arguments);
    }
    // Ensure the current window is active
    Window.Current.Activate();

    // Settings logic        
    var settings = SettingsPane.GetForCurrentView();

    settings.CommandsRequested += (s, arg) =>
    {
        var about = new SettingsCommand("0", "About", HandleSettings());
        arg.Request.ApplicationCommands.Add(about);

        var options = new SettingsCommand("1", "Options", HandleSettings());
        arg.Request.ApplicationCommands.Add(options);
    };

}

with that complete create your HandleSettings function

private UICommandInvokedHandler HandleSettings()
{
    return new UICommandInvokedHandler(async cmnd =>
    {
        switch (cmnd.Id.ToString())
        {
            case "0":
                await new MessageDialog(cmnd.Label).ShowAsync();
                break;
            case "1":
                await new MessageDialog(cmnd.Label).ShowAsync();
                break;
        }
    });

}

now there's numerous ways to skin that cat, but this is just the way I like to do it, you can avoid the switch statement and just create a separate handler for each setting, it's really your call.

Now when you open up your settings view you'll see our freshly added About and Options.

again something to note, if you're ever find yourself in the situation where you have duplicate custom options added to your settings pane that means that you or someone you work with put the logic at the page level instead of the app level and every time that page is navigated to the settings are being added to the settings pane.

Sunday 16 November 2014

Simple Webcam Video

Previously we used the CameraCaptureUI class to call a camera UI take a picture and pass it back to our application, this time lets do the same thing but with video instead.

to get started we need to as before we need to define our capabilities


Notice that as before we have webcam checked off, but we also included the webcam capability, this is a must, if you forget you're video capture wont work and the your app will let know it requires your permission.

next let's set up our UI

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

    <Grid  Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Button x:Name="Record_BTN" Content="Record"/>
        <MediaElement Margin="100" x:Name="VideoPlayer" Grid.Column="1" />
    </Grid>

</Page>

just as before with the exceptiont that we're using our MediaElement instead of our image object, because of the fact that we have to show our video.

next lets' set up our codebehind

using System;
using Windows.Media.Capture;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace pc.Media.Video
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.Record_BTN.Click += Record_BTN_Click;
        }

        async void Record_BTN_Click(object sender, RoutedEventArgs e)
        {
            var camera = new CameraCaptureUI();

            //wait for the image
            StorageFile video = await camera.CaptureFileAsync(CameraCaptureUIMode.Video);

            //Check if it was sent back
            if (video != null)
            {
                // open a stream to our sotrage file aka our video
                IRandomAccessStream stream = await video.OpenAsync(FileAccessMode.Read);
                VideoPlayer.SetSource(stream, video.ContentType);
            }
            else
            {
                await new MessageDialog("No Video recieved").ShowAsync();
            }
        }
    }
}


just as before we pass our video capture off to the CameraCaptureUI and it comes back with our storage file which we can manipulate however we please.



Saturday 15 November 2014

Printing

Using the Print contract requires Seven main steps:
  • A reference to a PrintManager instance for each view that you want users to be able to print.
  • Implement a PrintTask instance representing the actual printing operation.
  • Create a PrintDocument instance to hold a reference to the content that you want to print and handle the events raised during the printing process.
    • Calculate how many pages you need and distribute the content among them
    • Render a preview
    • Print your pages
  • Finally you have to deference all of the events you made before navigating away from the page.
To get started let's create a simple UI

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

    <Grid x:Name="MainLayout" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel Margin="0 100">
        <TextBox x:Name="Data_TextBox" Height="500" TextWrapping="Wrap" 
                 Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce in metus dui, a scelerisque neque. Morbi eget sapien lectus, hendrerit semper orci. Donec elit sem, pharetra in ornare ac, dictum sed massa. Donec rhoncus consequat urna. Sed non enim ut quam aliquet adipiscing. Ut a enim a sem blandit lobortis. Donec non volutpat orci. In at massa nunc, vel lobortis leo. Fusce vel erat sit amet justo sollicitudin varius" />
            <Button Content="Click" Click="Print_BTN_Click" />
        </StackPanel>
    </Grid>   
</Page>

With that done lets take a look at our codebehind, I've gone ahead and added some of the initital steps

using System;
using Windows.Graphics.Printing;
using Windows.UI.Xaml.Controls;

namespace pc.print
{
    public sealed partial class MainPage : Page
    {
        PrintManager _printManager;
        PrintDocument _printDocument; 

        public MainPage() {
            this.InitializeComponent();

            _printDocument = new PrintDocument();
           
            _printManager = PrintManager.GetForCurrentView();
            _printManager.PrintTaskRequested += PrintManager_PrintTaskRequested;
        }

        private void PrintManager_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args) {
            throw new NotImplementedException();
        }



        //Show printUI
        async void Print_BTN_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            await PrintManager.ShowPrintUIAsync();

        }
    }
}

Above we referenced the print manager and created a PrinterTask, now to test this we run our app and from the charms bar we selected Devices->Print; now this will fire our PrinteTaskRequested event which we subscribted the PrinterManager_PrintTaskRequested event handler, so this block will be the one that fires. Currently it just throws an exception, but let's change that

//print UI called
void PrintManager_PrintTaskRequested(PrintManager sender,
                                    
PrintTaskRequestedEventArgs args)
{
    PrintTask pt = args.Request.CreatePrintTask("Test", async taskArgs =>
    {  
        //create a deferral, because the document to print can only be set on the UI thread
        var deferral = taskArgs.GetDeferral();

        await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            taskArgs.SetSource(_printDocument.DocumentSource);
            deferral.Complete();
        });
    });

}

We create a printer task and grab a deferral, this is because the document to print can only be set on the UI thread, now at this point we don't accomplish very much at all, but we do get the PrintUI to show up.

No preview but at least we're heading in the right direction, so lets get this thing printing, because lets face it a preview is nice, but printing is a tad more important, don't worry we'll go back and set the preview up but for now lets get something on paper.

what we are going to have to do is create a page level element called PrintDocument and attach a handler to deal with passing pages to the printing API, the one restriction is that whatever we send to print has to inherit form FrameworkElement, which is just about every UI Element.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Windows.Graphics.Printing;
using Windows.UI;
using Windows.UI.Core;
using Windows.UI.Popups;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Printing;

namespace pc.print
{
    public sealed partial class MainPage : Page
    {
        PrintManager _printManager;
        PrintDocument _printDocument;

        public MainPage()
        {
            this.InitializeComponent();

            _printDocument = new PrintDocument();
            _printDocument.AddPages += _printDocument_AddPages;

            _printManager = PrintManager.GetForCurrentView();
            _printManager.PrintTaskRequested += PrintManager_PrintTaskRequested;
        }

        //Show printUI
        async void Print_BTN_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            await PrintManager.ShowPrintUIAsync();
        }

        //print UI called
        void PrintManager_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
        {
            PrintTask pt = args.Request.CreatePrintTask("Test", async taskArgs =>
            {
                //create a deferral, because the document to print can only be set on
                //the UI thread
                var deferral = taskArgs.GetDeferral();

                await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                {
                    taskArgs.SetSource(_printDocument.DocumentSource);
                    deferral.Complete();
                });
            });
        }

        //print command fired
        void _printDocument_AddPages(object sender, AddPagesEventArgs e)
        {
            //add the pages that will be printed, in this case a TextBlock that contains our Data
            _printDocument.AddPage(new TextBlock
            {
                Foreground = new SolidColorBrush(Colors.Black),
                Text = this.Data_TextBox.Text,
                FontSize = 75,
                TextWrapping = TextWrapping.Wrap
            });
            _printDocument.AddPagesComplete();
        }
    }
}


now Lets take a look at our last event handler, the AddPages one, so the print manager does the printing and the print document is what's printed. We have to add pages to our pint document, this event is raised once we hit the print button on the printUI (seen earlier). now if you hit the print button, you'll see a toast notification with your file saved (if your using xps)

if you click on the toast, you'll see you'r file printed (saved in xps viewer)

It looks awful, and it's cut off at the bottom and we have no preview, but at least we're printing.

so lets' try getting our preview to work; it's actually fairly easy first off in our page initialization we have to give the PrintDoucment's event GetPreviewPage a handler

public MainPage()
{
    this.InitializeComponent();

    _printDocument = new PrintDocument();
    //Added Preview Event Handler
    _printDocument.GetPreviewPage += _printDocument_GetPreviewPage;
    _printDocument.AddPages += _printDocument_AddPages;

    _printManager = PrintManager.GetForCurrentView();
    _printManager.PrintTaskRequested += PrintManager_PrintTaskRequested;

}

with that done we create the handler

//generate preview for print
void _printDocument_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
    //set the preview element
    _printDocument.SetPreviewPage(e.PageNumber, new TextBlock
    {
        Foreground = new SolidColorBrush(Colors.Black),
        Text = this.Data_TextBox.Text,
        FontSize = 75,
        TextWrapping = TextWrapping.Wrap
    });

}
now when we select our device from the print UI, we get an actual preview


however as you see above we're duplicating code, which is never a good thing, we're basically creating the same TextBlock to preview as we do to print, now we could very well create a page level text block and build it in the preview and then reuse it for the add pages method, which we will, but not till we handle this paging issue first.

before we get stated know up front that this will not be a demonstration in an optimal sizing strategy, this is just to demonstrate creating multiple pages for printing. so firstly let's create a page level list of TextBlocks that we can use for our preview and for our print methods, once that's done lets' add the pagination event to our PrintDocument object.

PrintManager _printManager;
PrintDocument _printDocument;
List<TextBlock> _tb;

public MainPage()
{
    this.InitializeComponent();

    _printDocument = new PrintDocument();
    _printDocument.Paginate += _printDocument_Paginate;
    _printDocument.GetPreviewPage += _printDocument_GetPreviewPage;
    _printDocument.AddPages += _printDocument_AddPages;

    _printManager = PrintManager.GetForCurrentView();
    _printManager.PrintTaskRequested += PrintManager_PrintTaskRequested;

}

with that done let's take a look at our event handler.

//Printer selected
void _printDocument_Paginate(object sender, PaginateEventArgs e)
{
    //get printer details, page widht/height, etc
    PrintTaskOptions printingOptions = e.PrintTaskOptions;
    PrintPageDescription pageDescription = printingOptions.GetPageDescription(0);

    //load all text data into our virtual textblock to print
    var page = new TextBlock
    {
        Foreground = new SolidColorBrush(Colors.Black),
        Text = this.Data_TextBox.Text,
        FontSize = 95,
        TextWrapping = TextWrapping.Wrap
    };

    //Create page boundries
    var size = new Size(pageDescription.PageSize.Width, pageDescription.PageSize.Height);

    //recursive function to spread text data over multiple TextBlocks
    _tb = ParsePages(page, new List<TextBlock>(), size);

    //set how many pages in the preview
    _printDocument.SetPreviewPageCount(_tb.Count, PreviewPageCountType.Intermediate);

}

lets dig into our parse pages function

List<TextBlock> ParsePages(TextBlock current, List<TextBlock> result, Size pageSize)
{
    //allow us to get our currnet textblocks actualHeight with text filled in
    current.Measure(pageSize);

    //check if there's an overflow
    if (current.ActualHeight - pageSize.Height <= 0)
    {
        result.Add(current);
        return result;
    }

    //push the overflow to another textblock
    var overflow = new TextBlock
    {
        Foreground = new SolidColorBrush(Colors.Black),
        FontSize = 95,
        TextWrapping = TextWrapping.Wrap,
        Text = current.Text.Substring(100)
    };

    //restrict the current page to the first 100 chars
    current.Text = current.Text.Substring(0, 100);

    //add the current page to the result set
    result.Add(current);

    //call the function again
    return ParsePages(overflow, result, pageSize);

}

with that done lets take a look at our renderPreview Method once again

//generate preview for print
void _printDocument_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
    //set the preview element
    var count = e.PageNumber;
    foreach (var p in _tb)
        _printDocument.SetPreviewPage(count++, p);

}

with that complete let's take a look at our actual print method

//print command fired
void _printDocument_AddPages(object sender, AddPagesEventArgs e)
{
    //add the pages that will be printed
    foreach (var p in _tb)
    {
        var txt = p.Text;
        _printDocument.AddPage(p);
    }
    _printDocument.AddPagesComplete();

}

and that's it, you're not printing a multi page document.


One final thought, if you'r application nagivates away from this page make sure to de-reference all of the event handlers you referenced in the constructor for the PrintDocument and PrintManager.