Windows 10 - programming Cortana - windows

I am trying to programm a litte application for Cortana.
My idea is, that i say: (I enabled the "Hey Cortana" feature)
Hey Cortana, Convert 45 degrees to farenheit
and I get (in the moment) a log in my Output window (Visual Studio). I tried to say exact this sentence and Cortana understood me perfectly, but Cortana opened the browser and entered it into Bing.
Why? What did I do wrong? I don't get any Syntax Error.
This is my Code:
// commands.xml
<?xml version="1.0" encoding="utf-8"?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.1">
<CommandSet xml:lang="en-us" Name="MyCommands_en-us">
<CommandPrefix> Convert, </CommandPrefix>
<Example> Convert 45 degrees to farenheit </Example>
<Command Name ="farenheitToDegrees">
<Example> 73 farenheit to degrees</Example>
<ListenFor> {farenheit} farenheit to degrees </ListenFor>
<Feedback> {farenheit} are ... in degrees </Feedback>
<Navigate/>
</Command>
<Command Name="degreesToFarenheit">
<Example> 45 degrees to farenheit </Example>
<ListenFor> {degrees} degrees to farenheit </ListenFor>
<Feedback> {degrees} degrees are ... in fareneheit </Feedback>
<Navigate/>
</Command>
<PhraseTopic Label="degrees" Scenario="Dictation">
<Subject>Temperature</Subject>
</PhraseTopic>
<PhraseTopic Label="farenheit" Scenario="Dictation">
<Subject>Temperature</Subject>
</PhraseTopic>
</CommandSet>
</VoiceCommands>
// App.xaml.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.ApplicationModel.VoiceCommands;
using Windows.Storage;
using Windows.Media.SpeechRecognition;
namespace HelloWorld
{
/// <summary>
/// Provides application-specific behavior to supplement the default Application class.
/// </summary>
sealed partial class App : Application
{
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync(
Microsoft.ApplicationInsights.WindowsCollectors.Metadata |
Microsoft.ApplicationInsights.WindowsCollectors.Session);
this.InitializeComponent();
this.Suspending += OnSuspending;
}
/// <summary>
/// Invoked when the application is launched normally by the end user. Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected async 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();
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();
var storageFile =
await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///commands.xml"));
await
Windows.ApplicationModel.VoiceCommands.VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(storageFile);
}
protected override void OnActivated(IActivatedEventArgs e)
{
// Was the app activated by a voice command?
if (e.Kind != Windows.ApplicationModel.Activation.ActivationKind.VoiceCommand)
{
return;
}
var commandArgs = e as Windows.ApplicationModel.Activation.VoiceCommandActivatedEventArgs;
SpeechRecognitionResult speechRecognitionResult = commandArgs.Result;
// Get the name of the voice command and the text spoken
string voiceCommandName = speechRecognitionResult.RulePath[0];
string textSpoken = speechRecognitionResult.Text;
switch (voiceCommandName)
{
case "farenheitToDegrees":
string farenheit = speechRecognitionResult.SemanticInterpretation.Properties["farenheit"][0];
System.Diagnostics.Debug.WriteLine((Convert.ToInt32(farenheit) - 32) / 1.8);
break;
case "degreesToFarenheit":
string degrees = speechRecognitionResult.SemanticInterpretation.Properties["degrees"][0];
System.Diagnostics.Debug.WriteLine(Convert.ToInt32(degrees) * 1.8 + 32);
break;
default:
System.Diagnostics.Debug.WriteLine("None of my bussiness");
break;
}
}
/// <summary>
/// Invoked when Navigation to a certain page fails
/// </summary>
/// <param name="sender">The Frame which failed navigation</param>
/// <param name="e">Details about the navigation failure</param>
void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
{
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
}
/// <summary>
/// Invoked when application execution is being suspended. Application state is saved
/// without knowing whether the application will be terminated or resumed with the contents
/// of memory still intact.
/// </summary>
/// <param name="sender">The source of the suspend request.</param>
/// <param name="e">Details about the suspend request.</param>
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
}
}
can somebody help me?

The VCD definition file you've listed above doesn't have either a PhraseTopic or PhraseList to define the parts you've got in curly braces:
<ListenFor> {farenheit} farenheit to degrees </ListenFor>
I'm guessing you probably wanted a PhraseTopic because that allows for an unconstrained dictation suitable for a wide range of numbers, something like this:
<PhraseTopic Label="farenheit" Scenario="Dictation">
<Subject>Temperature</Subject>
</PhraseTopic>
See the spec for VCD's here on msdn, you might want to play with tweaking the Scenario value. This does mean you'll need to handle the text you get as the farenheit term yourself, of course, but typically dictated text for numbers comes through in textual '1234' form (but not in 100% of cases).

check the properties of your VCD file, values shoud be: Buil Action = Content, Copy to Output Directory = Copy always. Anyway, I hope that you registered the vcd file:
VoiceCommandService.InstallCommandSetsFromFileAsync(new Uri("ms-appx:///VCD.xml"));
Check this MVA video about Cortana: https://www.microsoftvirtualacademy.com/en-US/training-courses/universal-windows-app-development-with-cortana-and-the-speech-sdk-8487

Well... Seems that you have understood all the steps but still something is missing...
Here's an example that I have made regarding Cortana's foreground functionality:
Here's the VCD...
<?xml version="1.0" encoding="utf-8" ?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.2">
<CommandSet xml:lang="en-us" Name="HomeControlCommandSet_en-us">
<CommandPrefix>HomeControl</CommandPrefix>
<Example>Control alarm, temperature, light and others</Example>
<Command Name="Activate_Alarm">
<Example>Activate alarm</Example>
<ListenFor>[Would] [you] [please] activate [the] alarm [please]</ListenFor>
<ListenFor RequireAppName="BeforeOrAfterPhrase">Activate alarm</ListenFor>
<ListenFor RequireAppName="ExplicitlySpecified">Activate {builtin:AppName} alarm</ListenFor>
<Feedback>Activating alarm</Feedback>
<Navigate />
</Command>
After create this definitions, you need to registry it at App Startup:
protected async override void OnLaunched(LaunchActivatedEventArgs e)
{
...
// Install the VCD
try
{
StorageFile vcdStorageFile = await Package.Current.InstalledLocation.GetFileAsync(#"HomeControlCommands.xml");
await VoiceCommandDefinitionManager.InstallCommandDefinitionsFromStorageFileAsync(vcdStorageFile);
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("There was an error registering the Voice Command Definitions", ex);
}
}
An then override App.OnActivated method to handle when the events are triggered:
protected override void OnActivated(IActivatedEventArgs e)
{
// Handle when app is launched by Cortana
if (e.Kind == ActivationKind.VoiceCommand)
{
VoiceCommandActivatedEventArgs commandArgs = e as VoiceCommandActivatedEventArgs;
SpeechRecognitionResult speechRecognitionResult = commandArgs.Result;
string voiceCommandName = speechRecognitionResult.RulePath[0];
string textSpoken = speechRecognitionResult.Text;
IReadOnlyList<string> recognizedVoiceCommandPhrases;
System.Diagnostics.Debug.WriteLine("voiceCommandName: " + voiceCommandName);
System.Diagnostics.Debug.WriteLine("textSpoken: " + textSpoken);
switch (voiceCommandName)
{
case "Activate_Alarm":
System.Diagnostics.Debug.WriteLine("Activate_Alarm command");
break;
To see the complete tutorial, please visit this link and a working project is here. Also, if you interested in respond to the user through Cortana window, check this post regarding Cortana in background

I think you are missing a part of the command.
You're asking for Cortana to convert degrees to Fahrenheit, and the program is confused because you're not being specific enough.
If you want Cortana to covert from degrees Celsius to degrees Fahrenheit, you have to tell it specifically Celsius to Fahrenheit.

Related

Background Audio Playback Agent terminates during playback Windows Phone 7.5

So I have a problem with the Background Audio Playback agent in WP 7.5, where I think it is getting terminated by the OS randomly during playback.
I have an app which implements a BAP agent which plays a number of mp3 files based the selected chapter in UI. Each chapter has multiple verses, these verses have an associated mp3 file in isolated storage.
Once a chapter has been selected in the UI and the user presses the play button BackgroundAudio.Instance.Play() is called and the first verse (mp3 file) for that chapter is loaded as an AudioTrack. When the track has ended the next track is loaded in the OnPlayStateChanged event method under the TrackEnded state.
I also have some logic in the TrackEnded which checks if the end of the chapter has been reached (i.e. the last mp3 file for the current chapter has been played) and if so the first mp3 file for the next chapter will retrieved.
Now all the above works fine when using the Windows Phone 7 Emulator (both 512Mb and 256Mb Emulators), the mp3 files are played correctly and when the end of a chapter has been reached the next mp3 file for the next chapter is correctly loaded and played.
The problem I encounter is when I deploy this app to a Win 8 Device (Lumia 920), the audio starts playing fine and the suddenly and seemingly randomly the audio stops! No error messages, the app does NOT crash, just the Audio Stops playing. Also when I click on the UVC buttons on the device NO AudioTrack info is displayed as is the case during audio playback or audio has been paused (just the volume info shows).
I have no idea what’s going on, I think the OS may be terminating the Background Audio Playback agent but I have no idea why (I don’t think I am reaching any of the memory limitations but I can not confirm this as I don’t know how to check if I am).
Any advice/help will be appreciated.
Thanks
Update 14/01/14
To confirm that the memory limit of 15Mb(WP7) and 20Mb(WP8) was not being reached by my BAP I implemented some code which logged the current memory usage of the BAP at various stages through out its execution.
The memory usage does not reach anywhere near the limits imposed by the OS on the BAP, the peak i reach is 7Mb the issue I described above is still occurring, I can see from the log that the next track has already been set but the state Trackready is never hit, also no exceptions/errors are thrown. This has really stumped me!
Update 15/01/14
Below is an example of how I have implemented the BAP:
public AudioPlayer()
{
if (!_classInitialized)
{
_classInitialized = true;
// Subscribe to the managed exception handler
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
Application.Current.UnhandledException += AudioPlayer_UnhandledException;
});
lastPlayedVerse = currentVerseNumber;
}
}
/// Code to execute on Unhandled Exceptions
private void AudioPlayer_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
//Helper class to help log any exceptions
IsolatedStore.WriteToIS("unhandeled Ex: " + e.ExceptionObject.Message, IsolatedStore.MemLogFileName);
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
}
protected override void OnError(BackgroundAudioPlayer player, AudioTrack track, Exception error, bool isFatal)
{
//Helper class to help log any exceptions
IsolatedStore.WriteToIS("OnError Called: " + error.Message, IsolatedStore.MemLogFileName);
if (isFatal)
{
Abort();
}
else
{
NotifyComplete();
}
}
protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
{
switch (playState)
{
case PlayState.TrackEnded:
track = null;
IsolatedStore.AppendToFileIS(string.Format("Track Ended::Time: {0}",DateTime.Now.ToLongTimeString()), IsolatedStore.MemLogFileName);
#region Track Ended logic
//IN here I have some logic to determine what the next track should be and then I call a function that returns an AudioTrack
player.Track = GetNextTrack(); //this method returns an AudioTrack
#endregion
break;
case PlayState.TrackReady:
IsolatedStore.AppendToFileIS(string.Format("Track Ready::Time: {0}, Track: {1}", DateTime.Now.ToLongTimeString(),track.Title), IsolatedStore.MemLogFileName);
//Put this try catch in here becoz i thought that this is where the issue was (not sure if its needed as any exception should be caught by the AudioPlayer_UnhandledException function.
try
{
player.Play();
}
catch (Exception ex)
{
IsolatedStore.AppendToFileIS(string.Format("Track Ready play exception: {0}", ex.Message), IsolatedStore.MemLogFileName);
}
break;
}
NotifyComplete();
}
protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
{
switch (action)
{
case UserAction.Play:
if (player.PlayerState != PlayState.Playing)
{
IsolatedStore.AppendToFileIS(string.Format("UA-PLAY::Time: {0}, Track: {1}", DateTime.Now.ToLongTimeString(),track.Title), IsolatedStore.MemLogFileName);
player.Play();
}
break;
}
NotifyComplete();
}
private AudioTrack GetNextTrack(int audioType2Get, string filePath, int verserNum, bool incrementTrackCount)
{
#region Memusage
//Code to log the memory usage
long currMemUsage = (long)DeviceExtendedProperties.GetValue("ApplicationCurrentMemoryUsage");
currMemUsage = (currMemUsage / 1024) / 1024;
long peakMemUsage = (long)DeviceExtendedProperties.GetValue("ApplicationPeakMemoryUsage");
peakMemUsage = (peakMemUsage / 1024) / 1024;
IsolatedStore.AppendToFileIS(string.Format("Getting Track-Time: {0}, Curr:{1}, Track: {2}", DateTime.Now.ToLongTimeString(), currMemUsage, verserNum), IsolatedStore.MemLogFileName);
#endregion
AudioTrack track = null;
#region AudioTrack Set region
//Some logic to return the AudioTrack
#endregion
}
Update 24/01/2014 ISSUE RESOLVED
I finally got some time to get around to attempting what #Soonts recommended in the answer I marked as the answer, and firstly I'm using a WP8 device so I skipped the first setp that he mentioned, next i did as was mentioned in step 2 and again the max memory usage was only 8Mb.
Then a few days back there was an update for my WP8 device (WP8 Update 3), after I installed this update I attempted to reproduce the problem and guess what! The issue DOES NOT OCCUR ANY MORE!, I had my audio on continuous play for over an hour with no issues! also the memory usage was stable at around 8Mb. So it looks like there may have been a silent update to the BG Audio.
The reason why I marked #snoots answer as the answer was because he mentioned in that answer that the issue may be fixed by a silent update.
This may happen on unhandled exceptions. Subscribe for Application.Current.UnhandledException (and if you're using async-await for TaskScheduler.UnobservedTaskException) and log them somewhere. Also, override OnError method of your agent, and log.
This may happen if forget to call BackgroundAgent.NotifyComplete() after you've finished processing the requests (i.e. for player agent, the OnPlayStateChanged and OnUserAction). In this case, the OS consludes you was unable to process the request in a timely manner, and terminates the BAP process.
RAM issues, but you've figured it out.
P.S. Here's the relevant part of my Sky.fm player application. It doesn't play local MP3s , instead it streams the music from the Internets, however the player agent code should be more or less the same.
/// <summary>This class wraps AudioPlayerAgent API into the async-friendly abstract class.</summary>
/// <remarks>Logging and exception handling are added, as well.</remarks>
public abstract class PlayerAgentAsync: AudioPlayerAgent
{
static PlayerAgentAsync()
{
UnhandledExceptionHandler.subscribe();
}
public PlayerAgentAsync()
{
Logger.info( "constructed" );
}
protected override void OnError( BackgroundAudioPlayer player, AudioTrack track, Exception ex, bool isFatal )
{
if( isFatal )
{
BackgroundErrorNotifier.addError( ex );
ex.log();
Abort();
}
else
{
ex.logWarning();
try
{
// Force the track to stop
// http://blogs.msdn.com/b/wpukcoe/archive/2012/02/11/background-audio-in-windows-phone-7-5-part-3.aspx
player.Track = null;
}
catch (System.Exception ex2)
{
ex2.logWarning( "Exception while trying to stop da playa" );
}
NotifyComplete();
}
}
/// <summary>Called when the play state changes, except for the error state.</summary>
protected override async void OnPlayStateChanged( BackgroundAudioPlayer player, AudioTrack track, PlayState playState )
{
Logger.info( "new playState = {0}", playState.ToString() );
try
{
await this.playStateChangedAsync( player, track, playState ).ConfigureAwait( false );
NotifyComplete();
}
catch (System.Exception ex)
{
this.onException( ex );
}
}
/// <summary>Called when the user requests an action using some application-provided UI or the Universal Volume Control (UVC) and the application has requested notification of the action.</summary>
protected override async void OnUserAction( BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param )
{
Logger.info( "action = {0};", action.ToString() );
try
{
await this.userActionAsync( player, track, action, param ).ConfigureAwait( false );
NotifyComplete();
}
catch( System.Exception ex )
{
this.onException( ex );
}
}
private void onException( Exception ex )
{
if( ex.shouldBeIgnored() )
{
ex.logWarning();
this.NotifyComplete();
return;
}
ex.log();
BackgroundErrorNotifier.addError( ex );
this.Abort();
}
protected override void OnCancel()
{
Logger.trace();
base.OnCancel();
}
/// <summary>Handle OnPlayStateChanged asyncronously.</summary>
/// <param name="player">The Microsoft.Phone.BackgroundAudio.BackgroundAudioPlayer.</param>
/// <param name="track">The track playing at the time that the play state changed.</param>
/// <param name="playState">The new state of the player.</param>
protected abstract Task playStateChangedAsync( BackgroundAudioPlayer player, AudioTrack track, PlayState playState );
/// <summary>Handle OnUserAction asyncronously</summary>
/// <param name="player">The Microsoft.Phone.BackgroundAudio.BackgroundAudioPlayer.</param>
/// <param name="track">The track playing at the time of the user action.</param>
/// <param name="action">The action that the user has requested.</param>
/// <param name="param">The data associated with the requested action.</param>
protected abstract Task userActionAsync( BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param );
}
If I were you, I would probably do the following, in that order:
Ensure you have the latest OS updates. They have silently fixed some BAP-related memory issues in their 7.8 update. If you're not using the latest OS, upgrade and try to reproduce.
Place breakpoints in every place of your GUI process code that interacts with BAP, to make sure you’re not mistakenly calling e.g. BackgroundAudioPlayer.Stop() or BackgroundAudioPlayer.Track=null, try to reproduce.
Instead of “logging current memory usage of the BAP at various stages”, in static constructor of some agent class, create a thread that, in the endless loop, appends to some CSV file in the isolated storage, logging current timestamp + current memory , then sleeps for 500-1000 milliseconds. Reproduce the problem, use ISETool.exe to download the log, use Excel to build the RAM usage graph…
If (3) won’t show RAM usage approaching the limit, Implement tracing to either isolated storage or network (BTW, I’ve implemented a SysLog RFC 5426 client in my WP7 code, and installed free software called “SysRose Syslog Desktop” on my PC), then try to do printf-style debugging.

Catch opened word file event

I want to make an application as a word add-in that changes files when they are opened.
So I created a word add-in project in visual Studio, and this is basically the code I have :
namespace WordAddIn1
{
public partial class ThisAddIn
{
private void Application_DocumentOpen(Microsoft.Office.Interop.Word.Document Doc)
{
MessageBox.Show("doc opened");
// do my stuff
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Application.DocumentOpen += new Word.ApplicationEvents4_DocumentOpenEventHandler(Application_DocumentOpen);
}
#endregion
}
}
The problem is, this works well if you start an empty word application (double click word.exe), then open a document, but not if the word application is started together with the document opening (double click a .doc file).
The DocumentOpen won't fire if you open Word by double-clicking on a document.
To Work around this you can check if a document was opened in Word and if so pass the document to Application_DocumentOpen method.
BTW - You seem to have changed the code in the InternalStartup method. As indicated by the comments you should not do this, but instead uses the ThisAddIn_Startup.
Here's code to do what Abbey suggested:
private void ThisAddIn_Startup(object sender, System.EventArgs a)
{
try
{
Word.Document doc = this.Application.ActiveDocument;
if (String.IsNullOrWhiteSpace(doc.Path))
{
logger.Debug(String.Format("Word initialized with new document: {0}.", doc.FullName));
ProcessNewDocument(doc);
}
else
{
logger.Debug(String.Format("Word initialized with existing document: {0}.", doc.FullName));
ProcessOpenedDocument(doc);
}
}
catch (COMException e)
{
logger.Debug("No document loaded with word.");
}
}
You often will have the DocumentOpen event triggered before your AddIn is loaded and sometime before the document is actually loaded into the Word Interop API. This means that on ThisAddIn_Startup, WordApp.ActiveDocument still returns Null.
A trick is to schedule a Task (System.Func<Boolean>) in your ThisAddIn_Startup event: I use a Windows.Forms.Timer and that works quite well. On Timer.Tick, try to execute the Task. If it returns false, then try again 500ms later, and so on until it works.
My Task is:
If WordApp.ActiveDocument Is Nothing Then Return False
DoSomething()
Return True
The timer will keep trying until you have an active document, i.e. until you can do something with the document the user opened by double-click.
There are other ways to expose this same mechanism: you could for example wrap this into a WordStartup object that exposes a DocumentOpenBeforeAddInStartUp event and listen to this. You could also have a wrapper of all Document changes events and trigger your own DocumentChange event when either WordStartup.DocumentOpenBeforeAddInStartUp, WordApp.DocumentChange, WordApp.DocumentOpen or WordApp.NewDocument triggers.

Location aware code converted from C# to F#?

I have the following code in a WP7 app, and am starting to look at F#.. I can't find any GeoCoordinate examples, can anyone give me an idea of how this code would look in F#? Or point me to an example? I've had a look at some tutorials, books and Pluralsight, so think I am just starting to grasp the basics..but can't seem to get my head around this! All the examples I can seem to find are based around mathematical problem spaces. Any help or advice is much appreciated!
public partial class MainPage : PhoneApplicationPage
{
GeoCoordinateWatcher watcher;
// Constructor
public MainPage()
{
InitializeComponent();
SupportedOrientations = SupportedPageOrientation.Portrait | SupportedPageOrientation.Landscape;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
//Reinitialize the GeoCoordinateWatcher
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
watcher.MovementThreshold = 100;//distance in meters
//Add event handlers for StatusChanged and PositionChanged Events
watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
//Start data acquisition
watcher.Start();
//hide button
btnStart.Visibility = Visibility.Collapsed;
}
#region Event Handlers
/// <summary>
/// Handler for the StatusChanged event. This invokes MyStatusChanged on the UI thread
/// and passes the GeoPositionStatusChangedEventArgs
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(() => MyStatusChanged(e));
}
/// <summary>
/// Handler for the PositionChanged Event. This invokes MyPositionChanged on the UI thread and
/// passes the GeoPositionStatusChangedEventArgs
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
Deployment.Current.Dispatcher.BeginInvoke(() => MyPositionChanged(e));
}
#endregion
/// <summary>
/// Custom method called from the PositionChanged event handler
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
void MyPositionChanged(GeoPositionChangedEventArgs<GeoCoordinate> e)
{
//update the map to show the current location
GeoCoordinate geo = new GeoCoordinate(e.Position.Location.Latitude, e.Position.Location.Longitude);
Location ppLoc = new Location(e.Position.Location.Latitude, e.Position.Location.Longitude);
mapMain.SetView(geo, 10);
//update pushpin location and show
MapLayer.SetPosition(ppLocation, ppLoc);
ppLocation.Visibility = System.Windows.Visibility.Visible;
}
/// <summary>
/// Custom method called from the StatusChanged event handler
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
void MyStatusChanged(GeoPositionStatusChangedEventArgs e)
{
switch (e.Status)
{
case GeoPositionStatus.Disabled:
//the location service is disabled or unsupported, alert the user
tbStatus.Text = "Sorry we can't find you on this device";
break;
case GeoPositionStatus.Initializing:
//location service is initializing
//disable the start location button
tbStatus.Text = "Looking For you...";
break;
case GeoPositionStatus.NoData:
//location service is working but no data found, alert the user and enable the stop location button
tbStatus.Text = "can't find you yet...";
ResetMap();
break;
case GeoPositionStatus.Ready:
//location service is receiving data, show the current position and enable the stop location button
tbStatus.Text = "We found you!";
break;
}
}
void ResetMap()
{
Location ppLoc = new Location(0, 0);
GeoCoordinate goe = new GeoCoordinate(0.0,0.0);
mapMain.SetView(goe, 1);
//update pushpin location and show
MapLayer.SetPosition(ppLocation, ppLoc);
ppLocation.Visibility = System.Windows.Visibility.Collapsed;
}
}
I think that this is due to the fact that F# is touted as a language that you can process a large amount of information without being very verbose. While you can build small user interface elements using F# by calling relevant libraries, the intention is for you to build UI's with C# / ASP.NET/ etc. So, it wouldn't really make sense with your application because all you are doing is building a small UI and connecting events of that UI to a larger library of geoprocessing capabilities.
But if you wanted to collect information from that library (or a similar one) of all the points of interest nearby, then sort them according to distance from the user and his potential for 'liking' that point of interest based on some algorithm designed to compare a random point of interest with catagories or prior ratings then F# would be a good choice. You can rapidly describe those data structures, manipulate them, and return the result of it's processing back to your user interface.
This is why instructions such as the one shown here can be helpful. While very light on calling or creating a user interface (the C# code just display some text passed from the F# code), it can be used to create a backend for your phone application.
The sample you posted is a lot of code, so I don't expect that anybody will translate that to F# for you. Calling .NET functionality from F# is generally quite similar to how you'd call it from C# (at least initially, before you learn how to use some advanced F# patterns), so the translation should be pretty direct.
The F# version of code that initializes the GeoCoordinateWatcher is probably going to look like this:
let watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High)
watcher.MovementThreshold <- 100
// Add event handlers for StatusChanged and PositionChanged Events
watcher.StatusChanged.Add(fun eargs ->
MyStatusChanged(eargs) )
watcher.PositionChanged.Add(fun eargs ->
MyPositionChanged(eargs) )
// Start data acquisition
watcher.Start()
In general, F# has a couple of nice features that simplify user interface programming. As far as I know, there isn't a guide on developing Windows Phone applications in F#, specifically, but MSDN has a section that describes development of Silverlight applications, and most of the patterns will be the same:
Developing Client-Side Applications - Real World Functional programming on MSDN

Is there a crystal reports API in visual studio that I can use to manipulate rpt files using a macro?

I'd like to programmatically manipulate my rpt files using a macro or add-in within Visual Studio 2005. What I want to achieve is the ability to automate updating the custom functions in my reports, since there seems no way to have a single copy of the functions shared between reports.
So I'd like to have a macro to:
Read the function definitions from somewhere, eg an xml file in my project
Open each of the rpt files in my solution and replace the existing function definitions with the new ones.
Is there an API for interacting with the rpt files in this way? Any pointers or examples would be greatly appreciated.
Rory
I think the answer is No, there isn't within VS Crystal Reports. It looks like there's an API for other versions, e.g. this
As an alternative, I've changed to having lots of code in my report formula instead of using custom functions. I can then update the report formula using ReportDocument.DataDefinition.FormulaFields..Text
In my case I only want to update one formula in each report, named 'Period'. I've created a file PeriodFormula.txt and included it in the project with Build Action = EmbeddedResource.
I created this class to read the txt file and update all reports within a given directory. It's currently hardcoded to only update the Period formula, but could easily be modified to operate from a list etc.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using CrystalDecisions.CrystalReports.Engine;
using CrystalDecisions.Shared;
namespace RMReports
{
public class CustomFunctionUpdater
{
/// <summary>
/// Update all rpt files in the given directory and all subdirectories.
/// Currently only updates the Period formula.
/// </summary>
/// <param name="directoryPath"></param>
public static void UpdateAllReports(String directoryPath)
{
Debug.WriteLine(string.Format("Starting update on all reports within {0}", directoryPath));
const string formulaName = "Period";
int reportsUpdated = 0;
string formulaText = GetFormulaText(formulaName);
foreach (String filename in Directory.GetFiles(directoryPath, "*.rpt", SearchOption.AllDirectories))
{
try
{
if (UpdateReportFunction(filename, formulaName, formulaText))
{
reportsUpdated++;
Debug.WriteLine(string.Format("Updated: {0}", filename));
}
else
Debug.WriteLine(string.Format("No update to: {0}", filename));
}
catch(Exception ex)
{
Debug.WriteLine(string.Format("Failed to update: {0}. Error: {1}", filename, ex.Message));
}
}
Debug.WriteLine(string.Format("done. {0} reports updated", reportsUpdated));
}
/// <summary>
/// Opens the given report file, updates the specified formula with the given text
/// and saves the report.
/// </summary>
/// <param name="reportFilename">The report file to update</param>
/// <param name="formulaName">The name of the formula to update</param>
/// <param name="formulaText">The new text of the formula to update</param>
/// <returns>Whether the report was updated. If the formula doesn't exist this will be false.</returns>
public static bool UpdateReportFunction(String reportFilename, String formulaName, string formulaText)
{
if (String.IsNullOrEmpty(formulaText)) return false;
if (!File.Exists(reportFilename)) throw new FileNotFoundException("reportFilename", reportFilename);
bool updated = false;
ReportDocument document = new ReportDocument();
try
{
document.Load(reportFilename, OpenReportMethod.OpenReportByDefault);
foreach (FormulaFieldDefinition f in document.DataDefinition.FormulaFields)
{
if (f.Name != formulaName) continue;
if (f.Text == formulaText) break; // no update needed
f.Text = formulaText;
updated = true;
break;
}
if (updated)
document.SaveAs(reportFilename);
}
finally
{
if (document.IsLoaded)
document.Close();
}
return updated;
}
public static void UpdateReportFunction(String reportFilename, String formulaName)
{
string formulaText = GetFormulaText(formulaName);
UpdateReportFunction(reportFilename, formulaName, formulaText);
}
/// <summary>
/// Reads the text for the given formula from the current assembly. Assumes the formula
/// exists in a file named [formulaName]Formula.txt that's been compiled as an embedded resource
/// in the current assembly, e.g. DoStuffFormula.txt for a formula named DoStuff.
/// </summary>
/// <param name="formulaName"></param>
/// <returns></returns>
public static String GetFormulaText(String formulaName)
{
string resourceName = Assembly.GetExecutingAssembly().GetName().Name + "." + formulaName + "Formula.txt";
Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
if (stream==null) return null;
return (new StreamReader(stream)).ReadToEnd();
}
}
}
Then I use it like this, to update all my reports (which are in folders beneath a 'reports' folder).
DirectoryInfo d = Directory.GetParent(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
string reportDirectory = Path.Combine(d.Parent.FullName, "reports");
CustomFunctionUpdater.UpdateAllReports(reportDirectory);
Hopefully someone else finds this useful!

Is it possible to produce a small preview or thumbnail image of a Crystal report in code?

I'm using Crystal to display the reports in my project, and I'd like to be able to display a small preview or thumbnail image of the report to the user when he or she is picking a report to display from my UI. Is there any way to produce these thumbnails dynamically from code?
The user has the option to add or remove reports by adding or removing them from the reports folder, so just making all the thumbnail images by hand isn't really an option.
I used the DSOFile object to obtain the thumbnail inside the report then used AxHost to convert the returned object to an image I could display. This wasn't the solution I wanted but the DSOFile is freely distributable so I guess this will work until I find something better.
Download and install the DSOFile DLL from Microsoft.
Add a reference to **DSO OLE Document Properties Reader 2.1
code
Here is my code, boiled down to the bare minimum:
namespace Ibs.Ui.OrderPrint
{
public partial class OrderPrintEdit
{
public OrderPrintEdit()
{
InitializeComponent();
}
#region -- reports_SelectedIndexChanged(sender, e) Event Handler --
private void reports_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
DSOFile.OleDocumentPropertiesClass oleDocumentPropertiesClass = new DSOFile.OleDocumentPropertiesClass();
DirectoryInfo reportDirectory = new DirectoryInfo(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Reports");
oleDocumentPropertiesClass.Open(reportDirectory + "\\" + reports.Text,true,DSOFile.dsoFileOpenOptions.dsoOptionDontAutoCreate);
Object thumbnail = oleDocumentPropertiesClass.SummaryProperties.Thumbnail;
if (thumbnail != null)
{
reportThumbnail.BackgroundImage = IPictureDispHost.GetPictureFromIPicture(thumbnail);
}
else
{
reportThumbnail.BackgroundImage = null;
}
oleDocumentPropertiesClass.Close(false);
}
catch (Exception ex)
{
}
}
#endregion
}
internal sealed class IPictureDispHost : AxHost
{
private IPictureDispHost() : base("{63109182-966B-4e3c-A8B2-8BC4A88D221C}")
{
}
/// <summary>
/// Convert the dispatch interface into an image object.
/// </summary>
/// <param name="picture">The picture interface</param>
/// <returns>An image instance.</returns>
public new static Image GetPictureFromIPicture(object picture)
{
return AxHost.GetPictureFromIPicture(picture);
}
}
}
I am filling a combobox with report names on the form load. In the SelectedIndexChanged event I get the Thumbnail object from the report and pass it to the conversion method. This should work for Office documents too.

Resources