Thursday, July 16, 2009

Autosizing of Grids, Canvasses and StackPanels

Deb: Moving from a relatively static environment, like Photoshop, to a dynamic environment like Silverlight/Blend, requires learning about autosizing and how visuals will behave when the browser is resized. One of the most challenging things for me to learn was the differences in behaviors between grids, canvasses and stackpanels.

I started with grids, the workhorse of the layout panels, and I got comfortable with setting the height and width to 'auto', and the alignment to stretch, and setting the margins to achieve the effect I was looking for. I learned how to set column and row sizes, locking certain ones and leaving others to size dynamically as the space changed.

I moved on to canvasses, learning how to place objects on the panel in a different way, but the autosizing aspect of the layout panel worked the same as the grid. I figured I had this dynamic sizing thing figured out. Then I got cocky and started sticking stackpanels into my grids, and suddenly things weren't working the way I expected. This video explains why:

I am not a developer, but I do have a strong intuitive understanding of hierarchies and trees. I had developed a mental model that said that if I picked 'auto' and 'stretch', my object would size to match the parent it was embedded in. Working with stackpanels forced me to reevaluate my mental model, to acknowledge that sometimes the sizing was driven by the children embedded in the object, and margins - as seen in the video - seem to be able to refer to both (in the example, the top and left margins are relative to the parent canvas but the right and bottom margins are relative to the 'children' buttons).

This is where trial and error has led me. If there is an explanation for these behaviors, a simplifying principle that can be applied in all cases, I would love to learn it -- I really like to understand why things are the way they are. Post a comment and let me know what you think!

Tuesday, July 14, 2009

The Designer's Perspective: Working with Silverlight and Expression Blend

Deb: For too long now, I've been sitting on the sidelines, content to edit Steve's posts. But it's time to start representing the designer's viewpoint when it comes to using Silverlight 3 and Expression Blend 3.

I was first attracted to Silverlight for two reasons: 1) visualization is very important to me and Silverlight lets me design some very compelling visualizations, and 2) Expression Blend promised me significantly more autonomy, and the ability to do whatever I could imagine without relying on a developer to translate my vision for me.

While I haven't become as autonomous as I would like, Blend keeps improving and for the most part, Blend 3 is a joy to work with. I have my moments of frustration, sure, but they are more than offset by the power and independence I've been given. The goal of my posts will be to reduce the frustration factor even further for fellow designers.

I can summarize my attitude toward XAML by saying this: in general, the only legitimate reason for me to go to the XAML is to finetune the sizing of rows and columns. Other than that, I shouldn't need to even look at it. I think this should be the ultimate test of Blend's ability to meet the needs of designers: no XAML coding required.

The point of view I represent is that of someone who has graphic arts training, who is adept at Adobe tools such as Photoshop, who is an extremely heavy user of the Internet, but who knows nothing about C# or .NET. Perhaps it may be difficult for some readers to understand someone with this background, but there are a lot of us out here. There are many things that may be obvious to a developer that can only be painfully learned through trial and error by a designer. Given the relative 'youth' of the Silverlight tools and their accompanying documentation (sparse), only the Internet, powerful search engines and the sharing of the Silverlight community make solving the problems we confront relatively painless. And it always helps to know that the pain of being an early adopter of Silverlight will be compensated for by the advantages that come from being among the first to learn these skills.

Saturday, July 4, 2009

Learning RIA Services: It's Great to be a Silverlight Developer

I have built WCF integrations to SQL Server as part of building Silverlight applications. Having created the basic elements of a CRUD (Create, Read, Update, Delete) using WCF (see Tim Heuer's blog on how to do this ), I had to ask myself if there wasn't a way to achieve the same result that required less code.

During MIX09 I saw Brad Adams' talks on .NET RIA Services (watch Brad Adams talk). On 3 separate occasions I worked through these examples until I began to understand how all the parts fit together. My basic feeling was that although these services are very powerful, they made a lot of assumptions about how I wanted to collect and consume the data. Too much magic going on behind the scenes for my purposes. Using the beta release of RIA Services, according to all the demos I could find on line, there was no way to loop through the returned records without binding it to the control.

The Problem Statement

I wanted to find out what I was doing wrong. Is there a way to just loop over the records returned from RIA services without binding to a control? I asked the question on Stack Overflow and in the Silverlight forums.




The Community to the Rescue

If I knew the answer, I would have asked a better question. Isn't that always the case? The members of the developer community offered answers to my ill-formed question:



No one gave me the exact answer. There was no code provided that I could just cut and paste, but I got great response in hours and their insight led me to the answer I was looking for:



Not long after I starting working on this problem, Silverlight 3 was released. RIA services have changed and I have not had time to investigate, but once I do I will add a new post.

It is Great to be a Silverlight Developer

I have been writing software since I was 18; I would have started sooner if I had access to a computer. Looking back over my 30+ years of programming, I am now having more fun than ever, and I think it is because of the times we live in -- Silverlight, the Internet, and the community of people who use them both. Silverlight has turned web applications into a rich and now, with OOB, sometimes even unconnected experience. Today's development culture is one of generosity and sharing. I am in contact with people around the world, Silverlight gurus and novices alike, and they all love to share. You get the sense that we are all looking to see how much we can accomplish together with Silverlight.

Friday, July 3, 2009

Bing Maps Silverlight Control Part 5: Calculating Distance

My dad was the first person to show me how to measure distances on a map using a pair of dividers and the scale printed on the map key. There is a much better way to do this using the Bing Maps Silverlight control -- it just takes a little programming and finding the right equation.



Here is the Bobcat Video I told you about:



Back to Programming...

Here is how you calculate the distance between points on the earth. You also need to know EarthRadiusInMiles = 3956.0;


/// <summary>
/// Calculate the distance between two geocodes. Defaults to using Miles.
/// </summary>
public static double CalcDistance(double lat1, double lng1, double lat2, double lng2)
{
return CalcDistance(lat1, lng1, lat2, lng2, GeoCodeCalcMeasurement.Miles);
}
/// <summary>
/// Calculate the distance between two geocodes.
/// </summary>
public static double CalcDistance(double lat1, double lng1, double lat2, double lng2, GeoCodeCalcMeasurement m)
{
double radius = GeoCodeCalc.EarthRadiusInMiles;
if (m == GeoCodeCalcMeasurement.Kilometers) { radius = GeoCodeCalc.EarthRadiusInKilometers; }
return radius * 2 * Math.Asin(Math.Min(1, Math.Sqrt((Math.Pow(Math.Sin((DiffRadian(lat1, lat2)) / 2.0), 2.0) + Math.Cos(ToRadian(lat1)) * Math.Cos(ToRadian(lat2)) * Math.Pow(Math.Sin((DiffRadian(lng1, lng2)) / 2.0), 2.0)))));
}

I created a Marker class to mark the end points of the distance calculation. To show the user that they are in distance calculation mode, I have also changed the cursor on the map control. In this case, I alternate between the arrow and the pointing finger.

PointsOfInterest m_oMarkers = new PointsOfInterest();

void cbMeasure_Checked(object sender, RoutedEventArgs e)
{
Map1.Cursor = Cursors.Hand;
m_oMarkers.Clear();
}
void cbMeasure_Unchecked(object sender, RoutedEventArgs e)
{
Map1.Cursor = Cursors.Arrow;
if ( m_oMarkers.Count > 1 )
AddLine(m_oMarkers,.5);
}

The code for collecting the user's selection of points is here:


void Map1_MouseClick(object sender, MapMouseEventArgs e)
{
var oLocation = Map1.ViewportPointToLocation(e.ViewportPoint);

PointOfInterest oPOI = null;
if (cbMeasure.IsChecked == true)
{
oPOI = new Marker() { Loc = oLocation };
m_oMarkers.Add(oPOI);
}
else
{
oPOI = new PointOfInterest() { Loc = oLocation };
m_oList.Add(oPOI);
}

RenderPoint(oPOI);

if (cbDraw.IsChecked == true)
try
{
double dRadius = double.Parse(txtRadius.Text);
AddCircle(oLocation, dRadius, .5);
}
catch( Exception ex)
{
MessageBox.Show(ex.Message);
}
}

Using a polyline and a tool tip is great way to report the distance:


double DistanceInMiles(LocationCollection oLocs)
{
double dDistance = 0.0;
for (int i = 0; i < oLocs.Count() - 1; i++)
dDistance += DistanceInMiles(oLocs[i], oLocs[i + 1]);

return dDistance;
}

double DistanceInMiles(Location Loc1, Location Loc2)
{
double dMiles = GeoCodeCalc.CalcDistance(Loc1.Latitude, Loc1.Longitude, Loc2.Latitude, Loc2.Longitude);
return dMiles;
}

LocationCollection RenderLine(List<PointOfInterest> oList)
{

var oPOIs = (from oPOI in oList
where oPOI is Marker
select oPOI).ToList<PointOfInterest>();

var oLocs = new LocationCollection();
foreach (var oPOI in oPOIs)
oLocs.Add(oPOI.Loc);

return oLocs;
}


void AddLine(List<PointOfInterest> oList, double dOpacity)
{
MapPolyline polyline = new MapPolyline();
polyline.Stroke = new SolidColorBrush(Colors.Red);
polyline.StrokeThickness = 1;
polyline.Opacity = dOpacity;

//this works in miles
LocationCollection oLocs = RenderLine(oList);
polyline.Locations = oLocs;
Map1.AddChild(polyline);

double dDist = DistanceInMiles(oLocs);

ToolTipService.SetToolTip(polyline, string.Format("{0:0.0} mi \n {1:0.00} yd", dDist, dDist * 5280.0 / 3.0));
}