ScheduledTaskAgent issue in updating WP8 app tiles programatically - c#

I am recently working in a Windows Phone 8 app that update tiles in background. I followed this http://jeffblankenburgdotcom.wordpress.com/2011/11/25/31-days-of-mango-day-25-background-agents/ tutorial to make my work done. I followed every steps, but its putting the tile first but it does not updated the next tile after 60seconds in background. You can download the solution file here https://onedrive.live.com/?cid=843581c79c017ebc&id=843581C79C017EBC%21149&ithint=file,.rar&authkey=!ABbazYty8Rw7a_A
MainPage.xaml.cs is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using BackgroundAgentTileTest.Resources;
using Microsoft.Phone.Scheduler;
namespace BackgroundAgentTileTest
{
public partial class MainPage : PhoneApplicationPage
{
PeriodicTask periodicTask;
string periodicTaskName = "PeriodicAgent";
bool agentsAreEnabled = true;
// Constructor
public MainPage()
{
InitializeComponent();
agentsAreEnabled = true;
// Obtain a reference to the period task, if one exists
periodicTask = ScheduledActionService.Find(periodicTaskName) as PeriodicTask;
if (periodicTask != null)
{
RemoveAgent(periodicTaskName);
}
periodicTask = new PeriodicTask(periodicTaskName);
// The description is required for periodic agents. This is the string that the user
// will see in the background services Settings page on the device.
periodicTask.Description = "This demonstrates a periodic task.";
// Place the call to Add in a try block in case the user has disabled agents.
try
{
ScheduledActionService.Add(periodicTask);
// If debugging is enabled, use LaunchForTest to launch the agent in one minute.
#if(DEBUG_AGENT)
ScheduledActionService.LaunchForTest(periodicTaskName, TimeSpan.FromSeconds(10));
#endif
}
catch (InvalidOperationException exception)
{
if (exception.Message.Contains("BNS Error: The action is disabled"))
{
MessageBox.Show("Background agents for this application have been disabled by the user.");
agentsAreEnabled = false;
}
if (exception.Message.Contains("BNS Error: The maximum number of ScheduledActions of this type have already been added."))
{
// No user action required. The system prompts the user when the hard limit of periodic tasks has been reached.
}
}
catch (SchedulerServiceException)
{
// No user action required.
}
}
private void RemoveAgent(string periodicTaskName)
{
try
{
ScheduledActionService.Remove(periodicTaskName);
}
catch (Exception)
{
}
}
}
}
ScheduledAgent.cs is
#define DEBUG_AGENT
using System.Diagnostics;
using System.Windows;
using Microsoft.Phone.Scheduler;
using Microsoft.Phone.Shell;
using System;
using System.Linq;
namespace ScheduledTaskAgent1
{
public class ScheduledAgent : ScheduledTaskAgent
{
/// <remarks>
/// ScheduledAgent constructor, initializes the UnhandledException handler
/// </remarks>
static ScheduledAgent()
{
// Subscribe to the managed exception handler
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
Application.Current.UnhandledException += UnhandledException;
});
}
/// Code to execute on Unhandled Exceptions
private static void UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (Debugger.IsAttached)
{
// An unhandled exception has occurred; break into the debugger
Debugger.Break();
}
}
/// <summary>
/// Agent that runs a scheduled task
/// </summary>
/// <param name="task">
/// The invoked task
/// </param>
/// <remarks>
/// This method is called when a periodic or resource intensive task is invoked
/// </remarks>
protected override void OnInvoke(ScheduledTask task)
{
//TODO: Add code to perform your task in background
//UpdateAppTile(GetLastUpdatedTimeMessage());
StandardTileData data = new StandardTileData
{
Title = "My tile!",
Count = 10, // I Need To Get This Counter From Isolated Storage Or My Other main Project
BackgroundImage = new Uri("/Background.png", UriKind.RelativeOrAbsolute),
BackTitle = "This is the back",
BackContent = DateTime.Now.ToString(),
BackBackgroundImage = new Uri("/Background.png", UriKind.RelativeOrAbsolute)
};
ShellTile.ActiveTiles.First().Update(data);
// If debugging is enabled, launch the agent again in one minute.
#if DEBUG_AGENT
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(60));
#endif
NotifyComplete();
}
private string GetLastUpdatedTimeMessage()
{
return string.Format("Last Updated: {0}", DateTime.Now);
}
private void UpdateAppTile(string message)
{
ShellTile appTile = ShellTile.ActiveTiles.First();
if (appTile != null)
{
StandardTileData tileData = new StandardTileData
{
BackContent = message
};
appTile.Update(tileData);
}
}
}
}
Note : I have put the reference.

It works after adding #define DEBUG_AGENT line on top of the MainPage.xaml.cs file.

Related

Xamarin Form HTTPClient Call crashing

I have a project where I am using System.Net.Http.HttpClient. I am trying to centralize all calls to my Web APIs so that I have common error handing etc. I created the following class in my project.
using ModernHttpClient;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace WebAPIHelper
{
class WebAPICaller
{
public async Task<string> CallWebService(string ps_URI)
{
HttpClient lobj_HTTPClient = null;
HttpResponseMessage lobj_HTTPResponse = null;
string ls_Response = "";
//We assume the internet is available.
try
{
//Get the Days of the Week
lobj_HTTPClient = new HttpClient(new NativeMessageHandler());
lobj_HTTPClient.BaseAddress = new Uri(App.APIPrefix);
lobj_HTTPClient.DefaultRequestHeaders.Accept.Clear();
lobj_HTTPClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
lobj_HTTPResponse = await lobj_HTTPClient.GetAsync(ps_URI);
if (!lobj_HTTPResponse.IsSuccessStatusCode)
{
Debug.WriteLine(lobj_HTTPResponse.ReasonPhrase);
}
else
{
ls_Response = await lobj_HTTPResponse.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
if (lobj_HTTPClient != null)
lobj_HTTPClient.Dispose();
if (lobj_HTTPResponse != null)
{
lobj_HTTPResponse.Dispose();
}
}
return ls_Response;
}
}
}
I call the function from an instance object I created in my ViewModel class for languages as follows:
using ModernHttpClient;
using Newtonsoft.Json;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace WebAPIHelper
{
public class VM_Languages : INotifyPropertyChanged
{
/// <summary>
/// A collection for CGSLanguage objects.
/// </summary>
public ObservableCollection<GBSLanguage_ForList> Items_ForList { get; private set; }
const string ic_LanguagesAPIUrl = #"/languages/true";
/// <summary>
/// Constructor for the Languages view model.
/// </summary>
public VM_Languages()
{
this.Items_ForList = new ObservableCollection<GBSLanguage_ForList>();
}
/// <summary>
/// Indicates of the view model data has been loaded
/// </summary>
public bool IsDataLoaded
{
get;
private set;
}
/// <summary>
/// Creates and adds a the countries to the collection.
/// </summary>
public async Task LoadData()
{
HttpClient lobj_HTTPClient = null;
HttpResponseMessage lobj_HTTPResponse = null;
string ls_Response = "";
try
{
IsDataLoaded = false;
string ls_WorkLanguageURI = ic_LanguagesAPIUrl;
//Get the Days of the Week
lobj_HTTPClient = new HttpClient(new NativeMessageHandler());
lobj_HTTPClient.BaseAddress = new Uri(App.APIPrefix);
lobj_HTTPClient.DefaultRequestHeaders.Accept.Clear();
lobj_HTTPClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
**//This call will not work
//WebAPICaller lobj_APICaller = new WebAPICaller();
//ls_Response = lobj_APICaller.CallWebService(ls_WorkLanguageURI).Result;
//This call will work
lobj_HTTPResponse = await lobj_HTTPClient.GetAsync(ls_WorkLanguageURI);**
if (lobj_HTTPResponse.IsSuccessStatusCode)
{
if (this.Items_ForList != null)
this.Items_ForList.Clear();
ls_Response = await lobj_HTTPResponse.Content.ReadAsStringAsync();
Items_ForList = JsonConvert.DeserializeObject<ObservableCollection<GBSLanguage_ForList>>(ls_Response);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
this.IsDataLoaded = true;
NotifyPropertyChanged("GBSLanguages_ForList");
}
}
/// <summary>
/// Notifies subscribers that a property has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I have all my ViewModels in a static class so I only ever get one instance of them as follows:
namespace WebAPIHelper
{
public static class ViewModelObjects
{
private static VM_Languages iobj_Languages;
public static VM_Languages Languages
{
get
{
if (iobj_Languages == null)
iobj_Languages = new VM_Languages();
return iobj_Languages;
}
}
}
}
In my on appearing code of my main page I have the following call to retrieve the data from the WebAPI
protected override async void OnAppearing()
{
Device.BeginInvokeOnMainThread(() =>
{
if (!ViewModelObjects.Languages.IsDataLoaded)
ViewModelObjects.Languages.LoadData();
});
//If the DOW and Language data are not loaded yet - wait
while (!ViewModelObjects.Languages.IsDataLoaded)
{
await Task.Delay(1000);
}
}
The problem is when I attempt to use my WebAPICaller class, it appears to crash on the line. I never get a return from it. No exceptions are ever raised and my program never continues.
lobj_HTTPResponse = await lobj_HTTPClient.GetAsync(ps_URI);
If I make what I believe to be the exact same call from my ViewModel, it works. (I have both the call to the WebAPICaller class as well as a direct call to GetAsync in the View Model so you can test it out by commenting and uncommenting.)
Any idea as to what I am doing wrong?
Link to full sample project:
https://1drv.ms/u/s!Ams6cZUzaeQy3M8uGAuaGggMt0Fi-A
So here is what I found. It seems that awaiting the HTTPClient.GetAsync was causing the error. (Pretty sure the thread was blocked.) So to start with I took out the await and added code to delay the task when HTTPClient's return task was not completed.
var lobj_Result = lobj_HTTPClient.GetAsync(ps_URI);
while (!lobj_Result.IsCompleted)
{
Task.Delay(100);
}
Because I no longer await the call in the LoadData method, I was able to remove the async Task declaration and simply make it a method.
public void LoadData()
{
HttpClient lobj_HTTPClient = null;
HttpResponseMessage lobj_HTTPResponse = null;
string ls_Response = "";
try
{
IsDataLoaded = false;
string ls_WorkLanguageURI = ic_LanguagesAPIUrl;
//Get the Days of the Week
lobj_HTTPClient = new HttpClient(new NativeMessageHandler());
lobj_HTTPClient.BaseAddress = new Uri(App.APIPrefix);
lobj_HTTPClient.DefaultRequestHeaders.Accept.Clear();
lobj_HTTPClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//This call will not work
WebAPICaller lobj_APICaller = new WebAPICaller();
ls_Response = lobj_APICaller.CallWebService(ls_WorkLanguageURI).Result;
if (ls_Response.Trim().Length > 0)
{
if (this.Items_ForList != null)
this.Items_ForList.Clear();
Items_ForList = JsonConvert.DeserializeObject<ObservableCollection<GBSLanguage_ForList>>(ls_Response);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
this.IsDataLoaded = true;
NotifyPropertyChanged("GBSLanguages_ForList");
}
}
The I could remove the async from my on appearing event and simply call the loaddata event synchronously.
if (!ViewModelObjects.Languages.IsDataLoaded)
ViewModelObjects.Languages.LoadData();
After making all these changes, the desired result was achieved. Everything ran in synchronous manner (I know the call to GetAsync function of the HTTPClient is still async) and did not return until the results were retrieve and loaded into the object.
I hope this helps other people out. :)
There are a couple of things I see going on. First, the commented-out code:
//This call will not work
//WebAPICaller lobj_APICaller = new WebAPICaller();
//ls_Response = lobj_APICaller.CallWebService(ls_WorkLanguageURI).Result;
Is using .Result instead of await. This may be the root of your problem. Using .Result should be avoided in general, just use await. See the answer here Await on a completed task same as task.Result? for more details on why.
The second thing is that you are already on the UI thread when OnAppearing is called, so there is no need to call Device.BeginInvokeOnMainThread. What you're doing in that method currently is equivalent to:
protected override async void OnAppearing()
{
if (!ViewModelObjects.Languages.IsDataLoaded)
await ViewModelObjects.Languages.LoadData();
}
The next question is whether or not it's a great idea to be doing this in OnAppearing(). This can cause your app to seem non-responsive.
The general use of Device.BeginInvokeOnMainThread is for when you don't know if you're currently on the main thread, but these UI event handlers always are called on the main thread by the Xamarin platform.
Based on the source code on github
void IPageController.SendAppearing()
{
if (_hasAppeared)
return;
_hasAppeared = true;
if (IsBusy)
MessagingCenter.Send(this, BusySetSignalName, true);
OnAppearing();
EventHandler handler = Appearing;
if (handler != null)
handler(this, EventArgs.Empty);
var pageContainer = this as IPageContainer<Page>;
((IPageController)pageContainer?.CurrentPage)?.SendAppearing();
}
there is still a way to do this with async/await approach.
You will notice that the OnAppearing method is called just before the event is triggered.
Subscribe to the Appearing event of the page/view
protected override void OnAppearing() {
this.Appearing += Page_Appearing;
}
and create an async method like you did originally but this time have it on an actual even handler
private async void Page_Appearing(object sender, EventArgs e) {
if (!ViewModelObjects.Languages.IsDataLoaded)
await ViewModelObjects.Languages.LoadData();
//unsubscribing from the event
this.Appearing -= Page_Appearing;
}
This way there is no need to busy wait delay the thread while waiting for the task to complete.

Windows Forms and Background Images

I'm using SlimDX to render to a Windows Panel within a form using Visual C# Express. SlimDX derives a class from System.Windows.Forms.Form called SlimDX.Windows.RenderForm. I need to use this class in order to handle the windows message loop. I in turn must derive a class from SlimDX.Windows.RenderForm in order to add controls. My derived class looks like this:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SlimDX;
using SlimDX.Windows;
namespace SceneGenerator
{
public partial class ApplicationForm : RenderForm
{
public ApplicationForm() { }
public ApplicationForm( string text )
{
InitializeComponent();
this.Text = text;
}
public Panel GetViewport() { return this.ViewportPanel; }
}
}
Now though my background image does not show up in Visual C# environment( nor during runtime ). The background image is set in the usual InitializeComponent() function of the base class...Here are my designer and entry point codes:
namespace SceneGenerator
{
partial class ApplicationForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ApplicationForm));
this.ViewportPanel = new System.Windows.Forms.Panel();
this.SuspendLayout();
//
// ViewportPanel
//
this.ViewportPanel.Location = new System.Drawing.Point(410, 12);
this.ViewportPanel.Name = "ViewportPanel";
this.ViewportPanel.Size = new System.Drawing.Size(475, 345);
this.ViewportPanel.TabIndex = 0;
//
// ApplicationForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("$this.BackgroundImage")));
this.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.ClientSize = new System.Drawing.Size(897, 788);
this.Controls.Add(this.ViewportPanel);
this.DoubleBuffered = true;
this.Name = "ApplicationForm";
this.Text = "ApplicationForm";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.Panel ViewportPanel;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Drawing;
using System.Runtime.InteropServices;
using SlimDX.Windows;
using SlimDX.Direct3D9;
using Device = SlimDX.Direct3D9.Device;
using Resource = SlimDX.Direct3D9.Resource;
namespace SceneGenerator
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Form1());
// form
var form = new ApplicationForm("Scene Generator");
Panel viewportPanel = form.GetViewport();
// device
var device = new Device(new Direct3D(), 0, DeviceType.Hardware, viewportPanel.Handle, CreateFlags.HardwareVertexProcessing, new PresentParameters()
{
BackBufferWidth = viewportPanel.Width,
BackBufferHeight = viewportPanel.Height
});
MessagePump.Run(form, () =>
{
device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.Green, 1.0f, 0);
device.BeginScene();
device.EndScene();
device.Present();
});
}
}
}
Why might this occur? Seems related to Visual C# Express and my lack of instructions to use the background image...
This is a problem caused by this RenderForm class. It overrides the OnPaintBackground() method and does nothing. So you don't see the BackgroundImage, you don't see BackColor either. You do get interesting LSD-style psychedelic effects in the designer. You can fix thix by writing your own:
protected override void OnPaintBackground(PaintEventArgs e) {
if (this.BackgroundImage != null) {
e.Graphics.DrawImage(this.BackgroundImage, this.DisplayRectangle);
}
}
But you'd better work from the assumption that this was done intentionally to solve some kind of drawing interference problem. Which you'll probably get back by using a Panel, you'd have to derive your own and in turn override its OnPaintBackground() method:
using System;
using System.Windows.Forms;
class RenderPanel : Panel {
protected override void OnPaintBackground(PaintEventArgs e) {
// Do nothing, like RenderForm
}
}
Might work, but this is pretty iffy stuff, not entirely uncommon for open source. This just doesn't look like it was made to do what you like to do. Best to avoid trouble and pass the form's Handle to the Device constructor.
Try referencing it as an external resource as in via a direct path or something. The way I'd try it is:
Environment.CurrentDirectory + #"\backgroundimage.png"
And if it still doesn't work like that one of two things might be happening.
Your background image is corrupt somehow
SlimDX might not accept BackgroundImages properly
Now I've never used SlimDX before so I'm not entirely sure on this but you could also try referencing it via the designer.

Download many images to listbox

I am writing Windows Phone application and can't understand why the emulator crashes. It says that: "Unhandled exception at 0x773415de in XDE.exe: 0xC0000005: Access violation reading location 0x05190000." This problem appears only when I begin to scroll very fast my listbox. I load per 20 items to listbox (the number of all items is 200). The problem is in my class of downloading images. I create a Webclient request for each image to server or it's better to create a thread pool?
In XAML:
<Image Name="userPic" Margin="0,8,1,2"
DelayFix:LowProfileImageLoader.UriSource="{Binding photoUrl}" Stretch="Fill"
Width="60"
Height="60"/>
downloadImage class:
using System;
using System.Net;
using System.Windows.Resources;
using System.Windows.Media.Imaging;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Phone;
using System.Diagnostics;
namespace PhoneApp
{
public class downloadImage
{
public int ImageIndex;
public string ImageURL;
public Boolean Downloaded;
public BitmapImage Bitmap;
public Image destinationImage;
private WebClient client;
public delegate void FileCompleteHandler(object sender);
public event FileCompleteHandler FileCompleteEvent;
private static readonly object _syncBlock = new object();
public downloadImage(int index, string imageURL, Image destinationImage = null)
{
this.ImageIndex = index;
this.ImageURL = imageURL;
this.destinationImage = destinationImage;
}
public void Download()
{
client = new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
client.OpenReadAsync(new Uri(this.ImageURL, UriKind.Absolute));
}
private void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null && e.Result != null)
{
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
StreamResourceInfo sri = new StreamResourceInfo(e.Result as Stream, null);
if (destinationImage == null)
{
this.Bitmap = new BitmapImage();
this.Bitmap.SetSource(sri.Stream);
}
else
{
this.destinationImage.Source = PictureDecoder.DecodeJpeg(e.Result);
}
this.Downloaded = true;
if (FileCompleteEvent != null)
FileCompleteEvent(this);
}));
}
}
}
}
The code of my class:
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Diagnostics;
using System.ComponentModel;
using Microsoft.Phone;
using Microsoft.Phone.Info;
namespace DelayFix
{
/// <summary>
/// Provides access to the Image.UriSource attached property which allows
/// Images to be loaded by Windows Phone with less impact to the UI thread.
/// </summary>
public static class LowProfileImageLoader
{
private static readonly object _syncBlock = new object();
private static bool _exiting;
/// <summary>
/// Gets the value of the Uri to use for providing the contents of the Image's Source property.
/// </summary>
/// <param name="obj">Image needing its Source property set.</param>
/// <returns>Uri to use for providing the contents of the Source property.</returns>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "UriSource is applicable only to Image elements.")]
public static Uri GetUriSource(Image obj)
{
if (null == obj)
{
throw new ArgumentNullException("obj");
}
return (Uri)obj.GetValue(UriSourceProperty);
}
/// <summary>
/// Sets the value of the Uri to use for providing the contents of the Image's Source property.
/// </summary>
/// <param name="obj">Image needing its Source property set.</param>
/// <param name="value">Uri to use for providing the contents of the Source property.</param>
[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters", Justification = "UriSource is applicable only to Image elements.")]
public static void SetUriSource(Image obj, Uri value)
{
if (null == obj)
{
throw new ArgumentNullException("obj");
}
obj.SetValue(UriSourceProperty, value);
}
/// <summary>
/// Identifies the UriSource attached DependencyProperty.
/// </summary>
public static readonly DependencyProperty UriSourceProperty = DependencyProperty.RegisterAttached(
"UriSource", typeof(Uri), typeof(LowProfileImageLoader), new PropertyMetadata(OnUriSourceChanged));
/// <summary>
/// Gets or sets a value indicating whether low-profile image loading is enabled.
/// </summary>
public static bool IsEnabled { get; set; }
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Static constructor performs additional tasks.")]
static LowProfileImageLoader()
{
Debug.WriteLine("Limit Memory:" + DeviceStatus.ApplicationMemoryUsageLimit / 1024 / 1024);
}
private static void HandleApplicationExit(object sender, EventArgs e)
{
Debug.WriteLine("Exit!");
}
private static void OnUriSourceChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
lock (_syncBlock)
{
var image = (Image)o;
var uri = (Uri)e.NewValue;
Debug.WriteLine("Memory:" + DeviceStatus.ApplicationCurrentMemoryUsage / 1024 / 1024);
if (DesignerProperties.IsInDesignTool || !uri.IsAbsoluteUri)
{
image.Source = PhoneAppVKContest.ImageUtils.getJpegImage(uri.OriginalString);
}
else
{
PhoneAppVKContest.downloadImage di = new PhoneAppVKContest.downloadImage(0, uri.AbsoluteUri);
di.destinationImage = image;
di.FileCompleteEvent += new PhoneAppVKContest.downloadImage.FileCompleteHandler(di_FileCompleteEvent);
di.Download();
}
}
}
static void di_FileCompleteEvent(object sender)
{
Debug.WriteLine("Download success!");
}
}
}
When you are creating webClient request for each image, then a conflict may arise when the webClient is bombarded with all the requests. You need to order the requests sequentially via a queue. If the image in the queue's first element is already in the cache or IS, link it, else check if webclient is busy or not. If busy then send it to the end of the queue. And check for the next image. If its not busy, submit a downloading request. This is the method which I have followed. You might also consider binding the Imagesource for each image inorder to prevent auto reloading on page reload or due to problem in scroll cache. I hope this answer helps you.

Windows form loads then quits

I'm creating a checkout system for a supermarket. It consists of a checkout, server and MIS program an operates WCF services between them. The problem I have is that the checkout program, which is a windows form, does a few neccessaries in it's application_load method and then just quits.
Here's the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CheckoutLibrary;
using Checkout.ServerLibraryService;
using Checkout.MarketService;
namespace Checkout
{
public partial class theForm : Form
{
private static int checkoutID = 3;
private Product[] allProducts;
public theForm()
{
InitializeComponent();
}
private void theForm_Load(object sender, EventArgs e)
{
// First cache all products
SupermarketServiceSoapClient marketService = new SupermarketServiceSoapClient();
allProducts = marketService.GetAllProducts();
// Load the service provided by the server
ServiceClient serverService = new ServiceClient();
// Load the event handlers for the bar code scanner
BarcodeScanner scanner = new BarcodeScanner();
scanner.ItemScanned += new BarcodeScanner.ItemScannedHandler(scanner_ItemScanned);
scanner.AllItemsScanned += new BarcodeScanner.AllItemsScannedHandler(scanner_AllItemsScanned);
scanner.Start(checkoutID);
}
void scanner_AllItemsScanned(EventArgs args)
{
throw new NotImplementedException();
}
void scanner_ItemScanned(ScanEventArgs args)
{
itemTextBox.Text = "Scanned " + GetItemName(args.Barcode);
}
private void scanItemButton_Click(object sender, EventArgs e)
{
scanner_ItemScanned(new ScanEventArgs(GetRandBarcode()));
}
// A barcode -> product name look up method
public string GetItemName(int barcode)
{
return allProducts[barcode].Description + " # " + allProducts[barcode].Price;
}
// Method to grab a random barcode for simulation
private int GetRandBarcode()
{
Random rand = new Random();
return rand.Next(0,500);
}
}
}
And program.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace Checkout
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new theForm());
}
}
}
Thanks for any insight.
In WinForms, if your form_load throws an exception, it quits without displaying anything. Annoying, but I'm guessing that's the problem.
You can try a try/catch, or you can hit CTRL+ALT+E and check the Thrown Column for Common Language Runtime Exceptions to see the error.
UPDATE:
Based on comments, here's a sample way to execute something on another thread.
ThreadStart ts = new ThreadStart(() => {
try {
scanner.Start(checkoutID);
} catch {
// Log error
}
});
Thread t = new Thread(ts);
t.Start();

Hide form at launch

I have a program which only needs a NotifyIcon to work as intended. So I've been trying to get the main form to hide when the program starts.
In frmMain_Load, I tried both
this.Hide();
this.Visible = false;
without success.
They work in other methods, like in the NotifyIcon_MouseClick-method, but I want it to hide at Load.
I saw in another question here at SO where Matias suggested this:
BeginInvoke(new MethodInvoker(delegate
{
Hide();
}));
This works, but when I launch the program I can see the form flashing real fast. It's better than nothing, but I wonder if there is any better solution to this.
Thanks.
// In Your Program.cs Convert This
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
// To This
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form1 TheForm = new Form1();
Application.Run();
}
// Call Application.Exit() From Anywhere To Stop Application.Run() Message Pump and Exit Application
There is an easy way, if your program has the default Visual Studio generated Program.cs file:
[STAThread]
static void Main()
{
Application.EnableVisualStyles ();
Application.SetCompatibleTextRenderingDefault (false);
Application.Run (new MainForm ());
}
the simple fact of calling Run will, indeed make the form visible. Try doing the following in the properties of your form:
Set WindowState to Minimized
Set ShowInTaskbar to false
This should do the trick!
Don't call Show or ShowDialog on your form, you can have your Application.Run target a custom class that then instantiates a form and doesn't show or creates a NotifyIcon instance and handles everything from there.
You can also put this.hide = true in the form_shown event. I believe that event is fired once only and after the load event. You might see alittle flicker though if your form has a lot of controls and/or the computer is slow.
If your program doesn't require a form to run, then the best method is to not have a form at all.
Setup your NotifyIcon in the Program code, and enter a loop until you want to exit the program by setting some value, or calling some method.
In this example setting UserExitCalled to true (Program.UserExitCalled = true) will cause the program to quit.
Here is a brief example:
static class Program {
internal static Boolean UserExitCalled;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Setup your tray icon here
while (!UserExitCalled) {
Application.DoEvents(); // Process windows messages
Thread.Sleep(1);
}
return;
}
}
Here the full program class from one of my system tray applications as a working example.
// *********************************************************************
// [DCOM Productions .NET]
// [DPDN], [Visual Studio Launcher]
//
// THIS FILE IS PROVIDED "AS-IS" WITHOUT ANY WARRANTY OF ANY KIND. ANY
// MODIFICATIONS TO THIS FILE IN ANY WAY ARE YOUR SOLE RESPONSIBILITY.
//
// [Copyright (C) DCOM Productions .NET All rights reserved.]
// *********************************************************************
namespace VisualStudioLauncher
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.Threading;
using VisualStudioLauncher.Common.Objects;
using VisualStudioLauncher.Forms;
using System.Drawing;
using VisualStudioLauncher.Common.Data;
using System.IO;
static class Program
{
#region Properties
private static ProjectLocationList m_ProjectLocationList;
/// <summary>
/// Gets or Sets the ProjectsLocationList
/// </summary>
public static ProjectLocationList ProjectLocationList
{
get
{
return m_ProjectLocationList;
}
set
{
m_ProjectLocationList = value;
}
}
private static ShellProcessList m_ShellProcessList = null;
/// <summary>
/// Gets or Sets the ShellProcessList
/// </summary>
public static ShellProcessList ShellProcessList
{
get
{
return m_ShellProcessList;
}
set
{
m_ShellProcessList = value;
}
}
private static NotifyIcon m_TrayIcon;
/// <summary>
/// Gets the programs tray application.
/// </summary>
public static NotifyIcon TrayIcon
{
get
{
return m_TrayIcon;
}
}
private static bool m_UserExitCalled;
/// <summary>
/// Gets a value indicating whether the user has called for an Application.Exit
/// </summary>
public static bool UserExitCalled
{
get
{
return m_UserExitCalled;
}
set
{
m_UserExitCalled = value;
}
}
// TODO: Finish implementation, then use this for real.
private static ApplicationConfiguration m_ApplicationConfiguration = null;
/// <summary>
/// Gets the application configuration
/// </summary>
public static ApplicationConfiguration ApplicationConfiguration
{
get
{
if (m_ApplicationConfiguration == null)
m_ApplicationConfiguration = ApplicationConfiguration.LoadConfigSection(#"./settings.config");
return m_ApplicationConfiguration;
}
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (args.Length > 0)
{
if (args[0].ToLower() == "-rmvptr")
{
for (int i = 1; i < args.Length; i++) {
try {
if (File.Exists(Application.StartupPath + #"\\" + args[i])) {
File.Delete(Application.StartupPath + #"\\" + args[i]);
}
}
catch { /* this isn't critical, just convenient */ }
}
}
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SplashForm splashForm = new SplashForm();
splashForm.Show();
while (!UserExitCalled)
{
Application.DoEvents();
Thread.Sleep(1);
}
if (m_TrayIcon != null)
{
m_TrayIcon.Icon = null;
m_TrayIcon.Visible = false;
m_TrayIcon.Dispose();
GC.Collect();
}
}
#region System Tray Management
public static void SetupTrayIcon()
{
m_TrayIcon = new NotifyIcon();
m_TrayIcon.Text = Resources.UserInterfaceStrings.ApplicationName;
m_TrayIcon.Visible = false; // This will be set visible when the context menu is generated
m_TrayIcon.MouseDoubleClick += new MouseEventHandler(m_TrayIcon_MouseDoubleClick);
if (Orcas.IsInstalled)
{
m_TrayIcon.Icon = Orcas.Icon;
}
else if (Whidbey.IsInstalled) {
m_TrayIcon.Icon = Whidbey.Icon;
}
else {
m_TrayIcon.Icon = SystemIcons.Warning;
m_TrayIcon.Text = "Visual Studio is not installed. VSL cannot run properly.";
}
}
static void m_TrayIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
{
return;
}
SettingsForm settingsForm = new SettingsForm();
settingsForm.Show();
}
#endregion
}
}

Categories

Resources