February 2009 - Posts
Once you have an image loaded how do you go back and get the original file name for the source file?
This can be accomplished through the Uri property of the images Source. However, you must first typecast the images Source to be a BitmapImage. The Uri has a property called OriginalString that returns the full relative path to the image file.
Example:
public string GetImageSourceFile(Image img)
{ BitmapImage bi = (BitmapImage) img.Source;
Uri uri = bi.UriSource;
return uri.OriginalString
}
Thank you,
--Mike Snow
Subscribe in a reader
If you ever run into this error when running your application you might be a bit perplexed by its encrypted meaning.
Looking at the exceptions details you will see something like which really doesn’t help you very much:
System.Windows.Markup.XamlParseException occurred
Message="AG_E_PARSER_BAD_PROPERTY_VALUE [Line: 4 Position: 53]"
LineNumber=4
LinePosition=53
StackTrace:
at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
at MapEditor.TerrainTile.InitializeComponent()
at MapEditor.TerrainTile..ctor(Double xLoc, Double yLoc, Double tileRadius)
InnerException:
The Silverlight Team is working to make improvements in error reporting. For now, to better understand what is causing these errors you should take a careful look at your XAML to see if something declared is missing or wrong. For example, say you have an event handler in your XAML that is not anywhere to be found in your code behind. Often I find myself removing an event handler I no longer want in my code behind but forgetting to remove it in my XAML as well.
Now you know (if you didn’t already).
--Mike
When it comes to drawing shapes such as rectangles you are probably fully aware you can draw shapes specifying the color and thickness of the lines. This is done via the properties StrokeThickness and Stroke. For example, take a look at the rectangle drawn here:
This rectangle is drawn with a line color of red set via the Stroke property. It’s thickness is set to “1” using the StrokeThickness property as seen here:
<Rectangle Width="128" Height="128" StrokeThickness="1" Stroke="Red"></Rectangle>
Did you also know you can change the shapes lines to be dotted/dashed while greatly configuring how they are drawn? This is done via the following two properties:
- StrokeDashArray
- StrokeDashCap
Let’s take a brief look at each.
StrokeDashArray
The StrokeDashArray let’s you set the length of each dash and the distance between each dash. You can specify an array of these values where the first value is the length and the second value is the distance. It pair value in the array is applied and then repeated once the array you specified runs out. In this example, we are drawing a dash of length 2 with a distance of 5 between each dash:
StrokeDashArray="1,5"
The next example has two values that repeat non-stop where we have a length of 1, distance of 5 followed by length of 10, distance of 2.
StrokeDashArray="1,5,10,2"
StrokeDashCap
StrokeDashCap affects how or in what shape the dots are drawn and it can be set to one of the following (each example is zoomed in to show detail):
- Round
- Square
Thank you,
--Mike Snow
Subscribe in a reader
In Tip of the Day #93 I showed you how to read XML via XmlReader. However, a better and simpler approach (IMHO) would be to use LINQ.
Let’s start with the same XML file we used in Tip #93.
<?xml version="1.0" encoding="utf-8" ?>
<ImageTree>
<Area name="grass">
<Image name="Normal Grass">grass1.png</Image>
<Image name="Dry Grass">grass2.png</Image>
<Image name="Mixed Grass">grass3.png</Image>
<Image name="Long Grass">grass4.png</Image>
</Area>
<Area name="tile">
<Image name="Brick">brick.png</Image>
<Image name="White Stone">stone.png</Image>
<Image name="Cracked Stone">crackedstone.png</Image>
<Image name="Black Brick">brick2.png</Image>
</Area>
</ImageTree>
To use LINQ you will need to add a reference to System.Xml.Linq. To do this, right click on your References folder in your Silverlight application in the Solution Explorer and choose “Add Reference”. You will find this component on the .NET tab. In your source file add a using statement to reference System.Xml.Linq.
The function below will create a tree view out of the XML file that is shown above. A few important notes on this code:
- The XML file will need to be marked as Build Action=Content so that it gets placed into your XAP file. If you select this file, you can apply this setting in it’s properties in the property grid.
- XDocument.Load() is used to load the XML file from your XAP.
- The XDocument has a enumerable property called Descendants. You can specify what node you want to search for as the parameter of this property and go from there.
private void CreateTree()
{
TreeViewItem areaItem = null;
TreeView tv = new TreeView();
TreeViewItem rootItem = new TreeViewItem();
rootItem.Header = "Images";
tv.Items.Add(rootItem);
XDocument document = XDocument.Load("MapImages.xml");
foreach (XElement element in document.Descendants("Area"))
{
areaItem = new TreeViewItem();
areaItem.Header = element.FirstAttribute.Value;
rootItem.Items.Add(areaItem);
foreach (XElement imageElement in element.Descendants("Image"))
{
TreeViewItem imageItem = new TreeViewItem();
imageItem.Tag = imageElement.Value;
imageItem.Header = imageElement.FirstAttribute.Value;
areaItem.Items.Add(imageItem);
}
}
MainCanvas.Children.Add(tv);
}
As you can see from the code above it’s a lot simpler and straight forward than using XmlReader.
Thank you,
--Mike Snow
Subscribe in a reader
XML (Extensible Markup Language) is a great format for saving structured data in. In this Tip I will be showing you how to read and process XML files from Silverlight using the XmlReader object.
Let’s say, for example, you want to store a tree structure of images grouping by them category. Your XML could look like this:
<?xml version="1.0" encoding="utf-8" ?>
<ImageTree>
<Area name="grass">
<Image name="Normal Grass">grass1.png</Image>
<Image name="Dry Grass">grass2.png</Image>
<Image name="Mixed Grass">grass3.png</Image>
<Image name="Long Grass">grass4.png</Image>
</Area>
<Area name="tile">
<Image name="Brick">brick.png</Image>
<Image name="White Stone">stone.png</Image>
<Image name="Cracked Stone">crackedstone.png</Image>
<Image name="Black Brick">brick2.png</Image>
</Area>
</ImageTree>
Few things to note about the XML above:
- XML can only have one root node which in my case I have called <ImageTree>
- <Area> and <Image> tags are called Elements
- “name” is an attribute and its content is the value for the attribute.
Now, on to the code below:
- To open an XML file I will be using the WebClient object. I have placed a file called MapImages.xml in my ClientBin folder that contains the data I will read. When the file read operation is complete (remember everything is asynchronous) the callback function client_DownloadStringCompleted will be called.
- When reading the data I check to see what the NodeType is. I only care about nodes that are of type Element or Text and I ignore stuff like comments, whitespace, etc.
- Once you read an Element you can get the attribute (such as name=”Brick”) for the Element by calling reader.MoveToFirstAttribute().
- The file name for each image is stored in the Tag property of each tree view item.
public Page()
{
InitializeComponent();
Uri url = new Uri("MapImages.xml", UriKind.Relative);
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
client.DownloadStringAsync(url);
}
void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
TreeViewItem areaItem = null;
TreeView tv = new TreeView();
TreeViewItem rootItem = new TreeViewItem();
rootItem.Header = "Images";
tv.Items.Add(rootItem);
StringReader stream = new StringReader(e.Result);
XmlReader reader = XmlReader.Create(stream);
string imageName = String.Empty;
string areaName = String.Empty;
string fileName = String.Empty;
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == "Area")
{
if (true == reader.MoveToFirstAttribute())
{
areaName = reader.Value;
areaItem = new TreeViewItem();
areaItem.Header = areaName;
rootItem.Items.Add(areaItem);
}
}
else if (reader.Name == "Image")
{
if (true == reader.MoveToFirstAttribute())
{
imageName = reader.Value;
}
}
}
else if (reader.NodeType == XmlNodeType.Text)
{
fileName = reader.Value;
TreeViewItem imageItem = new TreeViewItem();
imageItem.Header = imageName;
imageItem.Tag = fileName;
if (null != areaItem)
areaItem.Items.Add(imageItem);
}
}
MainCanvas.Children.Add(tv); // Add the treeview to our main canvas.
}
}
The result of the code above is a tree view with all the items added as seen in the image below.
On a final note, this code is hard wired to read a specifically formatted and error free file. You will want to add error checking in case the file is invalid in anyway.
Thank you,
--Mike Snow
Subscribe in a reader
In Tip of the Day #86 I showed you how to load external images not included in the project such as images on the web. In this Tip I will be showing you had to load images from the client’s machine through a file stream.
Due to obvious security reasons Silverlight cannot directly load files from a client box. However, in response to an event like a button Silverlight can load files through the OpenFileDialog where the client gets to choose what file to load.
The following sample shows you how to load a PNG file once a use clicks on a button. In the code below, “MyImage” is an Image control that I declared in my XAML. BitmapImage can be found in System.Windows.Media.Imaging.
private void Button_Click_Load_Image(object sender, RoutedEventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "PNG Files (*.png;*.png)|*.png;*.png | All Files (*.*)|*.*";
ofd.FilterIndex = 1;
if (true == ofd.ShowDialog())
{
System.IO.Stream stream = ofd.File.OpenRead();
BitmapImage bi = new BitmapImage();
bi.SetSource(stream);
MyImage.Source = bi;
stream.Close();
}
}
Thank you,
--Mike Snow
Subscribe in a reader
In addition to controls, the Silverlight Toolkit comes with a great set of themes that can easily be applied to your controls. These themes currently include:
- Bureau Black
- Bureau Blue
- Expression Dark
- Expression Light
- Rainier Purple
- Rainier Orange
- Shiny Blue
- Shiny Red
- Whistler Blue
You can see a sample preview of each of these themes by visiting this link.
In this Tip I will be showing you the necessary steps to get a theme applied to your Silverlight controls.
Step 1. Add a Reference to the Theme Components.
- In your Solution explorer right click on “References” and choose “Add Reference”.
- Browse to the location where you installed the Silverlight Toolkit binaries (i.e. c:\SilverlightToolkit\Binaries).
- Add Microsoft.Windows.Controls.Theming.
- In my demo I will be using the Shiny Red theme so also include a reference to Microsoft.Windows.Controls.Theming.ShinyRed.dll which is located in the Binaries\Themes folder.
Step 2. Add Namespace Declaration
- Open Page.xaml and add the following statement to your UserControl to reference the ShinyRed component.
<UserControl x:Class="MapEditor.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
xmlns:shinyRed="clr-namespace:Microsoft.Windows.Controls.Theming;
assembly=Microsoft.Windows.Controls.Theming.ShinyRed"
Width="800" Height="600">
Step 3. Apply the Theme to a Control
Declare the ShinyRed theme around any control you want to apply the them to like this:
<Canvas>
<shinyRed:ShinyRedTheme>
<Button Content="Save Map" Click="Button_Click_Save_Map" Width="80"></Button>
</shinyRed:ShinyRedTheme>
</Canvas>
Result:
As you can see in the image below, the theme is directly applied to the button.
Thank you,
--Mike Snow
Subscribe in a reader
The CSPROJ file, saved in XML format, stores all the references for your project including your compilation options. There is also a SLN file which stores information about projects that make up your solution.
If you are using Visual Studio and have the need to view or edit your CSPROJ file while in Visual Studio you can do so by the following these steps:
- Create a new Silverlight Application.
- In the Solution Explorer, right click your Silverlight Application node and choose “Unload Project”.
- Right click again on the node and choose Edit. This will open up your CSPROJ file for editing.
- After making any changes you want, save and close the file.
- Right click again on the node and choose Reload project when done.
Thank you,
--Mike Snow
Subscribe in a reader