I'm using ZXing.net to create a UserControl for scanning barcode into a Windows Phone 8.1 RT app using the camera.
The barcode are decoded well but i'm having freezes on the UI when the method CapturePhotoToStreamAsync is called, even it is awaited.
It takes about 600 ms to execute.
I'm testing the app into the emulator.
The code below is executed in an async method :
// Preview of the camera
await _mediaCapture.InitializeAsync(settings);
VideoCapture.Source = _mediaCapture;
VideoCapture.FlowDirection = Windows.UI.Xaml.FlowDirection.LeftToRight;
await _mediaCapture.StartPreviewAsync();
VideoEncodingProperties res = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview) as VideoEncodingProperties;
ImageEncodingProperties iep = ImageEncodingProperties.CreateBmp();
iep.Height = res.Height;
iep.Width = res.Width;
var barcodeReader = new BarcodeReader
{
TryHarder = true,
AutoRotate = true
};
WriteableBitmap wB = new WriteableBitmap((int)res.Width, (int)res.Height);
while (_result == null)
{
using (var stream = new InMemoryRandomAccessStream())
{
await _mediaCapture.CapturePhotoToStreamAsync(iep, stream);
stream.Seek(0);
await wB.SetSourceAsync(stream);
_result = barcodeReader.Decode(wB);
}
}
await _mediaCapture.StopPreviewAsync();
//callback to handle result
ScanCallback(_result.Text);
What can I do to prevent the UI from freezing ?
Luckily you don't have to capture photo to decode QRCode/Barcode on Windows Phone 8.1 Runtime. It is a quite new solution actually, but it works: https://github.com/mmaitre314/VideoEffect#realtime-video-analysis-and-qr-code-detection
After istalling the nuget package, you can easily decode barcodes realtime, without the need of calling CapturePhotoToStreamAsync. The only drawback is that you can only target ARM. You can find the sample code on the site. Or you can contact me, and I can send you the part of my project where I use this.
i always get better results when i use the camera to take a picture first (lets you focus on the correct place where the barcode is) and then send the picture for barcode recognition.
the stuttering is caused because you try to keep checking the live feed for barcodes which can be hard on the CPU (especially for ARM devices)
var dialog = new CameraCaptureUI();
StorageFile file = await dialog.CaptureFileAsync(CameraCaptureUIMode.Photo);
var stream = await file.OpenReadAsync();
// initialize with 1,1 to get the current size of the image
var writeableBmp = new WriteableBitmap(1, 1);
writeableBmp.SetSource(stream);
// and create it again because otherwise the WB isn't fully initialized and decoding
// results in a IndexOutOfRange
writeableBmp = new WriteableBitmap(writeableBmp.PixelWidth, writeableBmp.PixelHeight);
stream.Seek(0);
writeableBmp.SetSource(stream);
var result = ScanBitmap(writeableBmp);
string barcode = "";
if (result != null)
{
barcode = result.Text;
}
and here the ScanBitmap method:
private Result ScanBitmap(WriteableBitmap writeableBmp)
{
var barcodeReader = new BarcodeReader
{
Options = new ZXing.Common.DecodingOptions()
{
TryHarder = true
},
AutoRotate = true
};
var result = barcodeReader.Decode(writeableBmp);
if (result != null)
{
CaptureImage.Source = writeableBmp;
}
return result;
}
Related
I am working on an application in C# using XAudio2 from SharpDX. I know its outdated but instead of CSCore it offers the better Reverb. The aim itself is simple: load a wav file (48khz,24bit,1channel), get it through the XAudio2 Reverb with several Reverb presets and do a playback.
I managed to implement everything for a normal playback and also built in the Reverb. The audio file will be played back with the defaul reverb settings but as soon as I change the preset or change a specific value (RoomFilterFreq) the file isnt audible anymore and i don´t know why this happens. Does anyone has a clue about this? I worked through the few examples which are still online but could not find a reason for this behaviour.
I´m coding under Visual Studio 2019 with Net 4.7.2. The code looks as followed:
xaudio2 = new XAudio2();
xaudio2.StartEngine();
var masteringVoice = new MasteringVoice(xaudio2, 1, 48000);
var stream = new SoundStream(File.OpenRead(soundFilepath));
SharpDX.Multimedia.WaveFormat waveFormat = stream.Format;
var buffer = new AudioBuffer
{
Stream = stream.ToDataStream(),
AudioBytes = (int)stream.Length,
Flags = BufferFlags.EndOfStream
};
stream.Close();
sourceVoice = new SourceVoice(xaudio2, waveFormat, true);
// Set Loop
if (f1.loopcheckBox.Checked == true)
{
buffer.LoopBegin = buffer.PlayBegin;
buffer.LoopLength = buffer.PlayLength;
buffer.LoopCount = AudioBuffer.LoopInfinite;
}
// Set Reverb
if (f1.reverbenableButton.BackColor == Color.LightGreen)
{
var reverb = new Reverb(xaudio2);
var reverbparameters = (ReverbParameters)ReverbI3DL2Parameters.Presets.Room;
var effectDescriptor = new EffectDescriptor(reverb);
if (waveFormat.Channels == 1)
{
effectDescriptor.OutputChannelCount = 1;
}
else if (waveFormat.Channels == 2)
{
effectDescriptor.OutputChannelCount = 2;
}
else
{
MessageBox.Show("Channelrate not supported!");
return sourceVoice = null;
}
sourceVoice.SetEffectChain(effectDescriptor);
sourceVoice.SetEffectParameters(0, reverbparameters);
sourceVoice.EnableEffect(0);
}
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
sourceVoice.Start();
I am working on an UWP app for the HoloLens to read single frames from the devices camera. I want to use the camera mode with the lowest resolution available.
I took a look at the following links and examples and tried to create a minimal working app:
https://learn.microsoft.com/en-us/windows/uwp/audio-video-camera/set-media-encoding-properties
https://learn.microsoft.com/en-us/windows/uwp/audio-video-camera/use-opencv-with-mediaframereader
https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/CameraResolution
https://github.com/microsoft/Windows-universal-samples/tree/master/Samples/CameraOpenCV
This is the code snippet from MainPage.xaml.cs:
public async Task<int> Start()
{
// Find the sources
var allGroups = await MediaFrameSourceGroup.FindAllAsync();
var sourceGroups = allGroups.Select(g => new
{
Group = g,
SourceInfo = g.SourceInfos.FirstOrDefault(i => i.SourceKind == MediaFrameSourceKind.Color)
}).Where(g => g.SourceInfo != null).ToList();
if (sourceGroups.Count == 0)
{
// No camera sources found
return 0;
}
var selectedSource = sourceGroups.FirstOrDefault();
// Initialize MediaCapture
_mediaCapture = new MediaCapture();
var settings = new MediaCaptureInitializationSettings()
{
SourceGroup = selectedSource.Group,
SharingMode = MediaCaptureSharingMode.ExclusiveControl,
StreamingCaptureMode = StreamingCaptureMode.Video,
MemoryPreference = MediaCaptureMemoryPreference.Cpu
};
await _mediaCapture.InitializeAsync(settings);
// Query all properties of the device
IEnumerable<StreamResolution> allVideoProperties = _mediaCapture.VideoDeviceController.GetAvailableMediaStreamProperties(MediaStreamType.VideoRecord).Select(x => new StreamResolution(x));
// Order them by resolution then frame rate
allVideoProperties = allVideoProperties.OrderBy(x => x.Height * x.Width).ThenBy(x => x.FrameRate);
await _mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoRecord, allVideoProperties.ElementAt(0).EncodingProperties);
// Create the frame reader
MediaFrameSource frameSource = _mediaCapture.FrameSources[selectedSource.SourceInfo.Id];
_reader = await _mediaCapture.CreateFrameReaderAsync(frameSource, MediaEncodingSubtypes.Bgra8);
_reader.FrameArrived += ColorFrameReader_FrameArrivedAsync;
await _reader.StartAsync();
return 1;
}
private async void ColorFrameReader_FrameArrivedAsync(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
{
var frame = sender.TryAcquireLatestFrame();
if (frame != null)
{
var inputBitmap = frame.VideoMediaFrame?.SoftwareBitmap;
}
}
On my local machine (MacBookPro with Bootcamp partition) this code works using the webcam. It detects three supported video modes. I can change the resolution of the bitmap image in FrameArrivedAsync by changing the index from 0 to 1 or 2 at:
_mediaCapture.VideoDeviceController.SetMediaStreamPropertiesAsync(MediaStreamType.VideoRecord, allVideoProperties.ElementAt(0).EncodingProperties);
On HoloLens this code does not work. It detects the different modes like explained here (https://learn.microsoft.com/en-us/windows/mixed-reality/locatable-camera). But setting the MediaStreamProperties does not change anything regarding the received bitmap image. The bitmap is always 1280x720.
Just in case, we want to share how we setup the capture profile, you can refer to the following code with annotate to modify your project for testing. If in doubt, please feel free to add comments.
private async void SetupAndStartMediaCapture()
{
string deviceId = string.Empty;
_mediaCapture = new MediaCapture();
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
foreach (var device in devices)
{
if(MediaCapture.IsVideoProfileSupported(device.Id))
{
deviceId = device.Id;
break; // The video device for which supported video profile support is queried.
}
}
MediaCaptureInitializationSettings mediaCapSettings = new MediaCaptureInitializationSettings
{
VideoDeviceId = deviceId
};
IReadOnlyList<MediaCaptureVideoProfile> profiles = MediaCapture.FindAllVideoProfiles(deviceId);
var profileMatch = (
from profile in profiles
from desc in profile.SupportedRecordMediaDescription
where desc.Width == 896 && desc.Height == 504 && desc.FrameRate == 24 // HL1
select new { profile, desc }
).FirstOrDefault();// Select the Profile with the required resolution from all available profiles.
if (profileMatch != null)
{
mediaCapSettings.VideoProfile = profileMatch.profile;
mediaCapSettings.RecordMediaDescription = profileMatch.desc;
}
await _mediaCapture.InitializeAsync(mediaCapSettings); //Initializes the MediaCapture object.
}
I would like to know how to record 2 separate audio channel simultaneously.
I have 2 USB adapters with mic & speaker respectively.
The samples code which I can find only support single channel recording at a time.
Please help. Thanks.
For single channel my code as follow;
MediaCapture audioCapture = new MediaCapture();
MediaCaptureInitializationSettings captureInitSettings = new MediaCaptureInitializationSettings();
captureInitSettings.StreamingCaptureMode = StreamingCaptureMode.Audio;
captureInitSettings.MediaCategory = MediaCategory.Other;
captureInitSettings.AudioProcessing = AudioProcessing.Default;
await audioCapture.InitializeAsync(captureInitSettings);
private async void recordChannelA()
{
StorageFolder externalDevices = KnownFolders.RemovableDevices;
IReadOnlyList<StorageFolder> externalDrives = await externalDevices.GetFoldersAsync();
StorageFolder usbStorage = externalDrives[0];
if (usbStorage != null)
{
StorageFolder recordFolder = await usbStorage.CreateFolderAsync(recFolderName, CreationCollisionOption.OpenIfExists);
await usbStorage.GetFolderAsync(recFolderName);
StorageFile recordFile = await recordFolder.CreateFileAsync("Recording - " + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".mp3", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
MediaEncodingProfile profile = null;
profile = MediaEncodingProfile.CreateM4a(Windows.Media.MediaProperties.AudioEncodingQuality.Auto);
await audioCapture.StartRecordToStorageFileAsync(profile, recordFile);
Message.Text = "Recording ... ";
recordingtimerRun = new TimeSpan(0, 0, 0);
recordingTimer.Start();
}
else Message.Text = "Recording error !";
}
Update;
I created a 'listview' for the enumerated devices and to select the respective capture device. However, there is an Syntax Error which i cannot convert the enumaration.deviceinformation to imediasource.
captureInitSettings.AudioSource = captureDeviceList[audioCaptureList.SelectedIndex];
Update: I managed to get it to work
The solution is
captureInitSettingsA.AudioDeviceId = captureDeviceList[audioCaptureList.SelectedIndex].Id;
captureInitSettingsB.AudioDeviceId = captureDeviceList[audioCaptureList.SelectedIndex].Id;
However, how do i save these selections in app settings .. so that when I reboot I don't have to re-select again.
Update:
I manage to save the app setting for audiocapture & audiorender devices but I am not sure how to retrieve them & also to check if there is any previous settings saved.
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
localSettings.Values["audioACaptureSettings"] = captureAInitSettings.AudioDeviceId;
localSettings.Values["audioARenderSettings"] = mediaPlayerA.AudioDevice.Id;
localSettings.Values["audioBCaptureSettings"] = captureBInitSettings.AudioDeviceId;
localSettings.Values["audioBRenderSettings"] = mediaPlayerB.AudioDevice.Id;
private void loadAudioConfig()
{
Windows.Storage.ApplicationDataContainer localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
Windows.Storage.StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
if (localSettings.Values["audioACaptureSettings"] != null)
{
captureAInitSettings.AudioDeviceId = localSettings.Values["audioACaptureSettings"].ToString();
}
if (localSettings.Values["audioARenderSettings"] != null)
{
Object audioARenderValue = localSettings.Values["audioARenderSettings"];
mediaPlayerA.AudioDevice = audioARenderValue;
}
if (localSettings.Values["PAaudioCaptureSettings"] != null)
{
captureBInitSettings.AudioDeviceId = localSettings.Values["audioBCaptureSettings"].ToString();
}
if (localSettings.Values["PAaudioRenderSettings"] != null)
{
Object audioBRenderValue = localSettings.Values["audioBRenderSettings"];
mediaPlayerB.AudioDevice = audioBRenderValue;
}
You can refer to this document which introduced how to store and retrieve settings and other app data. You can save the data to Settings and Files.
When you use Settings, it only supports multiple data types as mentioned in the document.
If use files, you can store binary data or to enable your own, customized serialized types,.
In your provided code, it is correct to check if there is any previous settings saved:
if (localSettings.Values["audioACaptureSettings"] != null)
{
captureAInitSettings.AudioDeviceId = localSettings.Values["audioACaptureSettings"].ToString();
}
But it is incorrect about how to retrieve the setting as AudioDevice because it can not implicitly convert string to DeviceInformation. Please try in this way:
if (localSettings.Values["audioARenderSettings"] != null)
{
var aduioSource = localSettings.Values["audioARenderSettings"] as string;
mediaPlayerA.AudioDevice = await DeviceInformation.CreateFromIdAsync(aduioSource);
}
I am trying to set up a preview stream and recording loop with buttons to save the last 10 mins, 30 secs etc. This was working just fine until I started adding the code to handle rotation.
This is the line that throws.
await _mediaCapture.SetEncodingPropertiesAsync(MediaStreamType.VideoPreview,
videoEncodingProperties, mediaPropertySet);
here is the whole method
public async Task<MediaCapture> PrepareRecordingAsync() {
try {
_mediaCapture = new MediaCapture();
var allVideoDevices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
var desiredDevice = allVideoDevices.FirstOrDefault(x => x.EnclosureLocation != null && x.EnclosureLocation.Panel == Panel.Back);
_cameraDevice = desiredDevice ?? allVideoDevices.FirstOrDefault();
_rotationHelper = new CameraRotationHelper(_cameraDevice.EnclosureLocation);
_mediaCapture.Failed += MediaCapture_Failed;
var settings = new MediaCaptureInitializationSettings { VideoDeviceId = _cameraDevice.Id };
await _mediaCapture.InitializeAsync(settings);
var encodingProfile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto);
var rotationAngle = CameraRotationHelper.ConvertSimpleOrientationToClockwiseDegrees(_rotationHelper.GetCameraCaptureOrientation());
Guid RotationKey = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1");
encodingProfile.Video.Properties.Add(RotationKey, PropertyValue.CreateInt32(rotationAngle));
var videoEncodingProperties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview);
MediaPropertySet mediaPropertySet = new MediaPropertySet();
await _mediaCapture.SetEncodingPropertiesAsync(MediaStreamType.VideoPreview, videoEncodingProperties, mediaPropertySet);
_ras = new InMemoryRandomAccessStream();
_recording = await _mediaCapture.PrepareLowLagRecordToStreamAsync(encodingProfile, _ras);
DisplayInformation.AutoRotationPreferences = DisplayOrientations.Portrait;
ConcurrentRecordAndPhotoSupported = _mediaCapture.MediaCaptureSettings.ConcurrentRecordAndPhotoSupported;
} catch (UnauthorizedAccessException) {
// This will be thrown if the user denied access to the camera in privacy settings
System.Diagnostics.Debug.WriteLine("The app was denied access to the camera");
} catch (Exception ex) {
System.Diagnostics.Debug.WriteLine("MediaCapture initialization failed. {0}", ex.Message);
}
return _mediaCapture;
}
None of the solutions found via google search are any help.
This is basically a modification of the MSDN How-to's.
EDIT: If I change the offending line to the following then it works fine.
_mediaCapture.SetPreviewRotation(VideoRotation.Clockwise90Degrees);
I can reproduce your issue on my side, it will throw the error exception at code line await _mediaCapture.SetEncodingPropertiesAsync(...);
The stream number provided was invalid.
PreviewState
According to the SetEncodingPropertiesAsync method
Note that this rotation is performed by the consumer of the stream, such as the CaptureElement or a video player app, while the actual pixels in the stream still retain their original orientation.
This method performed by the consumer of the stream. It seems like that you need to invoke StartPreviewAsync() firstly before you setting the preview rotation so that you have preview stream. More details please reference the "Add orientation data to the camera preview stream" section of Handle device orientation with MediaCapture.
After starting the preview, call the helper method SetPreviewRotationAsync to set the preview rotation.
So updating your code snippet as follows it will work.
_mediaCapture = new MediaCapture();
var allVideoDevices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
var desiredDevice = allVideoDevices.FirstOrDefault(x => x.EnclosureLocation != null && x.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Back);
_cameraDevice = desiredDevice ?? allVideoDevices.FirstOrDefault();
_rotationHelper = new CameraRotationHelper(_cameraDevice.EnclosureLocation);
_mediaCapture.Failed += MediaCapture_Failed;
var settings = new MediaCaptureInitializationSettings { VideoDeviceId = _cameraDevice.Id };
await _mediaCapture.InitializeAsync(settings);
//Add the preview code snippet
PreviewControl.Source = _mediaCapture;
await _mediaCapture.StartPreviewAsync();
var encodingProfile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto);
var rotationAngle = CameraRotationHelper.ConvertSimpleOrientationToClockwiseDegrees(_rotationHelper.GetCameraCaptureOrientation());
Guid RotationKey = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1");
encodingProfile.Video.Properties.Add(RotationKey, PropertyValue.CreateInt32(rotationAngle));
var videoEncodingProperties = _mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview);
MediaPropertySet mediaPropertySet = new MediaPropertySet();
await _mediaCapture.SetEncodingPropertiesAsync(MediaStreamType.VideoPreview, videoEncodingProperties, mediaPropertySet);
More details please reference the official sample.
I'm about to loose it soon... I have two apps with working background tasks that are updating the live tile. The data for the live tile is downloaded, parsed and then an image is created dynamically and used as the background for the live tile.
Everything is working just fine for a day or two, but then the updating starts behaving very strange. The first one-two days both live tiles for my apps are updating every 28 minutes like clockwork. But then they start skipping updates. Often app A then updates when app B doesn't update the live tile so that they are not updating at the same time and only once an hour. To put it simple they are way off schedule.
This is really frustrating since I need to be able to rely on the tiles beeing updated every 30 minutes (if I have enough battery, good reception and so on).
I would really appreciate if someone could help me out and maybe take a look at my code to see if there might be something messing up the update interval (like not calling NotifyComplete correctly). I have removed some code and and tried to simplify it. Please ask if you need anything else to understand this.
I have been trying to fix this for the last two months, trying different phones and going throughmy code very carefully.
Your help is more appreciated than you can ever know.
Thanks in advance.
My OnInvoke function:
Timer t = null;
ShellToast toast = new ShellToast();
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(strUrlHBH));
request.Accept = "*/*";
request.AllowAutoRedirect = true;
// disable caching.
request.Headers["Cache-Control"] = "no-cache";
request.Headers["Pragma"] = "no-cache";
t = new Timer(
state =>
{
if (string.Compare(state.ToString(), id, StringComparison.InvariantCultureIgnoreCase) == 0)
{
//logger.Write("Timeout reached for connection [{0}], aborting download.", id);
runNotifyComplete = false;
NotifyComplete();
request.Abort();
t.Dispose();
}
},
id,
timeout,
0);
request.BeginGetResponse(
r =>
{
try
{
if (t != null)
{
t.Dispose();
}
var httpRequest = (HttpWebRequest)r.AsyncState;
var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
using (var reader = new StreamReader(httpResponse.GetResponseStream()))
{
var response = reader.ReadToEnd();
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
try
{
//Parse the result
if (boolResult) //If we have a result
{
Grid grid = new Grid();
StackPanel sp = new StackPanel();
sp.Height = 173;
sp.Width = 173;
//StreamResourceInfo info = Application.GetResourceStream(new Uri("Pin-to-start2.png", UriKind.Relative));
if ((bool)settings["LiveTileMetro"] == true)
{
// create source bitmap for Image control (image is assumed to be alread 173x173)
/*WriteableBitmap wbmp2 = new WriteableBitmap(1, 1);
wbmp2.SetSource(info.Stream);
Image img = new Image();
img.Source = wbmp2;
// add Image to Grid
grid.Children.Add(img);
strBackBackground = "Pin-to-start2.png";
}
else
{*/
sp.Background = (SolidColorBrush)Application.Current.Resources["PhoneAccentBrush"];
//sp.Background.Opacity = 0.0;
strBackBackground = "";
}
StreamResourceInfo info;
//GC.Collect();
info = Application.GetResourceStream(new Uri("/MyApp;component/images/Icons/livetile/livetile.png", UriKind.Relative));
WriteableBitmap wbmp3 = new WriteableBitmap(1, 1);
try
{
wbmp3.SetSource(info.Stream);
}
catch
{
}
Image img3 = new Image();
img3.Source = wbmp3;
// add Image to Grid
img3.Width = 173;
img3.Height = 173;
img3.Margin = new Thickness { Left = 0, Bottom = 0, Right = 0, Top = 0 };
TextBlock txtTemperature = new TextBlock();
TextBlock txtTemperatureRing = new TextBlock();
txtTemperature.Foreground = new SolidColorBrush(Colors.White);
txtTemperature.Text = strTemp;
txtTemperature.TextAlignment = TextAlignment.Right;
txtTemperatureRing.Style = (Style)Application.Current.Resources["PhoneTextTitle3Style"];
txtTemperatureRing.FontFamily = new FontFamily("Segoe WP Light");
txtTemperatureRing.FontSize = 40;
txtTemperatureRing.Foreground = new SolidColorBrush(Colors.White);
txtTemperatureRing.Text = "°";
txtTemperatureRing.TextAlignment = TextAlignment.Right;
txtTemperature.FontFamily = new FontFamily("Segoe WP Light");
txtTemperature.FontSize = 60;
txtTemperature.Margin = new Thickness { Left = 0, Bottom = 0, Right = 0, Top = -75 };
txtTemperature.Height = 80;
txtTemperature.Width = 145;
txtTemperatureRing.Margin = new Thickness { Left = 128, Bottom = 0, Right = 0, Top = -97 };
txtTemperatureRing.Height = 50;
txtTemperatureRing.Width = 39;
sp.Children.Add(img3);
sp.Children.Add(txtTemperature);
sp.Children.Add(txtTemperatureRing);
//call measure, arrange and updatelayout to prepare for rendering
sp.Measure(new Size(173, 173));
sp.Arrange(new Rect(0, 0, 173, 173));
sp.UpdateLayout();
grid.Children.Add(sp);
WriteableBitmap wbmp = new WriteableBitmap(173, 173);
wbmp.Render(grid, null);
wbmp.Invalidate();
//write image to isolated storage
string sIsoStorePath = #"\Shared\ShellContent\tile.png";
using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
//ensure directory exists
String sDirectory = System.IO.Path.GetDirectoryName(sIsoStorePath);
if (!appStorage.DirectoryExists(sDirectory))
{
appStorage.CreateDirectory(sDirectory);
}
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(sIsoStorePath, System.IO.FileMode.Create, appStorage))
{
wbmp.SaveJpeg(stream, 173, 173, 0, 100);
}
}
/// If application uses both PeriodicTask and ResourceIntensiveTask
//ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("TileID=2"));
ShellTile TileToFind = ShellTile.ActiveTiles.First();
//test if Tile was created
if (TileToFind != null)
{
StandardTileData NewTileData = new StandardTileData
{
BackgroundImage = new Uri("isostore:Shared/ShellContent/tile.png", UriKind.Absolute),
Title = strTitle,
Count = null,
BackTitle = (string)settings["SelectedCityName"],
BackBackgroundImage = new Uri(strBackBackground, UriKind.Relative),
BackContent = strWind + Environment.NewLine + strPrecipitation
};
//ShellTile.Create(new Uri("/MainPage.xaml?TileID=2", UriKind.Relative), NewTileData);
TileToFind.Update(NewTileData);
}
}
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}//If matches.count
}
catch
{
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}
finally
{
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}
}));
}
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}
catch
{
// error handling.
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}
finally
{
}
},
request);
}
catch
{
if (runNotifyComplete == true)
{
runNotifyComplete = false;
NotifyComplete();
}
}
finally
{
}
EDIT 1: Here is the code for initializing the background task
MessageBox.Show(MyResources.LiveTileToggleMsgBoxText, "Live tile", MessageBoxButton.OK);
//start background agent
PeriodicTask periodicTask = new PeriodicTask("AppPeriodicAgent0440");
periodicTask.Description = "Periodic task for APP that updates the LiveTile .";
periodicTask.ExpirationTime = System.DateTime.Now.AddDays(14);
// If the agent is already registered with the system,
if (ScheduledActionService.Find(periodicTask.Name) != null)
{
ScheduledActionService.Remove("AppPeriodicAgent0440");
}
try
{
//only can be called when application is running in foreground
ScheduledActionService.Add(periodicTask);
}
catch
{
}
EDIT 2:
This code is run everytime I exit the application in order to keep the background task alive. I put it in OnNavigatedFrom to avoid slowing down the start up of the app
//start background agent
PeriodicTask periodicTask = new PeriodicTask("AppPeriodicAgent0440");
periodicTask.Description = "Periodic task for APP that updates the LiveTile.";
periodicTask.ExpirationTime = System.DateTime.Now.AddDays(14);
// If the agent is already registered with the system,
if (ScheduledActionService.Find(periodicTask.Name) != null)
{
ScheduledActionService.Remove("AppPeriodicAgent0440");
}
//only can be called when application is running in foreground
ScheduledActionService.Add(periodicTask);
If you read the Background Agents overview
http://msdn.microsoft.com/en-us/library/hh202942(v=vs.92).aspx you'll see that it details several scenarios where you schedule won't be followed. For example:
Battery Saver mode is an option that the user can enable on the device
to indicate that battery life should be prioritized. If this mode is
enabled, periodic agents may not run, even if the interval has elapsed
There is no guarantee that the scheduled task will run on the expected schedule. It even has a +/- 10 minute clause in the execution timeschedule.
From your question there is no information about how you're recording other events/tasks that may be happenening on the device at the time you're expecting a schedule to run.
In that you have no logging of errors, timeouts or checking of the LastExitReason of the task there could be all sorts of reasons for the behaviour you're seeing.
If I had to guess where the issue is, based on the information you have provided, I'd presume the problem is with the web request (either the network or the server) and an error in the request causes the UI to not be updated.
There is no way to guarantee that the tile will update on a predictable schedule. If you must absolutely have this updated for your circumstances/requirements then this platform isn't going to be able to provide that guarantee.
If you need multiple tiles to be kept in sync I'd approach this by having all the tiles updated by the same process.
This way, even if there is an inconsistency in the updating of the tiles from a time perspective they'll be in sync with each other.