Friday, September 25, 2009

Learning Silverlight – Miscellaneous Tips

How to use OpenFileDialog?

OpenFileDialog ofd = new OpenFileDialog();
ofd.Multiselect = false;
ofd.Filter = "xml files (*.xml)|*.xml";
if (ofd.ShowDialog()==true)
{
string fileContent = ofd.File.OpenText().ReadToEnd();
// do some thing
}





How to use SaveFileDialog?





SaveFileDialog sfd = new SaveFileDialog();
sfd.Filter = "xml files (*.xml)|*.xml";
if (sfd.ShowDialog() == true)
{
byte[] fileBytes = System.Text.Encoding.UTF8.GetBytes(fileString); // fileString would be something you want to save to the file

using (Stream fs = (Stream)sfd.OpenFile())
{
fs.Write(fileBytes, 0, fileBytes.Length);
fs.Close();
}
}







How to parse XML?



To parse a XML file like this:





<List type="DotNetIdeas.CF.RecipeBox.RecipeBox">
<category name="Breakfast" order="0">
<item name="pan cake"/>
<item name="milk"/>
<category name="Desert" order="1">
</List>




You can do something like this:





XDocument xd = XDocument.Parse(xml);

XElement node = xd.Root;

var categories = (from category in xd.Descendants("category")
select new Category()
{
Name = category.Attribute("name").Value,
Order = Convert.ToInt32(category.Attribute("order").Value),
Items = FromXml(category.Elements("item"))
}).ToList();





If you happened to be like me, migrating the code from some old programs, don’t forget to add “using System.Linq” which is in System.Core.dll. Otherwise you will get a compiler error.



How to use TreeView? 



image



To create a TreeView like this one, you will need to use HierarchicalDataTemplate. It is in System.Windows.Controls. So add the following namespace first:




xmlns:control="clr-namespace:System.Windows;assembly=System.Windows.Controls"






<controls:TreeView x:Name="ingredientList" Height="330" Width="248" Canvas.Left="17" Canvas.Top="18">
<controls:TreeView.ItemTemplate>
<control:HierarchicalDataTemplate ItemsSource="{Binding Items}">
<StackPanel Orientation="Horizontal">
<CheckBox x:Name="itemCheckbox" IsChecked="{Binding ItemChecked, Mode=TwoWay}"/>
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</control:HierarchicalDataTemplate>

</controls:TreeView.ItemTemplate>
</controls:TreeView>



 


How to force data binding in TextChanged event?


When a text box has data binding, it will not update the source until it lost the focus. Sometimes we want the source to change as soon as we type something. Here is how to do it:



private void ingredientName_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
BindingExpression bExp = ingredientName.GetBindingExpression(TextBox.TextProperty);
if (bExp != null)
{
bExp.UpdateSource();
}
}







Learning Silverlight – How to programmatically add animation?

Recently I created a little Silverlight application called “LittleMath”. It is a simple number addition game. It randomly generate two numbers between 0 and 9. The user clicks on the number pad to give the answer and check it by clicking on “Check Answer”. This is my first Silverlight application. I found it very interesting. With the help of Expression Blend, you can build nice looking application with animation fairly quickly. However, sometimes you may need to animation programmatically. In my demo, when you check “Show Beads”, it will show the red beads one by one according to the randomly generated numbers. Here is how to do it.

First we create a story board and animation duration for each bead:

private double beadDuration = 0.5;
private Storyboard showBeadsStoryBoard = new Storyboard();






Then add the story board to resources with a name




LayoutRoot.Resources.Add("showBeads", showBeadsStoryBoard);



 


Before showing the beads, we need to clear the existing beads. Then set the story board duration based on the total numbers. For each bead, we create a DoubleAnimation so the bead will gradually appear on the screen. Because we want the bead to show up one by one, we will need to set the BeginTime. The rest is pretty straight forward.




private void ShowBeads()
{
ClearBeads();
int num1 = Convert.ToInt32(number1.Text);
int num2 = Convert.ToInt32(number2.Text);
Duration storyBoardduration = new Duration(TimeSpan.FromSeconds(beadDuration * (num1 + num2)+1));
showBeadsStoryBoard.Duration = storyBoardduration;
Duration duration = new Duration(TimeSpan.FromSeconds(beadDuration));

for (int i = 0; i < num1; i++)
{
AddBead(beadsPanel1, TimeSpan.FromSeconds(beadDuration * i + 1), duration);
}

for (int i = 0; i < num2; i++)
{
AddBead(beadsPanel2, TimeSpan.FromSeconds(beadDuration * (num1 + i) + 1), duration);
}

// Begin the animation.
showBeadsStoryBoard.Begin();
}

private void AddBead(StackPanel beadsPanel, TimeSpan beginTime, Duration duration)
{
// Add bead
Ellipse bead = new Ellipse();
bead.Width = 20;
bead.Height = 20;
bead.Margin = new Thickness(0);
bead.Fill = beadBrush;
bead.Opacity = 0;
beadsPanel.Children.Add(bead);

// Add animation
DoubleAnimation beadDoubleAnimation = new DoubleAnimation();
beadDoubleAnimation.Duration = duration;
beadDoubleAnimation.BeginTime = beginTime;
showBeadsStoryBoard.Children.Add(beadDoubleAnimation);
Storyboard.SetTarget(beadDoubleAnimation, bead);
Storyboard.SetTargetProperty(beadDoubleAnimation, new PropertyPath("(UIElement.Opacity)"));
beadDoubleAnimation.To = 1;
}




private void ClearBeads()
{
showBeadsStoryBoard.Stop();
beadsPanel1.Children.Clear();
beadsPanel2.Children.Clear();
}





It is really neat.





Thursday, September 10, 2009

Memory leak issue in “How Do I: Programmatically Monitor for a Specific Time of Day Without Draining a Device Battery?”

Recently I worked on improving our SmartOrganizer 3.1 application. I need to add notification to it, so when a task or appointment is due, the application will prompt the user. Also I want this to run efficiently so it won’t drain the device battery. I found some sample code from “How Do I” video for Device.

Though the code worked well initially, I found a couple issues which caused memory leak later. Here I will post the original code and my fix.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using HowDoI.Examples;
using System.Threading;

namespace TimeOfDayEvent_CS
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

const string _eventName = @"TimeOfDayEventExample";
const string _timeOfDayEventName = @"\\.\Notifications\NamedEvents\" + _eventName;
IntPtr _nativeWaitHandle = IntPtr.Zero;
Thread _backgroundThread = null;
bool _programIsShuttingDown = false;
private void menuSetTimeNtfy_Click(object sender, EventArgs e)
{
// Declare and convert time values
DateTime targetTime = GetTargetTime();
long targetTimeAsFileTimeUTC = targetTime.ToFileTime();
long targetTimeAsFileTimeLocal = 0;
Win32.FileTimeToLocalFileTime(ref targetTimeAsFileTimeUTC,
ref targetTimeAsFileTimeLocal);
SystemTime targetTimeAsSystemTime = new SystemTime();
Win32.FileTimeToSystemTime(ref targetTimeAsFileTimeLocal, targetTimeAsSystemTime);

// Create named Win32 Event Object
_nativeWaitHandle = Win32.CreateEvent(0, 0, 0, _eventName);

// Start the background thread
_backgroundThread = new Thread(ThreadFunction);
_backgroundThread.Start();

Win32.CeRunAppAtTime(_timeOfDayEventName, targetTimeAsSystemTime);
}

void ThreadFunction()
{
// Wait for the event to signal
// When signaled our target time has happened
Win32.WaitForSingleObject(_nativeWaitHandle, -1);

// Do our Target Time processing
if (!_programIsShuttingDown)
BeginInvoke((TimeHasOccurredDelegate)TimeHasOccurred, new object[] { DateTime.Now });
//TimeHasOccurred(DateTime.Now);
}

void TimeHasOccurred(DateTime time)
{
notification1.Text =
string.Format("
<html><body><font color=\'#000000\'><b>It's Time: {0}<b></font></body></html>",
time.ToString());
notification1.Visible = true;
Debug.WriteLine("
It's Time: " + time.ToString());
}

delegate void TimeHasOccurredDelegate(DateTime time);

private DateTime GetTargetTime()
{
//DateTime targetTime = new DateTime(2007, 8, 31, 10, 15, 0);
// For demo purposes pick date time 1 minute in the future
DateTime currentTime = DateTime.Now;
DateTime targetTime = currentTime + new TimeSpan(0, 1, 0);

Debug.WriteLine("
CurrentTime: " + currentTime.ToString());
Debug.WriteLine("
TargetTime: " + targetTime.ToString());

return targetTime;
}

const int _threadShutdownTimeout = 30000;
private void Form1_Closing(object sender, CancelEventArgs e)
{
if (_nativeWaitHandle != IntPtr.Zero)
{
_programIsShuttingDown = true;
Win32.SetEvent(_nativeWaitHandle);
if (_backgroundThread != null)
{
bool shutdownSucceeded = _backgroundThread.Join(_threadShutdownTimeout);
if (!shutdownSucceeded)
_backgroundThread.Abort();
}
}
}
}
}











Here is the native method calls




using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace HowDoI.Examples
{
static class Win32
{
#region Win32 Time-related Functiions

[DllImport("CoreDLL.dll")]
public static extern int CeRunAppAtTime(string application, SystemTime startTime);
[DllImport("CoreDLL.dll")]
public static extern int FileTimeToSystemTime(ref long lpFileTime, SystemTime lpSystemTime);
[DllImport("CoreDLL.dll")]
public static extern int FileTimeToLocalFileTime(ref long lpFileTime, ref long lpLocalFileTime);
[DllImport("CoreDLL.dll")]
public static extern int ShowWindow(IntPtr hWnd, int nCmdShow);

#endregion

#region Win32 Event Object Functions

[DllImport("CoreDLL.dll")]
public static extern IntPtr CreateEvent(int alwaysZero, int manualReset, int initialState, string eventName);
[DllImport("CoreDLL.dll")]
public static extern int WaitForSingleObject(IntPtr handle, int waitTimeInMilliseconds);
[DllImport("CoreDLL.dll")]
private static extern int EventModify(IntPtr handle, int eventAction);
[DllImport("CoreDLL.dll")]
public static extern int CloseHandle(IntPtr handle);

public static int SetEvent(IntPtr handle)
{
const int EVENT_SET = 3;
return EventModify(handle, EVENT_SET); // in WM, SetEvent, ResetEvent, & PulseEvent are all implemented as EventModify
}

#endregion

}

#region Win32 SystemTime

[StructLayout(LayoutKind.Sequential)]
public class SystemTime
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}

#endregion

}







The time monitoring and notification in above code worked perfectly. However, I noticed that if I stop the application before the time is up, the application disappeared from the task manager, but the memory was not released. Furthermore, I used the Remote Process Viewer to check which process is running. I can see that process is still there. As I was writing this blog, I noticed that it only happen on device, not emulator. For some reasons, the device couldn’t kill the background thread when it is not done. The solution is. in fact, pretty simple - reset the timer. Add the following line of code before Join the background thread. [currentTime] is current time in SystemTime format.




Win32.CeRunAppAtTime(_timeOfDayEventName, [currentTime]);
bool shutdownSucceeded = _backgroundThread.Join(_threadShutdownTimeout);



Friday, September 4, 2009

How to make an icon’s background transparent using Visual Studio Icon Editor?

To make an icon’s background transparent, you open the icon file using Visual Studio Icon Editor, choose the “Fill Tool”. Then choose the “Transparent” color from “Colors” on the left. Click on the areas you want to make transparent. That’s it.

image

Wednesday, September 2, 2009

Microsoft Bing on SmartPhone

To download “Bing”, type m.bing.com in your web browser. Click “Download interactive maps” for Windows Mobile. Then following the on-screen instruction.

image

After the installation, you will see the “Bing” icon in Programs.

image

Tap on “Bing” icon to run it. Below is the dashboard of Bing. The Map is very similar to Google Map. It also use cellular phone tower to decide your location, so it is not very accurate.

image

The two features I really like is the “Traffic” and “Movies”. The “Traffic” map seems accurate and it automatically update every 2 minutes. The “Movies” is easy to navigate. You can quickly find the theater a movie is playing in or check the show time of any particular theater around your area.

image image 

The “Gas Princes” searches the lowest gas price around your area. But I think they could add more sorting capabilities.

image

“Categories” allow you search local business by category. It is like a yellow page and I think it is very handy.

“Collections” has some predefined search criteria. I found the “Upcoming event” is kind of strange.  It is not sorted by date/time. It even listed classes at some hospitals.

“Bing” crashed a couple times when the memory on my cell phone running low. With all the maps and automatic updates, you could imagine that it requires a lot of resources. But overall, I think “Bing” is a really nice application.