I am making a radio app for windows phone 7.5. I want to play radio in the background. For that i am using windows phone background audio agent. In the agent code i nee to indicate the track and all the infor according to it. But i want to update the song title and artist name in the track declaration.The track declaration looks like this:
new AudioTrack(new Uri("Ring03.wma", UriKind.Relative),
"Ringtone 3",
"Windows Phone",
"Windows Phone Ringtones",
new Uri("shared/media/Ring03.jpg", UriKind.Relative)),
I wanted to store strings in some variables and then replace them with "Ringtone 3". So that i constantly change the value of the title string. I want to update the title every 5 seconds. So i want my media player to show to the user the track info, so that when the user changes the volume he would see the name and artist in a neat box. I tried to accomplish that but i get a error:( whic sounds like that:
Error 4 A field initializer cannot reference the non-static field, method, or property 'MyAudioPlaybackAgent.AudioPlayer.title.get'
Please help:(
AudioPlayer.cs
using System;
using System.Windows;
using Microsoft.Phone.BackgroundAudio;
namespace AudioPlaybackAgent1
{
public class AudioPlayer : AudioPlayerAgent
{
private static volatile bool _classInitialized;
/// <remarks>
/// AudioPlayer instances can share the same process.
/// Static fields can be used to share state between AudioPlayer instances
/// or to communicate with the Audio Streaming agent.
/// </remarks>
public AudioPlayer()
{
if (!_classInitialized)
{
_classInitialized = true;
// Subscribe to the managed exception handler
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
Application.Current.UnhandledException += AudioPlayer_UnhandledException;
});
}
}
/// Code to execute on Unhandled Exceptions
private void AudioPlayer_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
System.Diagnostics.Debugger.Break();
}
}
/// <summary>
/// Called when the playstate changes, except for the Error state (see OnError)
/// </summary>
/// <param name="player">The BackgroundAudioPlayer</param>
/// <param name="track">The track playing at the time the playstate changed</param>
/// <param name="playState">The new playstate of the player</param>
/// <remarks>
/// Play State changes cannot be cancelled. They are raised even if the application
/// caused the state change itself, assuming the application has opted-in to the callback.
///
/// Notable playstate events:
/// (a) TrackEnded: invoked when the player has no current track. The agent can set the next track.
/// (b) TrackReady: an audio track has been set and it is now ready for playack.
///
/// Call NotifyComplete() only once, after the agent request has been completed, including async callbacks.
/// </remarks>
protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
{
switch (playState)
{
case PlayState.TrackEnded:
player.Track = GetPreviousTrack();
break;
case PlayState.TrackReady:
player.Play();
break;
case PlayState.Shutdown:
// TODO: Handle the shutdown state here (e.g. save state)
break;
case PlayState.Unknown:
break;
case PlayState.Stopped:
break;
case PlayState.Paused:
break;
case PlayState.Playing:
break;
case PlayState.BufferingStarted:
break;
case PlayState.BufferingStopped:
break;
case PlayState.Rewinding:
break;
case PlayState.FastForwarding:
break;
}
NotifyComplete();
}
/// <summary>
/// Called when the user requests an action using application/system provided UI
/// </summary>
/// <param name="player">The BackgroundAudioPlayer</param>
/// <param name="track">The track playing at the time of the user action</param>
/// <param name="action">The action the user has requested</param>
/// <param name="param">The data associated with the requested action.
/// In the current version this parameter is only for use with the Seek action,
/// to indicate the requested position of an audio track</param>
/// <remarks>
/// User actions do not automatically make any changes in system state; the agent is responsible
/// for carrying out the user actions if they are supported.
///
/// Call NotifyComplete() only once, after the agent request has been completed, including async callbacks.
/// </remarks>
protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
{
switch (action)
{
case UserAction.Play:
if (player.PlayerState != PlayState.Playing)
{
track.Title = "tracktitle";
player.Play();
}
break;
case UserAction.Stop:
player.Stop();
break;
case UserAction.Pause:
player.Pause();
break;
case UserAction.FastForward:
player.FastForward();
break;
case UserAction.Rewind:
player.Rewind();
break;
case UserAction.Seek:
player.Position = (TimeSpan)param;
break;
case UserAction.SkipNext:
player.Track = GetNextTrack();
break;
case UserAction.SkipPrevious:
AudioTrack previousTrack = GetPreviousTrack();
if (previousTrack != null)
{
player.Track = previousTrack;
}
break;
}
NotifyComplete();
}
/// <summary>
/// Implements the logic to get the next AudioTrack instance.
/// In a playlist, the source can be from a file, a web request, etc.
/// </summary>
/// <remarks>
/// The AudioTrack URI determines the source, which can be:
/// (a) Isolated-storage file (Relative URI, represents path in the isolated storage)
/// (b) HTTP URL (absolute URI)
/// (c) MediaStreamSource (null)
/// </remarks>
/// <returns>an instance of AudioTrack, or null if the playback is completed</returns>
private AudioTrack GetNextTrack()
{
// TODO: add logic to get the next audio track
AudioTrack track = null;
// specify the track
return track;
}
/// <summary>
/// Implements the logic to get the previous AudioTrack instance.
/// </summary>
/// <remarks>
/// The AudioTrack URI determines the source, which can be:
/// (a) Isolated-storage file (Relative URI, represents path in the isolated storage)
/// (b) HTTP URL (absolute URI)
/// (c) MediaStreamSource (null)
/// </remarks>
/// <returns>an instance of AudioTrack, or null if previous track is not allowed</returns>
private AudioTrack GetPreviousTrack()
{
// TODO: add logic to get the previous audio track
AudioTrack track = null;
// specify the track
return track;
}
/// <summary>
/// Called whenever there is an error with playback, such as an AudioTrack not downloading correctly
/// </summary>
/// <param name="player">The BackgroundAudioPlayer</param>
/// <param name="track">The track that had the error</param>
/// <param name="error">The error that occured</param>
/// <param name="isFatal">If true, playback cannot continue and playback of the track will stop</param>
/// <remarks>
/// This method is not guaranteed to be called in all cases. For example, if the background agent
/// itself has an unhandled exception, it won't get called back to handle its own errors.
/// </remarks>
protected override void OnError(BackgroundAudioPlayer player, AudioTrack track, Exception error, bool isFatal)
{
if (isFatal)
{
Abort();
}
else
{
NotifyComplete();
}
}
/// <summary>
/// Called when the agent request is getting cancelled
/// </summary>
/// <remarks>
/// Once the request is Cancelled, the agent gets 5 seconds to finish its work,
/// by calling NotifyComplete()/Abort().
/// </remarks>
protected override void OnCancel()
{
}
}
}
AudioStreamer.cs
using Microsoft.Phone.BackgroundAudio;
using System.Windows.Media;
namespace AudioStreamAgent1
{
/// <summary>
/// A background agent that performs per-track streaming for playback
/// </summary>
public class AudioTrackStreamer : AudioStreamingAgent
{
/// <summary>
/// Called when a new track requires audio decoding
/// (typically because it is about to start playing)
/// </summary>
/// <param name="track">
/// The track that needs audio streaming
/// </param>
/// <param name="streamer">
/// The AudioStreamer object to which a MediaStreamSource should be
/// attached to commence playback
/// </param>
/// <remarks>
/// To invoke this method for a track set the Source parameter of the AudioTrack to null
/// before setting into the Track property of the BackgroundAudioPlayer instance
/// property set to true;
/// otherwise it is assumed that the system will perform all streaming
/// and decoding
/// </remarks>
protected override void OnBeginStreaming(AudioTrack track, AudioStreamer streamer)
{
//TODO: Set the SetSource property of streamer to a MSS source
NotifyComplete();
}
/// <summary>
/// Called when the agent request is getting cancelled
/// The call to base.OnCancel() is necessary to release the background streaming resources
/// </summary>
protected override void OnCancel()
{
base.OnCancel();
}
}
}
Background Audio agent is not a right choice for a radio streaming, you should use Background Streaming agent instead.
There are some good samples with mostly complete apps for audio streaming: Streaming MP3 player in WP7 and Background audio overview for Windows Phone.
You can set the track name via this code:
var player = BackgroundAudioPlayer.Instance;
player.Track = "new track name";
Related
I have a method documented like this:
/// <summary>
/// Execute a call request to the endpoint specified
/// </summary>
/// <param name="URI">Endpoint of API</param>
public string Call()
{
return null;
}
But when I put the mouse over Call, I can't see the URI param description, only the Call method description. I also tried pressing ctrl + shift + space but nothing happens. An image example:
You have to add a parameter to the function an then mouse-over the parameter in order to see the description.
For example:
/// <summary>
/// Execute a call request to the endpoint specified
/// </summary>
/// <param name="uri">Endpoint of API</param>
public string Call(string uri)
{
return null;
}
demo image
Also, you can go to Tools -> Options -> Environment -> Keyboard or Default Keyboard Shortcuts in Visual Studio, you can then search for commands and see what is assigned to that (and remap).
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.
I'm attempting to use the SaveBundleToSaveMap snippet linked below to implement custom save handling on the server side of a breeze web api implementation.
SaveBundleToSaveMap
This sample does not work as is? (see below); their is a null reference exception which could use some attention.
The SaveWorkState(provider, entitiesArray) constructor calls the ContextProvider.CreateEntityInfoFromJson(...) method which then calls (the class scoped) JsonSerializer.Deserialize(new JTokenReader(jo), entityType) method.
The issue is that JsonSerializer is uninitialised and we get a null reference exeption.
For e.g. I added this test hack to get the code running:
protected internal EntityInfo CreateEntityInfoFromJson(dynamic jo, Type entityType) {
//temp fix to init JsonSerializer if SaveChanges has NOT been called
if(JsonSerializer==null) JsonSerializer = CreateJsonSerializer();
var entityInfo = CreateEntityInfo();
entityInfo.Entity = JsonSerializer.Deserialize(new JTokenReader(jo), entityType);
entityInfo.EntityState = (EntityState)Enum.Parse(typeof(EntityState), (String)jo.entityAspect.entityState);
entityInfo.ContextProvider = this;
This issue does not occur in the standard release bits as CreateEntityInfoFromJson is always? called downstream from a SaveChanges() call which means the JsonSerializer gets initialised.
However, things would be better structured if an initialised JsonSerializer was passed to CreateEntityInfoFromJson as a parameter to avoid potential future null reference issues?
Alternately, is there a way to get the SaveBundleToSaveMap snippet to init the JsonSerializer? Its got a private setter :(
UPDATE
Implemented a very hacky stopgap solution. If anyone at IdeaBlade is watching, would be great to have a public API to convert to and from json saveBundle <-> saveMap.
/// <summary>
/// Convert a json saveBundle into a breeze SaveMap
/// </summary>`enter code here`
public static Dictionary<Type, List<EntityInfo>> SaveBundleToSaveMap(JObject saveBundle)
{
var _dynSaveBundle = (dynamic)saveBundle;
var _entitiesArray = (JArray)_dynSaveBundle.entities;
var _provider = new BreezeAdapter();
//Hack 1: Breeze.ContextProvider initializes a global JsonSerializer in its SaveChanges() method
//We are bypassing SaveChanges() and bootstrapping directly into SaveWorkState logic to generate our saveMap
//as such we need to init a serializer here and slipsteam it in via reflection (its got a private setter)
var _serializerSettings = BreezeConfig.Instance.GetJsonSerializerSettings();
var _bootstrappedJsonSerializer = JsonSerializer.Create(_serializerSettings);
//Hack 2:
//How to write to a private setter via reflection
//http://stackoverflow.com/questions/3529270/how-can-a-private-member-accessable-in-derived-class-in-c
PropertyInfo _jsonSerializerProperty = _provider.GetType().GetProperty("JsonSerializer", BindingFlags.Instance | BindingFlags.NonPublic);
//Hack 3: JsonSerializer property is on Breeze.ContextProvider type; not our derived EFContextProvider type so...
_jsonSerializerProperty = _jsonSerializerProperty.DeclaringType.GetProperty("JsonSerializer", BindingFlags.Instance | BindingFlags.NonPublic);
//Finally, we can init the JsonSerializer
_jsonSerializerProperty.SetValue(_provider, _bootstrappedJsonSerializer);
//saveWorkState constructor loads json entitiesArray into saveWorkState.EntityInfoGroups struct
var _saveWorkState = new SaveWorkState(_provider, _entitiesArray);
//BeforeSave logic loads saveWorkState.EntityInfoGroups metadata into saveWorkState.SaveMap
_saveWorkState.BeforeSave();
var _saveMap = _saveWorkState.SaveMap;
return _saveMap;
}
I looked into this. You don't actually need to make a change to the Breeze code to accomplish what you want. The ContextProvider is designed such that you can do just about whatever you want during save.
I'm curious: what "custom save handling" do you want to perform that you can't do today with the BeforeSave and AfterSave logic? I see in your "stopgap" code that you're calling BeforeSave on the SaveWorkState. What more do you need?
As an exercise, I wrote a NorthwindIBDoNotSaveContext that does what you want. Here's how it goes:
/// <summary>
/// A context whose SaveChanges method does not save
/// but it will prepare its <see cref="SaveWorkState"/> (with SaveMap)
/// so developers can do what they please with the same information.
/// See the <see cref="GetSaveMapFromSaveBundle"/> method;
/// </summary>
public class NorthwindIBDoNotSaveContext : EFContextProvider<NorthwindIBContext_CF>
{
/// <summary>
/// Open whatever is the "connection" to the "database" where you store entity data.
/// This implementation does nothing.
/// </summary>
protected override void OpenDbConnection(){}
/// <summary>
/// Perform your custom save to wherever you store entity data.
/// This implementation does nothing.
/// </summary>
protected override void SaveChangesCore(SaveWorkState saveWorkState) {}
/// <summary>
/// Return the SaveMap that Breeze prepares
/// while performing <see cref="ContextProvider.SaveChanges"/>.
/// </summary>
/// <remarks>
/// Calls SaveChanges which internally creates a <see cref="SaveWorkState"/>
/// from the <see param="saveBundle"/> and then runs the BeforeSave and AfterSave logic (if any).
/// <para>
/// While this works, it is hacky if all you want is the SaveMap.
/// The real purpose of this context is to demonstrate how to
/// pare down a ContextProvider, benefit from the breeze save pre/post processing,
/// and then do your own save inside the <see cref="SaveChangesCore"/>.
/// </para>
/// </remarks>
/// <returns>
/// Returns the <see cref="SaveWorkState.SaveMap"/>.
/// </returns>
public Dictionary<Type, List<EntityInfo>> GetSaveMapFromSaveBundle(JObject saveBundle)
{
SaveChanges(saveBundle); // creates the SaveWorkState and SaveMap as a side-effect
return SaveWorkState.SaveMap;
}
}
And here's how you could use it to get the SaveMap:
var saveMap = new NorthwindIBDoNotSaveContext().GetSaveMapFromSaveBundle(saveBundle);
Yes, it is "hacky", particularly if all you want is the SaveMap. But why do you just want the SaveMap?
We've designed the ContextProvider (and all of its sub-classes) such that you have free reign over the SaveChangesCore method. You could override that, further manipulate the SaveMap, then either delegate to the base implementation or do whatever else you have in mind for saving the entity data.
But while I don't see what you're after, it was not all that hard to extract the SaveChanges initialization logic into its own method.
So in the next release (after 1.5.2), you should find the following new method in the ContextProvider:
protected void InitializeSaveState(JObject saveBundle)
{
JsonSerializer = CreateJsonSerializer();
var dynSaveBundle = (dynamic)saveBundle;
var entitiesArray = (JArray)dynSaveBundle.entities;
var dynSaveOptions = dynSaveBundle.saveOptions;
SaveOptions = (SaveOptions)JsonSerializer.Deserialize(new JTokenReader(dynSaveOptions), typeof(SaveOptions));
SaveWorkState = new SaveWorkState(this, entitiesArray);
}
SaveChanges now calls that method before continuing on in its previous manner:
public SaveResult SaveChanges(JObject saveBundle, TransactionSettings transactionSettings = null) {
if (SaveWorkState == null || SaveWorkState.WasUsed) {
InitializeSaveState(saveBundle);
}
transactionSettings = transactionSettings ?? BreezeConfig.Instance.GetTransactionSettings();
...
}
Notice that SaveChanges won't call InitializeSaveState twice if you've already prepared the SaveWorkState by, say, calling InitializeSaveState externally and then called SaveChanges immediately thereafter. It also won't save twice with a "used" SaveWorkState.
The source is checked into github right now if you're interested.
You'll be able to get the SaveMap from a save bundle by adding this method to your sub-class of a ContextProvider as in this example:
public class NorthwindContextProvider: EFContextProvider<NorthwindIBContext_CF> {
...
public Dictionary<Type, List<EntityInfo>> GetSaveMapFromSaveBundle(JObject saveBundle) {
InitializeSaveState(saveBundle); // Sets initial EntityInfos
SaveWorkState.BeforeSave(); // Creates the SaveMap as byproduct of BeforeSave logic
return SaveWorkState.SaveMap;
}
...
}
Now you use that as follows:
var saveMap = ContextProvider.GetSaveMapFromSaveBundle(saveBundle);
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
/// <summary>
/// Get all following siblings of each element up to but not including the element matched by the selector.
/// </summary>
/// <param name="selector">A string containing a selector expression to indicate where to stop matching following sibling elements.</param>
/// <see cref="http://api.jquery.com/nextUntil/"/>
/// <returns></returns>
public SharpQuery NextUntil(string selector = null)
{
throw new NotImplementedException();
}
I wanted to add a link in my method docs to link to a fuller explanation. "see" seemed appropriate for this (intellisense suggested it). However, when I call start typing my method, "see" doesn't appear in the tooltip. Is there a way to go to that link? I tried pressing F1, it took me to MSDN instead.
The <see> tag must be used within the text of other comment tags in order to specify a hyperlink.
You can also use <seealso> to specify a hyperlink to appear in a See Also section of the generated documentation.
MSDN provides the following example:
/// text for class TestClass
public class TestClass
{
/// <summary>DoWork is a method in the TestClass class.
/// <para>Here's how you could make a second paragraph in a description. <see cref="System.Console.WriteLine(System.String)"/> for information about output statements.</para>
/// <seealso cref="TestClass.Main"/>
/// </summary>
public static void DoWork(int Int1)
{
}
/// text for Main
static void Main()
{
}
}
From what I gathered at:
http://msdn.microsoft.com/en-us/library/5ast78ax(VS.80).aspx
This tags ( , ) will be available in the generated documentation file (the XML file, when you do /doc compiler options), and then further processed by tool like Sandcastle