WynApse Home Page
Home    Blog    About    Contact       
Latest Article:


My Tags:
My Sponsors:







Lifetime Member:

Silverlight GlyphMap Utility






Silverlight Beta 1 Update

This application was first done February 21, 2007 using the February CTP. Modifications to get it running include 'plumbing' and args.x and args.y as in other articles.

I also had to move my animated panels into their own <Canvas.Resources> as discussed in the Rolling Animated Gear article.

I'm trying to explicitly name the extraneous files used in the xaml, and because of that, the font name display code had to change just a bit to avoid displaying names with paths.

One other issue with this example that was not a problem before the animation changes of Beta 1 is the slide-out panels had to be started 'in'... that makes sense, and they should have been that way before, but it wasn't a problem and became one during this conversion.

Silverlight Beta 1 Update Part II

I obviously did not spend enough time on the conversion to Beta 1 the first time around on this demo. Brad, Steele, and Brian at AZGroups helped me find problems and difficult spots that I had to rethink.

First, the page did not run correctly in Firefox. In the early days of WPF/E, I made it a point to try Firefox and IE, but everything always seemed to work, so I became lax and did not continue to do that. I suppose I need to start doing that once more.

Since I was away from my development machine, I could only view the app, and doing so I found a couple things I didn't like. I was trying to be explicit on file locations, and I think I was causing unnecessary downloads that may or may not have been part of the problem, but in any case, I've resolved that issue by using local references for fonts and graphics as I had been doing in WPF/E.

I believe the big problem was that I found at least one place in the JS where I was still doing document.getElementById("ag1Host") instead of sender.getHost(). It's very likely that this was the root of the problems but I won't kow for certain until I upload new files.

In the course of externally investigating the demo, I noticed that the ToolTip for Opening the Font Menu and Skin Menu both referred to "Close" not "Open". The menus DID open with mouse click, but the ToolTip was definitely incorrect. Additionally, the glyph on the open bar was not being changed to red when the mouse moved over the bar. This had me confused for a while this evening.

Sliding Menu Problems/Resolutions

I have no idea why this was working fine in WPF/E, but now with the availability of a "Visibility" element on an object, it's possible that things are operating a bit different under the hood.

In WPF/E, I used Opacity to 'hide' elements until I wanted them shown, for instance the menus that slide out. The menu begins life opened, but with Opacity of "0", so it's essentially invisible. The problem now becomes that mouse clicks on that clear area are accepted. So mouse overs and mouse clicks were actually being answered as if they were in response to a menu close. Since the menu is simply a toggle in/out, it really didn't matter much as far as the sliding panel was concerned.

Initially, I tried moving the panel in and not changing anything else, but that still didn't work good because the activation bar is really part of the panel itself.

The solution was to use the Visibility of the menu to avoid all the mess.

Menu bar Glyph Issues

Brad pointed out a problem with the ToolTip on the open/close menu and font bars. The problem had to do with the fact that I was using a glyph for the left/right arrow, and glyphs apparently still in Beta 1 cannot accept a mouse event, so while the bar itself has a ToolTip, changes the glyph color, and opens/closes the panel, the glyph itself is a dead zone.

I resolved that issue by making a Path object for the left/right arrows and painting it as I did the glyph. Now the arrow itself allows the mouse messages, and the menu button has a much better feel... thanks Brad!

Menu Button ToolTips/ToolTips in general

Since I was investigating the ToolTips, I decided to fix the problem of the "Close Menu" button ToolTip having to be too far to the left of the menu itself. This was done to avoid interference with the menu. The reality was I just should have put the ToolTip on a canvas higher on the z-order than the slide-out menu. Once I thought to do that, it became a simple manner to add a canvas at the bottom of the xaml file just for ToolTips.

Now any ToolTip created is done so in that canvas. There can only be one ToolTip at a time, so removing a ToolTip becomes simply a "clear()" call.

Now I'll see if all this works live on the site.

Thanks Brad, Steele, and Brian... for keeping me honest :)

Silverlight Beta 1 Update Part III

Having gotten no response from the forums, I noticed that from time to time, I experienced the same issue in IE that I was seeing and hearing about in Firefox. This validated my thought that I was doing something wrong with the JavaScript, or how it was being launched.

I investigated a bit the "Loaded" in xaml vs. "OnLoad" in the CreateObject. I believe I'm seeing a race condition although I have no way to support that. Since the first few lines of function called with "Loaded" setup some global variables, I made some changes to the code after that to not need any passed-in parameters, and called the rest of that piece of code "populateMap". populateMap is what does all the createFromXaml to fill in the grid inside the artwork. Having it separated like this gives me an opportunity to have "xaml_Page_Loaded" called when the xaml is loaded, then onLoad calls "populateMap" and continue the process. This way the whole looping through rows of glyphs isn't done during the "Loaded" call.

Does this resolve the problems? There's no way to know without uploading, so we'll see... I could NOT make it work... apparently there's more to 'onLoad' in the Create script than meets the eye... back to the forum I guess.


Original Article

While I was working on the Glyph Explorer code, I kept thinking of the Windows tool, charmap, and how I could use it to display the character codes of any font. It struck me that it would be very cool to have a GlyphMap tool to display Indice values of fonts, particularly dings.

It was pointed out to me that Glyphs have a UnicodeString parameter that I thought would allow me to set a character code into the Glyph. I tried that to no avail, so I tried displaying the UnicodeString value of a Glyph as it was produced on-screen, and kept getting "Undefined", and upon further questioning, was told in the WPF/E Forum that the UnicodeString only returns what I set it to be.

The Application

In any case, I decided to build a Silverlight application to display all the Glyph Indice codes for a font, and more importantly, for a ding font. I specifically chose the ones that most of us would consider 'normal' even though they are not in the Silverlight default list yet. The fact that Indice is the only way to select the Glyph makes this even more useful!

Features

As you can see from the above Silverlight canvas, the GlyphMap application displays a map of the Glyph characters for 1 of 4 'standard' ding fonts. In addition to seeing the row/column index, I used ToolTips in a fly-over of the Glyph to show a double-sized copy of the Glyph, and the Indice necessary to display it.

The two vertical bars on the right-hand side are the menu selectors for font (top) and skin (bottom). I wanted to be able to select from a couple fonts, but didn't want to try to go through a big 'upload and display' effort, so have stopped with 4 that also fit on a small menu. A ToolTip on the menu-open button tells you that it is for font or skin menu. Clicking on the button causes the menu to slide out to the left and get an opacity of 60%. As the mouse is taken over the menu, the menu and close button become opaque. Each menu option has fly-overs as well, and the close button has a ToolTip.

Once I had the font selection menu working, it was a logical step for me to then 'skin' the application and allow re-skinning.The skin consists of only 2 pieces of artwork:
  • The outer frame
  • A 'button' that is used for both font and skin menu open and close buttons
I used Paint Shop Pro for the artwork, and will include a .pspimage file that has three selections in the alpha channel. One for the main frame, one for the indent area of for the title text, and a smaller one for the font and skin buttons.

I initially had a problem with the menu sliding in and out in that it is coming from off-screen and displaying to a specified position. I wanted it to appear to come out of the skin itself and if I were better with the graphics, I'd like it to appear out of a slot in the skin. The problem I had was clipping the animated rectangle to it's resting position. I posed the question in the WPF/E forum, and Bryant Likes resolved it for me.

Interesting Silverlight Concepts Included

Beyond basic functionality, there are some interesting Silverlight concepts used in this:
  • Building xamlFragements and executing createFromXaml in JavaScript
  • Using the above to build ToolTips
  • Adding and remove elements from the canvas at runtime
  • Clipping an animated rectangle as discussed above
The xaml for this application is not all that difficult once you really examine it, and it's not all that big either. The real work is done in the javascript as you can see below. I've tried to comment both up to make it clear when reading.

Continued Challenges

The application tightened up during the detailed commenting exercise.I had a bit of 'flicker' on the rollovers before I did that and it all went away, so I'm reasonably pleased with how it works.

The only thing I really wasn't happy with for a long time was the font and skin menu open/close button artwork. I'd been through a couple renditions of each, trying to put text on them and had not been happy with any attempts, so I proceeded in the path of wrapping this all up to upload. Of course, I continued to test and play, and at some point noticed the nice left-right arrow buttons with a very narrow profile in WINGDNG3.

I gave them a try and when I saw how they looked decided they would work just fine. I like the size of the artwork, but it's too small for any realistic text, so using a Glyph is not only a good idea, but falls in the 'eating your own dogfood' category!

Next

I'll follow this article with detailed explanations in smaller examples of some of the interesting Silverlight pieces in this article.

JavaScript for producing this page:

<script>
var mouseDownPosition = 0;
var mouseDownValue = -1;

var nGlyphIndex = 133;
var nRowIndex = 0;
var nNumIndex = 0;
var GlyphToolTip = null;
var FontOpenClosetoolTip = null;
var SkinOpenClosetoolTip = null;
var nRolledImage = 0;
var GlyphMapMainCanvas;
var nFontMenuOnScreen=0;
var nSkinMenuOnScreen=0;
var sSkinSelection="";
var sSkin="Yellow";
var sTooltipColor = "CornSilk";

var ding="wingding.ttf";
var dingShort="wingding";
var control = null;
var ToolTipCanvas = null;

function xaml_Page_Loaded(sender, args) {
control = sender.getHost();
ToolTipCanvas = sender.findName("ToolTipCanvas");

// Save the main canvas for use in createFromXaml code
GlyphMapMainCanvas=sender.findName("GlyphMapMainCanvas");
populateMap();
}

function populateMap() {
// First display the inidces across the top
for (nNumIndex=0; nNumIndex < 20; nNumIndex++)
{
// Define a xaml fragment for the the numbers across the top
// Offset from left margin by 70 and space every 22 pixels
var xamlFragment = '<TextBlock FontSize="14" Canvas.Top="35" Canvas.Left="' + ((nNumIndex)*22 + 70) + '" Foreground="Black" Text="' + nNumIndex + '" />';
var TextBlock = control.content.createFromXaml(xamlFragment);

// Add the xaml fragment as a child of GlyphMapMainCanvas
GlyphMapMainCanvas.children.add(TextBlock);
}

// Going to have 15 rows.. this is empirically decided upon for the 4 ding fonts used
// 15 rows by 20 across is indice 0 to 300
for (nRowIndex=0; nRowIndex < 15; nRowIndex++)
{
// Each time through the loop, first print the row index
// Define a xaml fragment for the row index
// Offset from left margin by 34, begin top margin ot 55 and space every 22 pixels down
// The actual character printed is the RowIndex*20 so it runs 0,20,40...
var xamlFragment = '<TextBlock FontSize="14" Canvas.Top="'+ (nRowIndex*22 + 55) + '" Canvas.Left="34" Foreground="Black" Text="' + nRowIndex*20 + '" />';
var TextBlock = control.content.createFromXaml(xamlFragment);
GlyphMapMainCanvas.children.add(TextBlock);

// Now display 20 Glyphs
// The Glyph Indice is simply 20 times the Row index,
// so the first row is 0-19, second row 20-39, etc.
for (nGlyphIndex=nRowIndex*20; nGlyphIndex<(nRowIndex*20 + 20); nGlyphIndex++)
{
// Define a xaml fragment for the single Glyph
// The xmlns had to be entered to be able to name the object
// The object is named with the Indice of the Glyph
// The Glyphs are displayed at an offset of 70 pixels from the left, and spaced every 22 pixels
var xamlFragment = '<Glyphs xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Name="' + nGlyphIndex + '" Fill="Black" FontUri="' + ding + '" FontRenderingEmSize="16" OriginY="'+ (((nRowIndex)*22) + 70) + '" OriginX="' + ((nGlyphIndex-(nRowIndex*20))*22 + 70) + '" Indices="' + nGlyphIndex + '" MouseEnter="RollIntoGlyph" MouseLeave="RollOutofGlyph"/>';
var GlyphBlock = control.content.createFromXaml(xamlFragment);
GlyphMapMainCanvas.children.add(GlyphBlock);
}
}

// Display the font name on the frame
GlyphMapMainCanvas.findName("lblName").Text=dingShort;
GlyphMapMainCanvas.findName("lblNameBG").Text=dingShort;
}

// When you roll the mouse into a Glyph,
// Setup a tooltip with the Glyph double-size
// and the indice value
function RollIntoGlyph(sender, args) {
// the Glyph name is the indice value
var which=sender.Name.toString();

// nRolledImage retains the indice of the glyph
// we're rolling on to try to avoid flashes
if (nRolledImage != which)
{
// switching to a different glyph
nRolledImage = which;

// just in case the tooltip somehow didn't get removed
if (GlyphToolTip != null)
{
GlyphMapMainCanvas.children.remove(GlyphToolTip);
GlyphToolTip = null;
}
else
{
// Define the xaml fragment for the tool tip.
var xamlFragment = '<Canvas Width="50" Height="50" Background="' + sTooltipColor + '">';
xamlFragment += '<Rectangle Width="50" Height="50" Stroke="Black" />';
xamlFragment += '<Glyphs Fill="Black" FontUri="' + ding + '" FontRenderingEmSize="32" Canvas.Left="4" Canvas.Top="2" Indices="' + which + '" />';
xamlFragment += '<TextBlock FontSize="10" Canvas.Top="35" Canvas.Left="20" Foreground="Black" Text="' + which + '"/>';
xamlFragment += '</Canvas>';

GlyphToolTip = control.content.createFromXaml(xamlFragment);

// Position the tool tip at a relative x/y coordinate value.
GlyphToolTip["Canvas.Left"] = sender["OriginX"] - 50; //.args.getPosition(null).x-50;
GlyphToolTip["Canvas.Top"] = sender["OriginY"] - 60; //args.getPosition(null).y-50;

ToolTipCanvas.children.add(GlyphToolTip);
}
}
}

// When you roll off the Glyph, remove the tooltip
// and reset the Image indicator
function RollOutofGlyph(sender, args) {
if (GlyphToolTip != null)
{
// Remove the tool tip from the Canvas object.
ToolTipCanvas.children.clear();
}
GlyphToolTip = null;
nRolledImage = 0;
}

// Short stub for setting up common open/close
function RollIntoFontOpen(sender, args){
// need to do this here where we have
// a valid sender
sender.findName("FontOpenPath").Fill="Red";
sender.findName("FontOpenPath").Stroke="Red";
RollIntoFontOpenClose("Open", args);
}

// Short stub for setting up common open/close
function RollIntoFontClose(sender, args){
// need to do this here where we have
// a valid sender
sender.findName("FontClosePath").Fill="Red";
sender.findName("FontClosePath").Stroke="Red";
RollIntoFontOpenClose("Close", args);
}

// When you roll into the Font Menu Open button, display a tooltip
function RollIntoFontOpenClose(sOpenClose, args) {
if (FontOpenClosetoolTip == null)
{
var sText;
if (sOpenClose == "Open")
{
sText = "Open Font Menu";
}
else
{
sText = "Close Font Menu";
}

// Define the xaml fragment for the tool tip for the Font menu open
var xamlFragment = '<Canvas Width="120" Height="25" Background="' + sTooltipColor + '">';
xamlFragment += '<Rectangle Width="120" Height="25" Stroke="Black" />';
xamlFragment += '<TextBlock FontSize="10" Canvas.Top="5" Canvas.Left="20" Foreground="Black" Text="' + sText + '"/>';
xamlFragment += '</Canvas>';

FontOpenClosetoolTip = control.content.createFromXaml(xamlFragment);

// Position the tool tip at a relative x/y coordinate value.
if (sOpenClose == "Open")
{
FontOpenClosetoolTip["Canvas.Left"] = args.getPosition(null).x-120;
}
else
{
// The whole reason for the short stub...
// offset the close tool tip to avoid the button
FontOpenClosetoolTip["Canvas.Left"] = args.getPosition(null).x-120;
}
FontOpenClosetoolTip["Canvas.Top"] = args.getPosition(null).y-25;

ToolTipCanvas.children.add(FontOpenClosetoolTip);
}

}

// Close the tooltip when you roll out of the Font Menu Open button
function RollOutofFontOpenClose(sender, args) {
sender.findName("FontOpenPath").Fill="Black";
sender.findName("FontOpenPath").Stroke="Black";
sender.findName("FontClosePath").Fill="Black";
sender.findName("FontClosePath").Stroke="Black";
if (FontOpenClosetoolTip != null)
{
ToolTipCanvas.children.clear();
}
FontOpenClosetoolTip = null;
}


// Short stub for setting up common open/close
function RollIntoSkinOpen(sender, args){
// need to do this here where we have
// a valid sender
sender.findName("SkinOpenPath").Fill="Red";
sender.findName("SkinOpenPath").Stroke="Red";
RollIntoSkinOpenClose(sender, args, "Open");
}

// Short stub for setting up common open/close
function RollIntoSkinClose(sender, args){
// need to do this here where we have
// a valid sender
sender.findName("SkinClosePath").Fill="Red";
sender.findName("SkinClosePath").Stroke="Red";
RollIntoSkinOpenClose(sender, args, "Close");
}

// Slightly more complex than necessary because
// I combined open and close
function RollIntoSkinOpenClose(sender, args, sOpenClose) {
if (SkinOpenClosetoolTip == null) {
var sText;
if (sOpenClose == "Open")
{
sText = "Open Skin Menu";
}
else
{
sText = "Close Skin Menu";
}

// Define the XAML fragment for the tool tip.
var xamlFragment = '<Canvas Width="120" Height="25" Background="' + sTooltipColor + '">';
xamlFragment += '<Rectangle Width="120" Height="25" Stroke="Black" />';
xamlFragment += '<TextBlock FontSize="10" Canvas.Top="5" Canvas.Left="20" Foreground="Black" Text="' + sText + '"/>';
xamlFragment += '</Canvas>';

// Create the XAML fragment for the tool tip.
SkinOpenClosetoolTip = control.content.createFromXaml(xamlFragment);

// Position the tool tip at a relative x/y coordinate value.
if (sOpenClose == "Open")
{
SkinOpenClosetoolTip["Canvas.Left"] = args.getPosition(null).x-120;
}
else
{
// The whole reason for the short stub...
// offset the close tool tip to avoid the button
SkinOpenClosetoolTip["Canvas.Left"] = args.getPosition(null).x-120;
}
SkinOpenClosetoolTip["Canvas.Top"] = args.getPosition(null).y-25;

ToolTipCanvas.children.add(SkinOpenClosetoolTip);
}
}

function RollOutofSkinOpenClose(sender, args) {
sender.findName("SkinOpenPath").Fill="Black";
sender.findName("SkinOpenPath").Stroke="Black";
sender.findName("SkinClosePath").Fill="Black";
sender.findName("SkinClosePath").Stroke="Black";
if (SkinOpenClosetoolTip != null) {
ToolTipCanvas.children.clear();
}
SkinOpenClosetoolTip = null;
}

// Single function for open/close Font menu
function btnFontSlideMenu(sender, args) {
// Turning if off, don't need to turn off
// Individual pieces
sender.findName("FontOpenCanvas").Opacity=0;

if (nFontMenuOnScreen==0)
{
// We're going to mess with the graphics,
// so need to know if we're skinned or not
// Basically, hide the Open button
// Set the Close button to 60%
// Set the Menu to 60%
// and Start the SlideOut animation
if (sSkin== "NoSkin")
{
sender.findName("FontOpenGraphicNoSkin").Opacity=0;
sender.findName("FontCloseGraphic").Opacity=0;
}
else
{
sender.findName("FontCloseGraphic").Opacity=.6;
}
sender.findName("FontMenuCanvas").Opacity=.6;
sender.findName("FontCloseCanvas").Visibility="Visible";
sender.findName("FontSlideOut").Begin();
}
else
{
sender.findName("FontSlideIn").Begin();
}
}

// Once the Menu is out, set the
function FontSlideOut_Completed(sender, args) {
nFontMenuOnScreen=1;
}

function FontSlideIn_Completed(sender, args) {
// Turning it on... still need to deal with
// on/off of individual pieces
sender.findName("FontCloseCanvas").Visibility="Collapsed";
sender.findName("FontOpenCanvas").Opacity=1;

// First do what needs to be done because
// the Menu finished sliding in
sender.findName("FontMenuCanvas").Opacity=0;

if (sSkin=="NoSkin") {
sender.findName("FontOpenGraphicNoSkin").Opacity=1;
sender.findName("FontOpenGraphic").Opacity=0;
}
else
{
sender.findName("FontOpenGraphic").Opacity=1;
}
nFontMenuOnScreen=0;
}

function btnSkinSlideMenu(sender, args) {
// Turning if off, don't need to turn off
// Individual pieces
sender.findName("SkinOpenCanvas").Opacity=0;
if (nSkinMenuOnScreen==0)
{
if (sSkin=="NoSkin") {
sender.findName("SkinCloseGraphicNoSkin").Opacity=.6;
sender.findName("SkinCloseGraphic").Opacity=0;
}
else
{
sender.findName("SkinCloseGraphic").Opacity=.6;
}
sender.findName("SkinMenuCanvas").Opacity=.6;
sender.findName("SkinCloseCanvas").Visibility="Visible";
sender.findName("SkinSlideOut").Begin();
}
else
{
sender.findName("SkinSlideIn").Begin();
}
}

function SkinSlideOut_Completed(sender, args) {
nSkinMenuOnScreen=1;
}

// Skin SlideIn Completed is where the skin
// actually gets changed
function SkinSlideIn_Completed(sender, args) {
// Turning it on... still need to deal with
// on/off of individual pieces
sender.findName("SkinCloseCanvas").Visibility="Collapsed";
sender.findName("SkinOpenCanvas").Opacity=1;

// First do what needs to be done because
// the Menu finished sliding in
sender.findName("SkinMenuCanvas").Opacity=0;
if (sSkin=="NoSkin") {
sender.findName("SkinOpenGraphicNoSkin").Opacity=1;
sender.findName("SkinOpenGraphic").Opacity=0;
}
else
{
sender.findName("SkinOpenGraphic").Opacity=1;
}

// Now do a skin switch
if (sSkinSelection != "") {
switch(sSkinSelection)
{
case "NoSkin":
sender.findName("FontOpenGraphic").Opacity=0;
sender.findName("SkinOpenGraphic").Opacity=0;
sender.findName("FontOpenGraphicNoSkin").Opacity=1;
sender.findName("SkinOpenGraphicNoSkin").Opacity=1;
sender.findName("Frame").Opacity=0;
sender.findName("FrameNoSkin").Opacity=1;
sender.findName("FontMenuRect").Fill="White";
sender.findName("SkinMenuRect").Fill="White";
sTooltipColor = "White";
break;

case "Yellow":
sender.findName("Frame").Source="YellowFrame.png";
sender.findName("FontOpenGraphic").Source="YellowFontButton.png";
sender.findName("FontCloseGraphic").Source="YellowFontButton.png";
sender.findName("SkinOpenGraphic").Source="YellowFontButton.png";
sender.findName("SkinCloseGraphic").Source="YellowFontButton.png";
sender.findName("FontMenuRect").Fill="CornSilk";
sender.findName("SkinMenuRect").Fill="CornSilk";
sender.findName("FontOpenGraphic").Opacity=1;
sender.findName("SkinOpenGraphic").Opacity=1;
sender.findName("FontOpenGraphicNoSkin").Opacity=0;
sender.findName("SkinOpenGraphicNoSkin").Opacity=0;
sender.findName("Frame").Opacity=1;
sender.findName("FrameNoSkin").Opacity=0;
sender.findName("FontCloseGraphicNoSkin").Opacity=0;
sender.findName("SkinCloseGraphicNoSkin").Opacity=0;
sTooltipColor = "CornSilk";
break;

case "Blue":
sender.findName("Frame").Source="BlueFrame.png";
sender.findName("FontOpenGraphic").Source="BlueFontButton.png";
sender.findName("FontCloseGraphic").Source="BlueFontButton.png";
sender.findName("SkinOpenGraphic").Source="BlueFontButton.png";
sender.findName("SkinCloseGraphic").Source="BlueFontButton.png";
sender.findName("FontMenuRect").Fill="LightBlue";
sender.findName("SkinMenuRect").Fill="LightBlue";
sender.findName("FontOpenGraphic").Opacity=1;
sender.findName("SkinOpenGraphic").Opacity=1;
sender.findName("FontOpenGraphicNoSkin").Opacity=0;
sender.findName("SkinOpenGraphicNoSkin").Opacity=0;
sender.findName("Frame").Opacity=1;
sender.findName("FrameNoSkin").Opacity=0;
sender.findName("FontCloseGraphicNoSkin").Opacity=0;
sender.findName("SkinCloseGraphicNoSkin").Opacity=0;
sTooltipColor = "LightBlue";
break;

case "Cutout":
sender.findName("Frame").Source="CutoutFrame.png";
sender.findName("FontOpenGraphic").Source="CutoutFontButton.png";
sender.findName("FontCloseGraphic").Source="CutoutFontButton.png";
sender.findName("SkinOpenGraphic").Source="CutoutFontButton.png";
sender.findName("SkinCloseGraphic").Source="CutoutFontButton.png";
sender.findName("FontMenuRect").Fill="#d1cdff";
sender.findName("SkinMenuRect").Fill="#d1cdff";
sender.findName("FontOpenGraphic").Opacity=1;
sender.findName("SkinOpenGraphic").Opacity=1;
sender.findName("FontOpenGraphicNoSkin").Opacity=0;
sender.findName("SkinOpenGraphicNoSkin").Opacity=0;
sender.findName("Frame").Opacity=1;
sender.findName("FrameNoSkin").Opacity=0;
sender.findName("FontCloseGraphicNoSkin").Opacity=0;
sender.findName("SkinCloseGraphicNoSkin").Opacity=0;
sTooltipColor = "#d1cdff";
break;

}
sSkinSelection="";
}
nSkinMenuOnScreen=0;
}

// The roll over is different from the roll out
// because on the roll out we set the whole canvas
// to 60% regardless of what the individual ones are
// Rolling in, we have to be sure to set the
// menu and close button to 100% just in case
function RollOverFontMenu(sender, args) {
if (nFontMenuOnScreen==1)
{
if (sSkin=="NoSkin")
{
sender.findName("FontCloseGraphicNoSkin").Opacity=1;
}
else
{
sender.findName("FontCloseGraphic").Opacity=1;
}
sender.findName("FontMenuCanvas").Opacity=1;
}
}

function RollOutofFontMenu(sender, args) {
if (nFontMenuOnScreen==1)
{
sender.findName("FontMenuCanvas").Opacity=.6;
}
}

function RollOverSkinMenu(sender, args) {
if (nSkinMenuOnScreen==1)
{
if (sSkin == "NoSkin")
{
sender.findName("SkinCloseGraphicNoSkin").Opacity = 1;
}
else
{
sender.findName("SkinCloseGraphic").Opacity = 1;
}
sender.findName("SkinMenuCanvas").Opacity=1;
}
}

function RollOutofSkinMenu(sender, args) {
if (nSkinMenuOnScreen==1)
{
sender.findName("SkinMenuCanvas").Opacity=.6;
}
}

// When we roll over a menu item, set it to
// red and make sure the canvas is at 100%
// not sure how we could be there without
// making the canvas 100%, but just to be sure
function RollOverFontMenuItem(sender, args) {
sender.Foreground="Red";
RollOverFontMenu(sender, args);
}

// Rolling out, the canvas is already at 100%
function RollOutofFontMenuItem(sender, args) {
sender.Foreground="Black";
}

function RollOverSkinMenuItem(sender, args) {
sender.Foreground="Red";
RollOverSkinMenu(sender, args);
}

function RollOutofSkinMenuItem(sender, args) {
sender.Foreground="Black";
}

// Font Selection
// set the item back to black
// slide the menu back in
// set the font name to the choice
// nuke all children
// and reload the page
function FontMenuSelection(sender, args) {
sender.Foreground="Black";
sender.findName("FontSlideIn").Begin();
ding=sender.Name.toString();
dingShort=sender.Text.toString();
GlyphMapMainCanvas.children.clear();
populateMap();

}

// Skin Selection
// set the SelectionName to setup the the
// Slide In Complete functionality
// set the global skin name
// set the item back to black
// slide the font menu back in if it's out
// slide the skin menu in
function SkinMenuSelection(sender, args) {
sSkinSelection=sender.name.toString();
sSkin=sSkinSelection;

if (nFontMenuOnScreen)
{
sender.findName("FontSlideIn").Begin();
}
sender.Foreground="Black";
sender.findName("SkinSlideIn").Begin();
}


</script>


XAML for producing this page:

<Canvas 
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Loaded="xaml_Page_Loaded" >

<Image x:Name="Frame" Source="YellowFrame.png" Height="425" Width="540" Canvas.Top="0" Canvas.Left="0" />

<Rectangle x:Name="FrameNoSkin" Canvas.Left="25" Canvas.Top="30" Width="485" Height="355" Stroke="Black" Opacity="0" />

<TextBlock x:Name="lblNameBG" FontSize="14" Canvas.Top="6" Canvas.Left="102" Foreground="White" FontWeight="900" Text=""/>
<TextBlock x:Name="lblName" FontSize="14" Canvas.Top="5" Canvas.Left="101" Foreground="Black" FontWeight="900" Text=""/>

<!-- Canvas to wrap the Font Open artwork and Glyph -->
<Canvas x:Name="FontOpenCanvas" Canvas.Top="100" Canvas.Left="520" Width="10" Height="100" >

<!-- Button to open the font menu -->
<Image x:Name="FontOpenGraphic" Canvas.Top="0" Canvas.Left="0" Width="10" Height="100" Source="YellowFontButton.png" Cursor="Hand" MouseLeftButtonDown="btnFontSlideMenu" MouseEnter="RollIntoFontOpen" MouseLeave="RollOutofFontOpenClose"/>

<!-- No Skin Button to open the font menu -->
<Rectangle x:Name="FontOpenGraphicNoSkin" Canvas.Top="0" Canvas.Left="0" Width="10" Height="100" Fill="White" Stroke="Black" Cursor="Hand" MouseLeftButtonDown="btnFontSlideMenu" Opacity="0" MouseEnter="RollIntoFontOpen" MouseLeave="RollOutofFontOpenClose"/>

<Path x:Name="FontOpenPath" Stroke="Black" Fill="Black" Cursor="Hand" MouseLeftButtonDown="btnFontSlideMenu" MouseEnter="RollIntoFontOpen" MouseLeave="RollOutofFontOpenClose"
Data="M 1,50 L4,45 L4,47 L7,47 L7,53 L4,53 L4,55z"/>

</Canvas>

<!-- Canvas to wrap the Skin Open artwork and Glyph -->
<Canvas x:Name="SkinOpenCanvas" Canvas.Top="220" Canvas.Left="520" Width="10" Height="100">

<!-- Button to open the skin menu -->
<Image x:Name="SkinOpenGraphic" Canvas.Top="0" Canvas.Left="0" Width="10" Height="100" Source="YellowFontButton.png" Cursor="Hand" MouseLeftButtonDown="btnSkinSlideMenu" MouseEnter="RollIntoSkinOpen" MouseLeave="RollOutofSkinOpenClose"/>

<!-- No Skin Button to open the skin menu -->
<Rectangle x:Name="SkinOpenGraphicNoSkin" Canvas.Top="0" Canvas.Left="0" Width="10" Height="100" Fill="White" Stroke="Black" Cursor="Hand" MouseLeftButtonDown="btnSkinSlideMenu" Opacity="0" MouseEnter="RollIntoSkinOpen" MouseLeave="RollOutofSkinOpenClose"/>

<Path x:Name="SkinOpenPath" Stroke="Black" Fill="Black" Cursor="Hand" MouseLeftButtonDown="btnSkinSlideMenu" MouseEnter="RollIntoSkinOpen" MouseLeave="RollOutofSkinOpenClose"
Data="M 1,50 L4,45 L4,47 L7,47 L7,53 L4,53 L4,55z"/>

</Canvas>


<Canvas x:Name="GlyphMapMainCanvas" />

<!-- -->
<!-- -->
<!-- Font Menu slide-out -->
<!-- -->
<!-- -->

<!-- Outer canvas to allow clipping of the menu to the actual menu area -->
<Canvas Canvas.Left="430" Canvas.Top="100" Width="120" Height="100">

<Canvas x:Name="FontMenuCanvas" Canvas.Left="90" Canvas.Top="0" Width="100" Height="100" Opacity="0"
MouseEnter="RollOverFontMenu"
MouseLeave="RollOutofFontMenu"
>

<!-- Canvas to wrap the Font Close artwork and Glyph -->
<Canvas x:Name="FontCloseCanvas" Canvas.Top="0" Canvas.Left="0" Width="10" Height="100" Visibility="Collapsed">

<!-- Button to close the font menu -->
<Image x:Name="FontCloseGraphic" Canvas.Top="0" Canvas.Left="0" Width="10" Height="100" Source="YellowFontButton.png" Cursor="Hand" MouseLeftButtonDown="btnFontSlideMenu" Opacity="0" MouseEnter="RollIntoFontClose" MouseLeave="RollOutofFontOpenClose"/>

<!-- No Skin Button to close the font menu -->
<Rectangle x:Name="FontCloseGraphicNoSkin" Canvas.Top="0" Canvas.Left="0" Width="10" Height="100" Fill="White" Stroke="Black" Cursor="Hand" MouseLeftButtonDown="btnFontSlideMenu" Opacity="0" MouseEnter="RollIntoFontClose" MouseLeave="RollOutofFontOpenClose"/>

<Path x:Name="FontClosePath" Stroke="Black" Fill="Black" Cursor="Hand" MouseLeftButtonDown="btnFontSlideMenu" MouseEnter="RollIntoFontClose" MouseLeave="RollOutofFontOpenClose"
Data="M 1,47 L4,47 L4,45 L7,50 L4,55 L4,53 L1,53z"/>

</Canvas>

<Rectangle x:Name="FontMenuRect" Canvas.Top="0" Canvas.Left="10" Width="90" Height="100" Fill="CornSilk" Stroke="Black" />

<TextBlock x:Name="webdings.ttf" Canvas.Top="10" Canvas.Left="20" FontSize="12" Foreground="Black" Text="webdings" Cursor="Hand" MouseEnter="RollOverFontMenuItem" MouseLeave="RollOutofFontMenuItem" MouseLeftButtonDown="FontMenuSelection"/>
<TextBlock x:Name="wingding.ttf" Canvas.Top="30" Canvas.Left="20" FontSize="12" Foreground="Black" Text="wingding" Cursor="Hand" MouseEnter="RollOverFontMenuItem" MouseLeave="RollOutofFontMenuItem" MouseLeftButtonDown="FontMenuSelection"/>
<TextBlock x:Name="WINGDNG2.TTF" Canvas.Top="50" Canvas.Left="20" FontSize="12" Foreground="Black" Text="WINGDNG2" Cursor="Hand" MouseEnter="RollOverFontMenuItem" MouseLeave="RollOutofFontMenuItem" MouseLeftButtonDown="FontMenuSelection"/>
<TextBlock x:Name="WINGDNG3.TTF" Canvas.Top="70" Canvas.Left="20" FontSize="12" Foreground="Black" Text="WINGDNG3" Cursor="Hand" MouseEnter="RollOverFontMenuItem" MouseLeave="RollOutofFontMenuItem" MouseLeftButtonDown="FontMenuSelection"/>

<Canvas>
<Canvas.Resources>
<Storyboard x:Name="FontSlideOut" Completed="FontSlideOut_Completed" >
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="FontMenuCanvas" From="90" To="0" Duration="0:0:1" />
</Storyboard>
</Canvas.Resources>
</Canvas>

<Canvas>
<Canvas.Resources>
<Storyboard x:Name="FontSlideIn" Completed="FontSlideIn_Completed" >
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="FontMenuCanvas" From="0" To="90" Duration="0:0:1" />
</Storyboard>
</Canvas.Resources>
</Canvas>

</Canvas>

<!-- Clip the menu to the 100X100 area -->
<Canvas.Clip>
<RectangleGeometry Rect="0, 0, 100, 100"/>
</Canvas.Clip>

</Canvas>

<!-- -->
<!-- -->
<!-- Skin Menu slide-out -->
<!-- -->
<!-- -->

<!-- Outer canvas to allow clipping of the menu to the actual menu area -->
<Canvas Canvas.Left="430" Canvas.Top="220" Width="120" Height="100">

<Canvas x:Name="SkinMenuCanvas" Canvas.Left="90" Canvas.Top="0" Width="100" Height="100" Opacity="0"
MouseEnter="RollOverSkinMenu"
MouseLeave="RollOutofSkinMenu"
>

<!-- Canvas to wrap the Skin Close artwork and Glyph -->
<Canvas x:Name="SkinCloseCanvas" Canvas.Top="0" Canvas.Left="0" Width="10" Height="100" Visibility="Collapsed">

<!-- Button to close the skin menu -->
<Image x:Name="SkinCloseGraphic" Canvas.Top="0" Canvas.Left="0" Width="10" Height="100" Source="YellowFontButton.png" Cursor="Hand" MouseLeftButtonDown="btnSkinSlideMenu" Opacity="0" MouseEnter="RollIntoSkinClose" MouseLeave="RollOutofSkinOpenClose"/>

<!-- No Skin Button to close the skin menu -->
<Rectangle x:Name="SkinCloseGraphicNoSkin" Canvas.Top="0" Canvas.Left="0" Width="10" Height="100" Fill="White" Stroke="Black" Cursor="Hand" MouseLeftButtonDown="btnSkinSlideMenu" Opacity="0" MouseEnter="RollIntoSkinClose" MouseLeave="RollOutofSkinOpenClose"/>

<Path x:Name="SkinClosePath" Stroke="Black" Fill="Black" Cursor="Hand" MouseLeftButtonDown="btnSkinSlideMenu" MouseEnter="RollIntoSkinClose" MouseLeave="RollOutofSkinOpenClose"
Data="M 1,47 L4,47 L4,45 L7,50 L4,55 L4,53 L1,53z"/>

</Canvas>

<Rectangle x:Name="SkinMenuRect" Canvas.Top="0" Canvas.Left="10" Width="90" Height="100" Fill="CornSilk" Stroke="Black" />

<!-- Skin Menu Items -->
<TextBlock x:Name="NoSkin" Canvas.Top="10" Canvas.Left="20" FontSize="12" Foreground="Black" Text="No Skin" Cursor="Hand" MouseEnter="RollOverSkinMenuItem" MouseLeave="RollOutofSkinMenuItem" MouseLeftButtonDown="SkinMenuSelection"/>
<TextBlock x:Name="Yellow" Canvas.Top="30" Canvas.Left="20" FontSize="12" Foreground="Black" Text="Yellow" Cursor="Hand" MouseEnter="RollOverSkinMenuItem" MouseLeave="RollOutofSkinMenuItem" MouseLeftButtonDown="SkinMenuSelection"/>
<TextBlock x:Name="Blue" Canvas.Top="50" Canvas.Left="20" FontSize="12" Foreground="Black" Text="Blue" Cursor="Hand" MouseEnter="RollOverSkinMenuItem" MouseLeave="RollOutofSkinMenuItem" MouseLeftButtonDown="SkinMenuSelection"/>
<TextBlock x:Name="Cutout" Canvas.Top="70" Canvas.Left="20" FontSize="12" Foreground="Black" Text="Cutout" Cursor="Hand" MouseEnter="RollOverSkinMenuItem" MouseLeave="RollOutofSkinMenuItem" MouseLeftButtonDown="SkinMenuSelection"/>

<Canvas>
<Canvas.Resources>
<Storyboard x:Name="SkinSlideOut" Completed="SkinSlideOut_Completed" >
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="SkinMenuCanvas" From="90" To="0" Duration="0:0:1" />
</Storyboard>
</Canvas.Resources>
</Canvas>

<Canvas>
<Canvas.Resources>
<Storyboard x:Name="SkinSlideIn" Completed="SkinSlideIn_Completed" >
<DoubleAnimation Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="SkinMenuCanvas" From="0" To="90" Duration="0:0:1" />
</Storyboard>
</Canvas.Resources>
</Canvas>

</Canvas>

<!-- Clip the menu to the 100X100 area -->
<Canvas.Clip>
<RectangleGeometry Rect="0, 0, 100, 100"/>
</Canvas.Clip>
</Canvas>

<Canvas x:Name="ToolTipCanvas" />

</Canvas>



Microsoft MVP

Member of WPF and Silverlight Insiders
Silverlight Control
Creative Commons License

Copyright © 2006-2014, WynApse