Contents
Introduction
It's a Media Player that works with transparency effects. It also plays MP3 and wav files, displays the tags if present and displays the album art image if present.
Original Media Player user interface is impossible to control using your finger. Here I have tried to solve this problem.
As a sample, I used an interface just like the iPod touch one. In this tutorial, mostly I continue to explain a better way to work with transparency on Windows Mobile. For an introduction to this article, I suggest you read my previous article on transparency iPhone UI[^].
In this article, you can also read about:
- Resolution-aware and Orientation-aware
- Dynamic graphic text resize
- Mouse gesture
- Intercepting button
- Work with Windows Media Player OCX on Windows Mobile
Background
I found some problem with sound files in particular for retrieving the tag info and the current position time, after a long search, I chose to use the Windows Media Player OCX. The article Audio Book Player [^] by brochpirate gives you a fast way to use the OCX on Mobile through the OpenNETCF.org, it's a great working sample. Here, I explain a bit about how you can play with it.
How to Work with WMP
To use the Windows Media Player, you only need to import this DLL as reference:
OcxControls
OpenNETCF.Windows.Forms.AxHost
WMPLib
And add the cPlayer
to your app. Now you are ready to get control of WMP, really cool, isn't it?
Using the Code
The Solution
Going deep into the solution, you can find:
iPlayer
class, the main form with: Resolution-aware, dynamic graphic text resize, mouse gesture, and work also with Windows Media Player COM
cPlayer
class, this class works with Media Player OCX, it's under the abPlayer
. I add only few properties - the loop and the volume
FilesManger
class, this class gets all the MP3 and Wav files of your mobile device
ImageButtons
class, a class button with transparency and other stuff
PlatformAPIs
class, it is the class to manage the P/Invoke
SlideButton
class, the class that moves the Image (progress)
- BMPs folder, it contains all the BMPs used in the code
iPlayer Class
This is the main form of the app. I paint all the forms in the method myPaint(Graphics dc)
. The myPaint
can be called from the Paint
eventhandler and use the e.Graphics
or can be called when a state changes and the dc
used is the CreateGraphics()
. Then my paint draws all the screen with all the controls inside, beginning with the background, the TopBar
, the SongBar
, LocationBar
(if needed), BottomBar
, and the various buttons used.
The Background
The bitmapBackImage
Bitmap is loaded as a background and the default image of the first image found in the song directory. To choose the image, I preferred to display the "Folder.jpg" if it exists because it is the default used by the default internet CD searching.
This is the code:
private void SearchBGImage()
{
if (FormListScanFiles.lvwSoundFiles.Items.Count != 0)
{
string path = Player.PlayingPath.Remove(Player.PlayingPath.LastIndexOf(@"\"),
Player.PlayingPath.Length - Player.PlayingPath.LastIndexOf(@"\"));
String[] ImageFiles = GetImageFiles(path);
if (ImageFiles.Length != 0)
{
foreach (string ImageFile in ImageFiles)
{
if (ImageFile.Remove(0, ImageFile.LastIndexOf(@"\") + 1).ToLower() ==
"folder.jpg")
{
bitmapBackImage = new Bitmap(ImageFile);
return;
}
}
bitmapBackImage = new Bitmap(ImageFiles[0]);
return;
}
}
bitmapBackImage = bitmapBackImageDefault;
}
The default image:
If I need to stretch the image, I use this code:
Rectangle srcRect = new Rectangle(0, 0, bitmapBackImage.Width, bitmapBackImage.Height);
Rectangle destRect = new Rectangle(0, 0, this.Width, this.Height);
gxBuffer.DrawImage(bitmapBackImage, destRect, srcRect, GraphicsUnit.Pixel);
else I draw centre & unscaled:
int x = (this.Width - this.bitmapBackImage.Width) / 2;
int y = (this.Height - this.bitmapBackImage.Height) / 2;
gxBuffer.DrawImage(this.bitmapBackImage, x, y);
The Topbar
This part is similar to the iPhone UI article but here I change the position of the images dynamically. The Topbar
is divided in 4 images and the time text:
- The Battery level, with a left anchor with no stretch
- The timeline, with left anchor (the battery level width), right anchor (the GSM level width +
- Current Player status width), use the stretch
- Current Player status, with left anchor (timeline width+ battery level width), right anchor (the battery level width) no stretch
- The GSM level, with a right anchor with no stretch
- The time is drowned in the middle of the screen width and in the middle centred in the middle of the timeline (timeline height/2- text height/2)
All the images:
- Battery Level Bitmaps
- GSM Level Bitmaps
- Top Player status
and these two samples of the view:
The SongBar with text resize
This part is a stretch of a single bitmap to all the width of the screen and with a fixed height.
This is the original bitmap:
And these are two stretch samples:
Over it, I put two ImageButton
s, one for Exit and one for the files list. The buttons are centred in the middle of the SongTopBar
bitmap height, and the exit has the left anchor, the right has the right anchor.
In the middle of the screen, I put the Artist name, the song name and the album name. The strings are the tags retrieved from the current file selected.
Here I use my DynamicGraphicTextResizing
for a "Dynamic graphic text resize". If the width of the displayed string is much greater than the display width, I modify the string
with the minimum size that fits the screen weight and I add to the end "..."
private void DynamicGraphicTextResizing(ref string text,
ref SizeF size, Font songTitleFont)
{
while (size.Width > this.Width - buttonExit.Image.Width - buttonList.Image.Width)
{
text = text.Remove(text.Length - 4, 4);
text = text + "...";
size = gxBuffer.MeasureString(text, songTitleFont);
}
}
To draw the string
, I use this code:
string title = Player.GetMediaTAG(eTagNames.TITLE);
SizeF sizeTitle = gxBuffer.MeasureString(title, FontsongTitle);
DynamicGraphicTextResizing(ref title, ref sizeTitle, FontsongTitle);
xSong = this.Width / 2 - (int)sizeTitle.Width / 2 + buttonExit.Image.Width / 2;
gxBuffer.DrawString(title, FontSong, BrushWhite, xSong,
bitmapTopHeader.Height + sizeTitle.Height - 2);
Info: brochpirate in his article adds a way to scroll the text. Take a look there, I think you can find it useful.
These are all the images used:
This is a sample on how it works:
The LocationBar
Info: This part is shown only if the users click on the SongBar
, it displays the current location, the song duration, and a progress bar for the current location.
To show the Location bar, I use an internal bool value ShowTimeLine
and I decide to draw it if you click over the SongBar
. When the LocationBar
is shown, it enables a timer every second to draw the current location and to draw the step for the progress.
private bool SongMouseUp(MouseEventArgs e)
{
if (ClientAreaSong().Contains(e.X, e.Y))
{
ShowTimeLine = !ShowTimeLine;
timerDrawLocation.Enabled = SetTimerDrawLocation();
return true;
}
return false;
}
The LocationBar
is divided in 8 parts.
- Repeat button with 2 states, pressed On, Off
- Current Location time + the background
- Progress of the song is divided in 2 parts, the played and not played
- Duration time + the background
- Shuffle button with 2 states, pressed On, Off
Note: Here I've not covered everything in the algorithm but I believe that you will understand these easily.
The Repeat button is set to the WMP repeat state. The anchor is left and it's not stretched.
The current location time and the duration time are string retrieved from the player Player.LocationString
Player.DurationString
. The Background image is this:
The progress is divided in 2 parts, the played and not played, I use two images and I calculate the location position on the screen iWidthProgressPlayed
. After I draw the PlayedProgress
from a fixed start to the iWidthProgressPlayed
and the NotPlayedProgress
from iWidthProgressPlayed
with a dynamic width (all the size possible less than played) iWidthTotal - iWidthProgressPlayed
.
The Shuffle button is set to the WMP repeat state.
All the images used:
These are 3 samples on how it works:
Attention: I tried the app under a few devices and sometimes I found it slow, I suggest you to use the LocationBar
only if needed. The program starts with the LocationBar
hidden.
Middle of the screen
If the search doesn't find a file (MP3 or wav), it displays a text in the middle of the screen "No files found".
This is the code:
private void DrawSongNumber(Graphics gx)
{
if (FormListScanFiles.lvwSoundFiles.Items.Count == 0)
{
NoFilesFound = "No files found";
SizeF sizeNoFilesFound =
gxBuffer.MeasureString(songname, FontSongNoFilesFound);
int xSong = this.Width / 2 - (int)sizeNoFilesFound.Width / 2;
int ySong = this.Height / 2 - (int)sizeNoFilesFound.Height / 2;
int widthMessage = (int)sizeNoFilesFound.Width ;
int xMessage = (this.Width / 2 - (int)widthMessage / 2 )-10;
int yMessage = this.Height / 2 - (int)bitmapMessage.Height / 2;
DrawAlphaStretchX(gxBuffer, bitmapMessage,
170, xMessage, yMessage, widthMessage);
gxBuffer.DrawString(NoFilesFound,
FontSongNoFilesFound, BrushWhite, xSong, ySong);
}
}
and this is a sample:
The Bottom bar
For the bottom, I used a large background with the left anchor and the right anchor.
The bitmap is divided in two sections (the first half and the second half).
In the first half, I put the Player controls: Previous, Play/Pause, Next. These images are stored in three ImageButton
s. Also the player status is over in the Topbar.
The second half contains the volume slide, it's a SlideButton
. Here I add a modification on the previous posted in the iPhone UI. I add possibility to interact with the progress, and set it with a percentage. When the mouse is moved over the Slide button, the volume changes and it displays the percentage is similar to the used in the location bar.
These are all the images used:
These are two samples:
Points of Interest
I would like to remind that this program is not a complete interface. This article adds some features used in the iPhone UI and I hope you find them useful.
Note: I believe that the article contains some translation errors, please humour me and go ahead reading the article. Soon I'll translate it better.
Links
- Audio Book Player here[^], many thanks for your help BrochPirate.
- The Hosting ActiveX Controls in the .NET Compact Framework 2.0 here[^]
- The introduction of this article iPhone UI[^]
History
- 7 Jan 2009 - First release
- 10 Jan 2009 - Second release
- Minor bug fixes
- Hidden the
LocationBar
at the start
- Added the
Message
bitmap