Quickstart: Scanning (XAML)

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

You can scan from your Windows Store app or desktop app built in Windows 8.1.

For your app to scan, you must list the available scanners so your app can communicate with the devices, and then choose the type of scanning you want. How your app scans depends on the available scanners.

Watch this video for a quick overview on how to add scanning from your app.

Prerequisites

  • You must be familiar with C++ or C#, and XAML.
  • You must have Microsoft Visual Studio 2013 installed.
  • You must be connected to a scanner.
  • You must have an app that you want to scan from. If you don't have your own app, you can download the Scan Sample app and use that. Note  The examples here are found in the Scan Sample app.  

Open your app's source code for editing

This procedure describes how to open the ScanRuntimeAPI app from the Scan Sample app. If you are using your own app, open it in Visual Studio 2013 and skip to the next step.

  1. Open the Scan Sample app and download the C# or C++ example to your computer.
  2. In Visual Studio 2013, click File > Open Project and navigate to the folder that contains the solution file of the sample app that you downloaded in the previous step.
  3. Select the ScanRuntimeAPI solution file, and click Open.

Enumerate available scanners

Windows does not detect scanners automatically. You must perform this step in order for your app to communicate with the scanner. In this example, the scanner device enumeration is done using the Windows.Devices.Enumeration namespace.

  1. First, add these using statements to your class definition file.

    using namespace Windows::Devices::Enumeration;
    using namespace Windows::Devices::Scanners;
    
    using Windows.Devices.Enumeration;
    using Windows.Devices.Scanners;
    
  2. Next, implement a device watcher to start enumerating scanners. For more info, see How to get notifications if devices are added, removed, or changed.

    void ScannerContext::InitScannerWatcher()
    {
        // Create a Device Watcher class for type Image Scanner for enumerating scanners
        watcher = DeviceInformation::CreateWatcher(DeviceClass::ImageScanner);
    
        // Register for added, removed and enumeration completed events
        watcher->Added += ref new TypedEventHandler<DeviceWatcher^, DeviceInformation^>(
            this, &ScannerContext::OnScannerAdded);
        watcher->Removed += ref new TypedEventHandler<DeviceWatcher^, DeviceInformationUpdate^>(
            this, &ScannerContext::OnScannerRemoved);
        watcher->EnumerationCompleted += ref new TypedEventHandler<DeviceWatcher^,Object^>(
            this, &ScannerContext::OnScannerEnumerationComplete);
    }
    
            void InitDeviceWatcher()
            {
                // Create a Device Watcher class for type Image Scanner for enumerating scanners
                scannerWatcher = DeviceInformation.CreateWatcher(DeviceClass.ImageScanner);
    
                scannerWatcher.Added += OnScannerAdded;
                scannerWatcher.Removed += OnScannerRemoved;
                scannerWatcher.EnumerationCompleted += OnScannerEnumerationComplete;
            }
    
  3. Create an event handler for when a scanner is added.

    void ScannerContext::OnScannerAdded(_In_ WDE::DeviceWatcher^ sender, _In_ WDE::DeviceInformation^ deviceInfo)
    {
        MainPage::Current->Dispatcher->RunAsync(
            CoreDispatcherPriority::Normal,
            ref new DispatchedHandler(
                [this, deviceInfo] () -> void 
                {
                    MainPage::Current->NotifyUser(
                        "Scanner with device id " + deviceInfo->Id + " has been added", 
                        NotifyType::StatusMessage);
    
            private async void OnScannerAdded(DeviceWatcher sender,  DeviceInformation deviceInfo)
            {
                await
                MainPage.Current.Dispatcher.RunAsync(
                    Windows.UI.Core.CoreDispatcherPriority.Normal,
                    () =>
                    {
                        MainPage.Current.NotifyUser(String.Format("Scanner with device id {0} has been added",
                            deviceInfo.Id), NotifyType.StatusMessage);
    
                        // search the device list for a device with a matching device id
                        ScannerDataItem match = FindInList(deviceInfo.Id);
    
                        // If we found a match then mark it as verified and return
                        if (match != null)
                        {
                            match.Matched = true;
                            return;
                        }
    
                        // Add the new element to the end of the list of devices
                        AppendToList(deviceInfo);
                    }
                );
            }
    

Scan

  1. Get an ImageScanner object

    For each ImageScannerScanSource enumeration type, whether it's Default, AutoConfigured, Flatbed, or Feeder, you must first create an ImageScanner object by calling the ImageScanner.FromIdAsync method, like this.

    create_task(ImageScanner::FromIdAsync(deviceId)).then([this, destinationFolder](ImageScanner^ myScanner)
    {
        // Scan using myScanner object.
        ...
    }
    
    ImageScanner myScanner = await ImageScanner.FromIdAsync(deviceId);
    
  2. Just scan

    To scan with the default settings, your app relies on the Windows.Devices.Scanners namespace to select a scanner and scans from that source. No scan settings are changed. The possible scanners are auto-configure, flatbed, or feeder. This type of scan will most likely produce a successful scan operation, even if it scans from the wrong source, like flatbed instead of feeder.

    Note  If the user places the document to scan in the feeder, the scanner will scan from the flatbed instead. If the user tries to scan from an empty feeder, the scan job won't produce any scanned files.

     

    IAsyncOperationWithProgress<ImageScannerScanResult^, UINT32>^ asyncOp = 
        myScanner->ScanFilesToFolderAsync(ImageScannerScanSource::Default, destinationFolder);
    
    var result = await myScanner.ScanFilesToFolderAsync(ImageScannerScanSource.Default, 
        folder).AsTask(cancellationToken.Token, progress);
    
  3. Scan from Auto-configured, Flatbed, or Feeder source

    Your app can use the device's Auto-Configured Scanning to scan with the most optimal scan settings. With this option, the device itself can determine the best scan settings, like color mode and scan resolution, based on the content being scanned. The device selects the scan settings at run time for each new scan job.

    Note  Not all scanners support this feature, so the app must check if the scanner supports this feature before using this setting.

     

    In this example, the app first checks if the scanner is capable of auto-configuration and then scans. To specify either flatbed or feeder scanner, simply replace AutoConfigured with Flatbed or Feeder.

    if (myScanner->IsScanSourceSupported(ImageScannerScanSource::AutoConfigured))
    {
        ...
        // Scan API call to start scanning.
        IAsyncOperationWithProgress<ImageScannerScanResult^, UINT32>^ asyncOp = 
            myScanner->ScanFilesToFolderAsync(ImageScannerScanSource::AutoConfigured, destinationFolder);
        ...
    }
    
    if (myScanner.IsScanSourceSupported(ImageScannerScanSource.AutoConfigured))
    {
        ...
        // Scan API call to start scanning with Auto-Configured settings. 
        var result = await myScanner.ScanFilesToFolderAsync(
            ImageScannerScanSource.AutoConfigured, folder).AsTask(cancellationToken.Token, progress);
        ...
    }
    

Preview the scan (optional)

You can add code to preview the scan before scanning to a folder. In the example below, the app checks if the Flatbed scanner supports preview, then previews the scan.

if (myScanner->IsPreviewSupported(ImageScannerScanSource::Flatbed))
{
    MainPage::Current->NotifyUser("Scanning", NotifyType::StatusMessage);    
    // Scan API call to get preview from the flatbed.
    return create_task(myScanner->ScanPreviewToStreamAsync(ImageScannerScanSource::Flatbed, stream));
    ...
}
if (myScanner.IsPreviewSupported(ImageScannerScanSource.Flatbed))
{
    rootPage.NotifyUser("Scanning", NotifyType.StatusMessage);
                // Scan API call to get preview from the flatbed.
                var result = await myScanner.ScanPreviewToStreamAsync(
                    ImageScannerScanSource.Flatbed, stream);

Cancel scanning (optional)

You can let users cancel the scan job midway through a scan, like this.

void ScenarioJustScan::CancelScanning()
{
    if (ModelDataContext->ScenarioRunning)
    {
        cancellationToken.cancel();
        DisplayImage->Source = nullptr;
        ModelDataContext->ScenarioRunning = false;
        ModelDataContext->ClearFileList();
    }
}
  void CancelScanning()
  {
      if (ModelDataContext.ScenarioRunning)
      {
          if (cancellationToken != null)
          {
              cancellationToken.Cancel();
          }                
          DisplayImage.Source = null;
          ModelDataContext.ScenarioRunning = false;
          ModelDataContext.ClearFileList();
      }
  }

Scan with progress (optional)

At high level, you may want to show the progress of the scan. Do this by setting up a progress event handler and getting a CancellationTokenSource class.

  1. Create a CancellationTokenSource object.

    cancellationToken = Concurrency::cancellation_token_source();
    
    cancellationToken = new CancellationTokenSource();
    
  2. Set up the progress event handler and get the progress of the scan.

    auto progress = ref new AsyncOperationProgressHandler<ImageScannerScanResult^, UINT32>
                        (this, &SDKSample::ScanRuntimeAPI::ScenarioDeviceAutoConfiguredScan::Progress);
        asyncOp->Progress = progress;                    
    
        return create_task(asyncOp, cancellationToken.get_token());
    
    rootPage.NotifyUser("Scanning", NotifyType.StatusMessage);
    var progress = new Progress<UInt32>(ScanProgress);
    

App capability declaration (optional)

Users can scan to any folder dynamically using the FolderPicker class, but you must declare the Pictures Library capability in the manifest to allow users to scan to that folder.

For more info on app capability, see App capability declarations.

Summary

In this page, you added the capability to scan from your app using C# or C++ and XAML. To add scanning to your app using JavaScript and HTML, see Scan from your app (Windows Store apps using JavaScript and HTML). Next, you might want to add printing to your app. For more info, see Printing.

Windows.Devices.Scanners namespace

Auto-Configured Scanning