| More
Java and .NET Software Development Blog | Intertech
 


Leaders let the sun shine on others

While I don’t consider him a leader to emulate, Napoleon Bonaparte nailed it when he said, “A soldier will fight long and hard for a bit of colored ribbon.” That may sound cynical, but it points to a simple fact about human beings: We need to be recognized for our good work. We need sincere praise when it’s deserved. This is a basic truth that effective leaders embrace. When giving praise at Intertech, we try to be immediate and specific. Ideal praise speaks to a specific action and the overall benefit it created for the organization, while the experience is still fresh. We also try to give employees the chance to “relive the experience” by asking them—in person—“how did you do it?” It’s also great to give praise in writing (a memo or handwritten note versus an e-mail) or in public so the recipient can bask in the recognition in front of peers. As an organization grows, everyone needs to be involved with giving appropriate praise. At Intertech, we have created the ACE award to instill this value into our culture.   ACEs are nominated by their peers for living our values of attitude, commitment, and excellence. 

Tom’s Takeaway: Always err on the side of giving too much praise versus not enough. Give specific praise and put it in writing. Build a culture of recognition at your company. Life is short. People cannot hear they’re doing a good job often enough! 


Posted by: Tom Salonek
Posted on: 7/24/2010 at 3:44 PM
Categories: Business
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Expression Blend as a Better Venn Diagramming Tool

OK, this title is not meant to be taken too literally. But the fact remains that the Blend IDE does have some useful ways to perform Venn-like operations on graphically rendered data.

First question: Why would you care?  Well, as it turns out, many custom UserControls begin life as simple vector-graphics which are rendered with the Shape tools (pen, pencil, line, etc) and tweaked using the Properties window.

As you are building such a rendering, the IDE will capture pen and pencil strokes using a <Path> element, and members of System.Windows.Shapes for rectangles, circles and lines.  For example, assume the following collection of objects on a WPF main window:

image

If you were to examine the XAML, you would find something such as the following:

Code Snippet
  1. <Grid x:Name="LayoutRoot">
  2. ????<Rectangle Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="102" Margin="93,104,0,0" Stroke="Black" VerticalAlignment="Top" Width="94"/>
  3. ????<Ellipse Fill="#FFB266C4" HorizontalAlignment="Left" Height="90" Margin="144,56,0,0" Stroke="Black" VerticalAlignment="Top" Width="141"/>
  4. ????<Path Data="M309,146 C312.9974,153.42375 327.83819,145.16181 316,157 308.306,164.694 300.44568,164.73715 288,167 272.18419,169.8756 257.07965,169 241,169 241,192.88331 245.44728,208.70181 221,225 198.3426,240.10493 176.35947,237.47189 149,232 139.06896,230.01379 120.68398,243.94709 142,270 162.84782,295.48066 192.04381,292 231,292 256.11759,292 272,295.23879&#xd;&#xa;272,251 272,232.79339 285.87442,229.22512 312,224 326.54263,221.09147 327.62914,223.96357 338,211 343.95409,203.55739 346.32948,183.5472 341,172 333.43652,155.61245 328.80223,157.37715 313,148" Fill="#FF69C042" Margin="134.133,104,168,124" Stretch="Fill" Stroke="Black"/>
  5. </Grid>

Now, using Blend, you can select multiple shape objects (via  Shift + Click) and then activate the context menu on your selection.  Here you will find a Combine menu tem, which exposes a series of Venn-like operations:

image

For example, if you were to pick the Unite option for each selected shape, your resulting image would appear as so:

image

And the previous three elements have been combined into a single <Path> object:

Code Snippet
  1. <Path Data="M150.77036,85.421501 L148.74716,86.002968 C140.37247,88.254791 131.16498,89.5 121.5,89.5 111.83501,89.5 102.62753,88.254791 94.252838,86.002972 L93.5,85.786606 93.5,138.79258 94.053612,138.78882 C106.61694,138.54271 118.46339,135.52435 130.50014,127.49986 148.8356,115.27625 150.91786,103.32245 150.81303,88.033772 z M225.23241,52.076152 L225.59306,52.121458 C228.37724,52.402526 230.62875,52.309617 229.83183,54.242061 L229.71127,54.485531 229.14326,54.191226 C227.95673,53.57149 226.69139,52.894059 225.33565,52.134794 z M218.50015,48.500001 L222.50015,50.499996 C222.99397,50.793032 223.47688,51.075187 223.94933,51.347355 L225.23241,52.076152 224.2794,51.956441 C222.05745,51.608034 219.74934,50.819918 218.50015,48.500001 z M121.5,0.5 C160.15993,0.5 191.5,20.423328 191.5,45.000001 191.5,54.216252 187.0928,62.778128 179.5451,69.880373 L177.75679,71.479061 179.9185,71.396465 C185.73831,71.12915 191.56923,70.578312 197.50015,69.499966 209.94583,67.237118 217.80615,67.193968 225.50015,59.499981 227.71982,57.280325 229.00154,55.767313 229.59267,54.725051 L229.71127,54.485531 230.30397,54.792619 C240.14322,59.853534 244.35483,61.185095 250.50015,74.499956 255.82964,86.047142 253.45424,106.05729 247.50015,113.49989 237.1293,126.46344 236.04279,123.59135 221.50015,126.49987 195.37457,131.72498 181.50015,135.29324 181.50015,153.49982 181.50015,197.73853 165.61774,194.49976 140.50014,194.49976 101.54394,194.49976 72.347946,197.98041 51.50013,172.49979 44.172749,163.54411 41.536594,156.02055 41.635677,150.08342 L41.670158,149.5 0.5,149.5 0.5,48.499997 51.714752,48.499997 51.704113,48.42385 C51.568863,47.293838 51.5,46.152031 51.5,45.000001 51.5,20.423328 82.840065,0.5 121.5,0.5 z" Fill="#FFF4F4F5" Margin="93,56,277.035,190.84" Stretch="Fill" Stroke="Black"/>

Blend is plump-full of useful shortcuts like these, and I'll post other tidbits over time.

Happy Blending.


Posted by: Andrew Troelsen
Posted on: 7/20/2010 at 9:07 AM
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Social Networking as a Means of Seeking New Job Opportunities

Networking as a means of finding new work opportunities is more important now than ever before. While the economy might be tight, opportunities for networking have never been easier thanks to the web and social networking tools such as LinkedIn, Twitter and even Facebook.

 

These types of sites provide excellent ways of identifying friends and acquaintances who work for (or have some affiliation with) companies in which you are interested. Reach out to those individuals to gain additional insight on the company, business unit, and type of work that might be available.  This critical background information will allow you to customize your resume to highlight past experiences that may be of particular interest to the hiring manager. Furthermore, ask those key individuals to be a reference for you.  An internal reference carries a great deal of weight and may be the crucial differentiator between you and the next applicant.

 

You may find that one of your friends or former colleague is “linked” (has a direct relationship of some sort) with an individual within the company you are applying at, such as the hiring manager.  You could ask this individual to introduce the two of you, providing a chance for you to discuss the position directly with the ultimate decision maker and circumventing human resources where you otherwise may not have made it past a complicated filtering process. 

 

It's important to take time to build your LinkedIn profile, to add to your connections, and to effectively use your connections to help with your job search.  Keep your profile current and complete as this will provide you with the best possible job matches.  The more complete your LinkedIn profile, the better your chances to be found and to be contacted.  Use your LinkedIn profile like a resume and provide prospective employers with detailed information on your skills and experience.  It's also important to help your connections.  “What goes around comes around,” as the proverbial saying goes. 

 

Some employers prefer that candidates have LinkedIn recommendations (LinkedIn will show you the number of recommendations you have below the requirements). Employers also may prefer referrals through the LinkedIn network. Follow the instructions in the job posting to optimize your chances of securing an interview.

Consider joining groups that may post job postings on their sites, and think about starting a discussion letting the group know of your interest in looking for a new opportunity within its area of expertise. I cannot stress enough the importance of joining groups (company groups, alumni groups, college groups and networking groups) and include logos where they exist.  All are good sources for networking, establishing contacts, and finding job listings. 

 

More and more people are sending job postings to their contacts and posting job listings to groups to which they belong.  You may wish to start by joining Intertech Elite Partners. There are filtering mechanisms that allow you to be selective in your search, such as targeting specific locations (i.e., within 10 miles of a given zip code), by job function, by industry, and by posting date, to name a few.

Make your introduction concise.  Given the number of candidate submissions received on a daily basis, you have as little seven seconds to make a positive impression before making it to the recycle bin.  Differentiate yourself by customizing your profile\resume to the position.  Highlight how your experience relates to the requirements of the position by including that information as high level bullets, while providing supporting detail below to be viewed if the reader so chooses.

 

Companies are using LinkedIn's Corporate Solutions and many more hiring managers and recruiters are using LinkedIn to post jobs and source candidates for employment. It's important to make sure you're found by prospective employers and to use LinkedIn to apply for jobs and make connections with people who can help your job search.

 

LinkedIn company profiles are an excellent way to find more information on a company you're interested in pursuing. You'll be able see your connections at the company, new hires, promotions, jobs posted, related companies, and company statistics.

 

JobsInsider is a tool you can download that will describe how you are connected at jobs listed on Monster, CareerBuilder, HotJobs, Craigslist, SimplyHired, Dice, and Vault. You then can see who you are connected with at the company and request an introduction\referral to the hiring manager.

 

Best wishes to you in your search!


Posted by: Intertech
Posted on: 7/15/2010 at 7:48 PM
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Guidance for Early Adopters of Visual Studio 2010

If you are an early adopter of Visual Studio 2010 I want to point you to some very good online resources.  All of these links point to resources created by the ALM RangersI am one of the ALM Rangers which means I volunteer to spend some of my off hours (aka nights and weekends) working with the MS product groups to identify missing features and/or come up with  guidance for common scenarios.   It is really cool to be able to work with the product teams (the folks who write the VS code) to find out exactly how features were meant to be used and maybe even cooler to work with the product teams and other rangers (literally around the world) to figure out how to use Visual Studio to do things it wasn?t designed to do.

The official ranger mission is to provide out of band solutions for missing features or guidance. The content in these links was created with support from Microsoft Product Group, Microsoft Most Valued Professionals (MVPs) and technical specialists from technology communities around the globe, giving you a real-world view from the field, where the technology has been tested and used.

Web and Load Testers:

If you do any sort of web performance testing or load testing with visual studio you need to read this.  This is top notch information.  If I have any questions about  the behavior of the tool or best practices, this is my first and often only stop.

http://vsptqrg.codeplex.com/releases/view/42484

TFS Integration:

Here is some great guidance on making the move to TFS 2010.

We were actually able to use the the TFS Integration tools to get one customer hooked up with the new test tools when TFS 2010 was still in beta and keeping the TFS 2008 production database synched with the new TFS 2010 database until the release bits were available a couple months later.

http://tfsintegration.codeplex.com/releases/view/35476

Here is some more links to to some information and or labs where I at most acted as a reviewer on some small portion of the content.

Visual Studio 2010 Quick Reference Guidance

Visual Studio Team Foundation Server Branching Guide 2010

Visual Studio 2010 Team Foundation Server Requirements Management Guidance

Visual Studio 2010 and Team Foundation Server 2010 VM Factory

Coming Soon:

(looking forward to this as so many of my customers use database projects to manage their database)

Visual Studio 2010 Database Guidance

Visual Studio 2010 Architecture Tooling Guidance


Posted by: Tim Star
Posted on: 7/13/2010 at 7:21 PM
Tags: , , ,
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Leaders Delegate

Some leaders think they need to do it all. This is simply not true. In fact, trying to do everything yourself is counterproductive to being an effective leader. As leaders or managers, we oversee people. Delegation, like Key Result Areas, should be clear. People who are being asked to do something should understand what is being asked of them, and they should have the necessary tools and training to get the job done. One great thing about delegating is that is allows your employees to grow and prove their abilities, particularly if the leader defines the necessary result but not how it gets done. It also frees the leader from daily tasks, which allows him or her to think about the big picture and whether or not progress is being made toward the goals. After delegating, though, a leader must follow-up to ensure that what was delegated is actually being done. Leaders also reward those who deliver, in dollars and through personal recognition.

Tom’s Takeaway: Good leaders delegate, ensuring employees understand what they need to do and have the tools to do it. They also follow up to ensure that delegated tasks get done and find tangible ways to reward those who deliver. 


Posted by: Tom Salonek
Posted on: 7/11/2010 at 5:42 PM
Categories: Business
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Thoughts on "blended classes"

About 1 year ago, Intertech began offering "blended classes".  This training mode allows us to hold a class which is attended by folks in our physical classroom (in lovely Eagan, MN) as well as people from around the US.  Those who are attending remotely typically use a land-line phone system (or in some cases, VOIP) to hear the lecture, and webex to view the slides, demos and quizzes.

At first, I was fearful that this mix of student types could hurt both groups of people. My first fear was that the remote attendees could become isolated. However, since Intertech has training rooms equipped with SmartBoards (you know, the cool white boards you can control with your hand, write on with "virtual pens" and so on), the remote folks have much more engaging eye candy- far more than a bunch of random PowerPoint slides. The use of the SmartBoard seems to keep the remote folks more connected than I realized. The local folks also seem to have a fair share of "oohs" and "aahs".

I was also fearful that local and remote students might have problems hearing each other.  Thankfully, our training rooms are equipped with built in speakers / mics, so again, things are fairly smooth.

To be honest, teaching a blended class feels little different than teaching a class where all students are local to the classroom.  These days, I tend to feel like something is "missing" if we don't have at least 1 remote student.  The mixing of student types tends to make everyone a bit more interactive, which is fantastic.

Not everything is flawless however. We have had a few times where we lost connections to remote students due to oddball issues in the area.  Once, some city workers basically took down our internet connectivity more much of a work day.  Obviously this was no good for anyone.  Beyond such issues, the core infrastructure (webex, mics, land-lines, etc) has been quite solid.

So, as an FYI, if you are interested in a standard Intertech class, but don't live in state, do be aware that any of our classes can be attended remotely. This can be very beneficial to the attendees.  Not only do they reduce some travel and hotel expenses, but they can be "close to home base" to respond to any fires at work which pop up.

And, I suppose you *could* attend class in your PJs if you really want to (don't worry, we don't bother with any web cams).


Posted by: Andrew Troelsen
Posted on: 7/8/2010 at 10:24 AM
Tags:
Categories: Business
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Using Extension Methods to Improve & Simplify Type Conversion in .Net

As the .NET platform has evolved, languages like C# and VB.NET have introduced more and more features to improve the compile-time type safety of the code we write. Features like generics, custom conversion operators, and now co- and contra-variance make it easier than ever before to write code that the compiler can examine for type errors. With each new release, more and more of these features also find their way into the .NET framework classes.

Even in .Net 4, every now and then you run across a situation where type conversion rears it's ugly head and we have to revert to writing code that requires runtime casts. One such situation is when you write code that uses the ADO.NET classes and interfaces - particularly DataTable and IDataReader. Matters can get even worse when we don't know (or don't correctly anticipate) the type of value returned in a given call. If we don't tread carefully here, we can easily encounter runtime errors. Look at the following innocuous bit of code:

var connection = new OracleConnection("Data Source=DEV;");
IDbCommand cmd = new OracleCommand("SELECT 1.85 AS AGE FROM DUAL", connection);
connection.Open();

double age = (double) cmd.ExecuteScalar();

While this looks like it should work, if we actually run it we get:

System.InvalidCastException: Specified cast is not valid.

Clearly, that's not what we want. What's happening here is that Oracle returns a decimal here rather than a double. ExecuteScalar() returns object, and it's illegal to try to unbox a value of one type into a variable of a different type, even if a conversion exists between the two types. While this is a common point of confusion in C#, there is a straight-forward solution - perform the cast in two steps:

double age = (double)(decimal) cmd.ExecuteScalar();

An alternative approach is to use the Convert class:

double age = Convert.ToDouble( cmd.ExecuteScalar() );

Convert will do all of the necessary runtime checks to figure out what type we're dealing with, and will return an appropriately-typed result. Convert.ToDouble() will even work if the value passed in is a string:

IDbCommand cmd = new OracleCommand("SELECT '1.85' AS AGE FROM DUAL", connection);
double age = Convert.ToDouble( cmd.ExecuteScalar() );

Convert is nice, but it does have it's limitations. Among them are:

  1. It can't convert to or from nullable types (int?, double?, DateTime?)
  2. It can't convert DBNull values, which are often encountered in ADO.NET
  3. It doesn't handle conversions to or from enum types
  4. It offers no ability to supply a default value when dealing with nulls
  5. It isn't much less verbose than the straight-forward casting syntax

We can do better than this. In fact, we're going to create a set of extension methods that will make it simpler, easier, and cleaner to perform runtime conversion of types, and we're going to address the limitations above along the way.

Show me the code! All right, down to business. The following listing shows the complete source for our conversion utility class. The class is marked as static to allow it to define a number of extension methods.

public static class ConvertExtesions
{
// converts the source object variable value to the desired result type
public static TR ChangeType<TR>( this object value ) { return (TR)ChangeType(value, typeof(TR)); }

// converts the source value, and if null returns the specified default value public static TR ChangeType<TR>( this object value, TR whenNull ) { return (value == null || value is DBNull)
? whenNull
: (TR)ChangeType(value, typeof(TR)); } public static object ChangeType( this object value, Type convertToType ) { if ( convertToType == null )
{
throw new ArgumentNullException("convertToType");
}

// return null if the value is null or DBNull
if( value == null || value is DBNull )
{
return null;
} // non-nullable types, which are not supported by Convert.ChangeType(), // unwrap the types to determine the underlying time if (convertToType.IsGenericType &&
convertToType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{ convertToType = Nullable.GetUnderlyingType(convertToType);
} // deal with conversion to enum types when input is a string if (convertToType.IsEnum && value is string) { return Enum.Parse(convertToType, value as string); }

// deal with conversion to enum types when input is a integral primitive
if (value != null && convertToType.IsEnum && value.GetType().IsPrimitive && !(value is bool) && !(value is char) && !(value is float) && !(value is double) ) { return Enum.ToObject(convertToType, value); }
// use Convert.ChangeType() to do all other conversions
return Convert.ChangeType(value, convertToType, CultureInfo.InvariantCulture); } }

We're going to break down and examine the code in a moment. But first, let's see how this makes our earlier example better. The same code from before can now be written as:

double age = cmd.ExecuteScalar().ChangeType<double>();

We can even substitute null values with defaults gracefully:

double age = cmd.ExecuteScalar().ChangeType( 0.0d ); // return 0.0 when null

Nullable types are no obstacle either:

double? age = cmd.ExecuteScalar().ChangeType<double?>();

Don't know the type at compile time? Not a problem:

object age = cmd.ExecuteScalar().ChangeType( Type.GetType("System.Double") );

Strings and integral values can now also be converted to enums:

enum CompareResult
{
    LessThan,
    EqualTo,
    GreaterThan,
}

IDbCommand cmd = new OracleCommand("SELECT 'EqualTo' AS CR FROM DUAL", connection);


CompareResult age = cmd.ExecuteScalar().ChangeType<CompareResult>();

So, how did we make this all possible? Let's take a look at the implementation of the CompareExtensions class. The first two overloads of ChangeType<> are:

public static TR ChangeType<TR>( this object value );
public static TR ChangeType<TR>( this object value, TR whenNull );

In all overloads, TR is the type of the result that will be returned. The first overload accepts any object will attempt to convert it to the type TR. Because the type parameter only appears in the output position it cannot be inferred for us - which requires that we supply it:

int valueAsInt = "12345".ChangeType<int>();

The second overload accepts a default value parameter to return if the conversion fails. By making the type of the default value parameter TR we also allow the compiler to infer the type of the return value from the default, which means we can omit the explicit type argument list:

object value = null;
int result = value.ChangeType(-1); // TR is inferred here...

Neither the first or second overloads do much work, the second performs a bit of null checking as a shortcut - but otherwise, both simply delegate to the third overload. That's where all the real work happens.

The third overload of ChangeType() looks like:

public static object ChangeType( this object value, Type convertToType )

You probably notice that this overload is not, in fact, generic. This allows it to be used in situations where the type we are converting to is not known at compile time but only at runtime. The very first step of this method:

if( convertToType == null ) { throw new ArgumentNullException("convertToType"); }

ensures that there's always a Type to which we will try to convert the value to. Next, we check if the value is null or DBNull, in which case the converted value will always be null, so we just return that:

if( value == null || value is DBNull ) { return null; }

Otherwise, we check if the type we're converting to is a nullable type - in which case we really need to perform a conversion to the type that underlies the nullable type. We determine this by using the Nullable.GetUnderlyingType() method, and set that as the type we are actually converting to.

if (convertToType.IsGenericType && 
    convertToType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
    convertToType = Nullable.GetUnderlyingType(convertToType);
}

The next step is to check if we are converting from a string representation to an enum representation, and if so, use Enum.Parse() to convert the value. Otherwise, we also need to check if we are converting from a primitive type to an enum representation:

if (value != null && convertToType.IsEnum && value.GetType().IsPrimitive &&
   !(value is bool) && !(value is char) && !(value is float) && !(value is double) )
{
    return Enum.ToObject(convertToType, value);
}

Here we should note that for such a conversion to be possible, the incoming value must not be null and must be one of the integral types other than char: byte, sbyte, short, ushort, int, uint, long, ulong). If this is the case, we use Enum.ToObject() to convert and return the result.

Finally, if no other case holds, we just delegate to Convert.ChangeType() to do all other conversion processing, and return its result.

And there you have it. Let's wrap things up and look at what we've figured out:

  1. Casting can be a tricky business, particularly when you don't know (or can't always know) the types you are converting from at compile time.
  2. You encounter runtime exceptions if you try to unbox a value from an object to a type other than what is actually boxed, even if a conversion exists.
  3. The Convert class provides a helpful suite of methods for runtime conversion, including the flexible ChangeType() method.
  4. Convert has a number of limitations - it doesn't deal with nullable types, DBNull, or enums, and it doesn't allow us to specify a default value for nulls.
  5. Using extension methods and type inference, we can create our own flexible conversion utility that overcome the limitations of ChangeType, and simplifies the syntax of runtime type conversion.

Posted by: Leo Bushkin
Posted on: 7/6/2010 at 5:33 PM
Tags:
Categories: .NET
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Silverlight 3, the CollectionViewSource and sorting

In my previous post we were introduced to the CollectionViewSource. We have multiple ItemSources binding to the same collection using a CollectionViewSource ? very powerful.  Now what if we want to sort the items displayed in one of our grid cells?  We will focus on row one and column one for the rest of this post.

I?ve added some more default data to the Control?s constructor to make things a little more interesting and also notice I have added a few buttons to the bottom of our control:

image

The Sort by Name button adds a sort description to the CollectionViewSource

theDataContext[0].Column1.View.SortDescriptions.Add(new SortDescription("SomeName",ListSortDirection.Ascending));

This code tells the CollectionViewSource to sort the items in it?s view by the SomeName property in ascending order.  Click the button and we see the following:

image

Click the Clear Sort Descriptions button.

The Sort By Value button tells the CollectionViewSource to sort the items in the view by the SomeValue Property in ascending order.

theDataContext[0].Column1.View.SortDescriptions.Add(new SortDescription("SomeValue", ListSortDirection.Ascending));

Click the button to see the following:

image

This time our list is sorted by the SomeValue property.

Where things get interesting is that you can add multiple sort descriptions.  Try clearing the filter, then adding the name and value filter (in that order) and we see the following:

image

The collection is first sorted by name, then by value.

Clear the filter and click the Value, then name button and we see this:

image

The collection is first sorted by SomeValue, then by SomeName.

The order is important!  If you want to add a sort description to the top of the sort order, simply use the Insert method as follows:

theDataContext[0].Column1.View.SortDescriptions.Insert(0,new SortDescription("SomeValue", ListSortDirection.Ascending));

Here is the modified XAML:



Code Snippet



  1. <UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="SilverlightApplication1.MainPage"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5.    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
  6.     <UserControl.Resources>
  7.         <Style TargetType="Button">
  8.             <Setter Property="FontSize" Value="9"/>
  9.         </Style>
  10.     </UserControl.Resources>
  11.   <Grid x:Name="LayoutRoot">
  12.         <ScrollViewer VerticalScrollBarVisibility="Auto">
  13.             <StackPanel Orientation="Vertical">
  14.             <data:DataGrid x:Name="TheMainDataGrid"  AutoGenerateColumns="False" ItemsSource="{Binding}" HeadersVisibility="Column">
  15.                 <data:DataGrid.Columns>
  16.                     <data:DataGridTemplateColumn Header="Column 1">
  17.                         <data:DataGridTemplateColumn.CellTemplate>
  18.                             <DataTemplate>
  19.                                 <ListBox MinHeight="15" ItemsSource="{Binding Path=Column1.View}" >
  20.                                     <ListBox.ItemTemplate>
  21.                                         <DataTemplate>
  22.                                             <StackPanel Orientation="Horizontal">
  23.                                                 <TextBlock Margin="10" Text="{Binding Path=SomeName}" />
  24.                                                 <TextBlock Margin="10" Text="{Binding SomeValue}" />
  25.                                             </StackPanel>
  26.                                         </DataTemplate>
  27.                                     </ListBox.ItemTemplate>
  28.                                 </ListBox>
  29.                             </DataTemplate>
  30.                         </data:DataGridTemplateColumn.CellTemplate>
  31.                     </data:DataGridTemplateColumn>
  32.                     <data:DataGridTemplateColumn Header="Column 2">
  33.                         <data:DataGridTemplateColumn.CellTemplate>
  34.                             <DataTemplate>
  35.                                 <ListBox MinHeight="15" ItemsSource="{Binding Path=Column2.View}" >
  36.                                     <ListBox.ItemTemplate>
  37.                                         <DataTemplate>
  38.                                             <StackPanel Orientation="Horizontal">
  39.                                                 <TextBlock Margin="10" Text="{Binding SomeName}" />
  40.                                                 <TextBlock Margin="10" Text="{Binding SomeValue}" />
  41.                                             </StackPanel>
  42.                                         </DataTemplate>
  43.                                     </ListBox.ItemTemplate>
  44.                                 </ListBox>
  45.                             </DataTemplate>
  46.                         </data:DataGridTemplateColumn.CellTemplate>
  47.                     </data:DataGridTemplateColumn>
  48.                 </data:DataGrid.Columns>
  49.             </data:DataGrid>
  50.             <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
  51.                 <TextBlock Margin="5" Text="Name:"/>
  52.                 <TextBox x:Name="tbNAme" Margin="5" Width="100"></TextBox>
  53.                 <TextBlock Margin="5" Width="100" Text="Value:"/>
  54.                 <TextBox x:Name="tbValue" Margin="5" Width="100"></TextBox>
  55.                 <Button x:Name="AddButton" Margin="5" Content="Add" Click="AddButton_Click"/>
  56.             </StackPanel>
  57.             <StackPanel HorizontalAlignment="Left">
  58.                 <Button x:Name="ChangeNameButton" Width="150" Content="Change Name" Click="ChangeNameButton_Click"/>
  59.                 <Button x:Name="ChangeValueButton" Width="150" Content="Change Value" Click="ChangeValueButton_Click"/>
  60.                 <Button x:Name="refresh" Width="150" Content="Refresh CVS" Click="refresh_Click" />
  61.                 <Button x:Name="AddNameSort" Width="150" Content="Sort by Name" Click="AddNameSort_Click"/>
  62.                 <Button x:Name="AddValueSort" Width="150" Content="Sort by Value" Click="AddValueSort_Click"/>
  63.                 <Button x:Name="ClearSort" Width="150" Content="Clear Sort Descriptions" Click="ClearSort_Click"/>
  64.             </StackPanel>
  65.         </StackPanel>
  66.             </ScrollViewer>
  67.   </Grid>
  68. </ UserControl>




Here is the modified code behind:



Code Snippet



  1. using System.Collections.ObjectModel;
  2. using System.Windows.Controls;
  3. using System.Windows.Data;
  4. using System.ComponentModel;
  5. namespace SilverlightApplication1
  6. {
  7.     public partial class MainPage : UserControl
  8.     {
  9.         ObservableCollection<TestClass> theMainCollection ;
  10.         ObservableCollection<TheRowClass> theDataContext;
  11.         public MainPage()
  12.         {
  13.             InitializeComponent();
  14.             theMainCollection = new ObservableCollection<TestClass>()
  15.                 { new TestClass() {SomeName="A",SomeValue=11},
  16.                     new TestClass() {SomeName="A",SomeValue=12},
  17.                     new TestClass() {SomeName="A",SomeValue=10},
  18.                     new TestClass() {SomeName="AB",SomeValue=21},
  19.                     new TestClass() {SomeName="A",SomeValue=13},
  20.                     new TestClass() {SomeName="B",SomeValue=13},
  21.                     new TestClass() {SomeName="B",SomeValue=14},
  22.                     new TestClass() {SomeName="AB",SomeValue=11},
  23.                     new TestClass() {SomeName="BC",SomeValue=22},
  24.                     new TestClass() {SomeName="AB",SomeValue=23},
  25.                     new TestClass() {SomeName="BC",SomeValue=24}
  26.                 };
  27.           
  28.             theDataContext = new ObservableCollection<TheRowClass>();
  29.             theDataContext.Add(new TheRowClass(theMainCollection));
  30.             theDataContext.Add(new TheRowClass(theMainCollection));
  31.             TheMainDataGrid.DataContext = theDataContext;
  32.             AddFilter(theDataContext[0].Column1, 1, 1);
  33.             AddFilter(theDataContext[0].Column2, 1, 2);
  34.             AddFilter(theDataContext[1].Column1, 2, 1);
  35.             AddFilter(theDataContext[1].Column2, 2, 2);
  36.            
  37.         }
  38.         private void AddFilter(CollectionViewSource cvs, int row, int column)
  39.         {
  40.             cvs.Filter += delegate(object o, FilterEventArgs eArgs)
  41.             {
  42.                 //Simple filter saying if column = 1 look for an A in the SomeName property
  43.                 //Else look for a B.  If we are in Row 1 look for a 1 in the SomeValue property
  44.                 //Else look for a 2
  45.                 string columnFilter;
  46.                 if (row == 1)
  47.                     columnFilter = "A";
  48.                 else
  49.                     columnFilter = "B";
  50.                 if ((eArgs.Item as TestClass).SomeValue.ToString().Contains(column.ToString()) && (eArgs.Item as TestClass).SomeName.Contains(columnFilter))
  51.                     eArgs.Accepted = true;
  52.                 else
  53.                     eArgs.Accepted = false;
  54.             };
  55.         }
  56.         private void AddButton_Click(object sender, System.Windows.RoutedEventArgs e)
  57.         {
  58.             TestClass tc = new TestClass();
  59.             tc.SomeName = tbNAme.Text;
  60.             tc.SomeValue = int.Parse(tbValue.Text);
  61.             theMainCollection.Add(tc);
  62.         }
  63.         private void ChangeNameButton_Click(object sender, System.Windows.RoutedEventArgs e)
  64.         {
  65.             theMainCollection[theMainCollection.Count-1].SomeName = "B";
  66.         }
  67.         private void ChangeValueButton_Click(object sender, System.Windows.RoutedEventArgs e)
  68.         {
  69.             theMainCollection[theMainCollection.Count - 1].SomeValue = 25;
  70.         }
  71.         private void refresh_Click(object sender, System.Windows.RoutedEventArgs e)
  72.         {
  73.             theDataContext[0].Column1.View.Refresh();
  74.             theDataContext[1].Column1.View.Refresh();
  75.             theDataContext[0].Column2.View.Refresh();
  76.             theDataContext[1].Column2.View.Refresh();
  77.         }
  78.         private void AddNameSort_Click(object sender, System.Windows.RoutedEventArgs e)
  79.         {
  80.             theDataContext[0].Column1.View.SortDescriptions.Add(new SortDescription("SomeName",ListSortDirection.Ascending));
  81.         }
  82.         private void AddValueSort_Click(object sender, System.Windows.RoutedEventArgs e)
  83.         {
  84.             theDataContext[0].Column1.View.SortDescriptions.Add(new SortDescription("SomeValue", ListSortDirection.Ascending));
  85.         }
  86.         private void ClearSort_Click(object sender, System.Windows.RoutedEventArgs e)
  87.         {
  88.             theDataContext[0].Column1.View.SortDescriptions.Clear();
  89.         }
  90.     }
  91. }




See my previous post to get the supporting code.


Posted by: Tim Star
Posted on: 7/5/2010 at 8:56 PM
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Silverlight 3 and the CollectionViewSource

I have been working on a really cool Silverlight application for the last several weeks.  The application is a graphically intensive businesses application with some pretty challenging lists nested in a grid and some very cool drag and drop functionality.  Here is the scenario: 

When the app starts up we make a call to a WCF service and get several hundred to several thousand objects back in an observable Collection.  We slice, dice, and manipulate the ObservableCollection<T> and now the challenge is we have to present several dozen to several hundred different views into this same data.  Throwing several hundred different views of the same data at a user all at once is not particularly useful so the views are stored in cells of a scrollable grid.  As I mentioned, each cell in the grid is a list of items that needs to be generated based on some attributes of the column and row in the grid.  Conceptually I want to break my original collection into a bunch of smaller collections but I didn?t want to physically break the collection  into multiple collections because the drag and drop functionality I spoke of would force me to get into the business of removing an item from one collection and adding it to another.  Additionally once the items are placed where we want them I would have to reassemble the master collection and send it back to the server.  Using a single ObservableCollection<T>) collection is a good fit.  How do we get multiple independent views into this ObservableCollection<T>) ? 

Enter the CollectionViewSource.  The CollectionViewSource allows us to set a source property (in our case the ObservableCollection<T> we got from the WCF call) then set one or many filters and finally we bind each cell to the View property of the CollectionViewSource to allow us to get a list of objects that satisfy our filter criteria.

Let?s whip up a very simple application and demonstrate the behavior. 

Here is the screen:  We have a grid with 2 rows and 2 columns.  Each Cell represents a list of type TestClass.  TestClass has two properties, SomeName ? a string property, and SomeValue an integer property.  Below the grid we have two text boxes and and add button.  This will allow us to demonstrate that we can add items to the observable collection and have the UI automatically update.  Ignore the bottom 3 buttons for the moment.

image

Rather than make a WCF call we are building the ?Main? collection of objects  in the MainPage constructor:



Code Snippet



  1.             theMainCollection = new ObservableCollection<TestClass>()
  2.                 { new TestClass() {SomeName="A",SomeValue=11},
  3.                     new TestClass() {SomeName="A",SomeValue=12},
  4.                     new TestClass() {SomeName="B",SomeValue=13},
  5.                     new TestClass() {SomeName="B",SomeValue=14},
  6.                     new TestClass() {SomeName="AB",SomeValue=21},
  7.                     new TestClass() {SomeName="BC",SomeValue=22},
  8.                     new TestClass() {SomeName="AB",SomeValue=23},
  9.                     new TestClass() {SomeName="BC",SomeValue=24}
  10.                 };



Now we need to create a collection for the grid to bind to.  Remember, what we want is for each grid cell to be a view into our ?main? collection.  In our Example we are using a DataGrid object to display our views and we want the DataGrid to automatically update if we add an object to or remove an object from the main collection .  What we need here is an ObeservableCollection of objects with a CollectionViewSource property for each column.  In our example we have called the object TheRowClass. TheRowClass has two properties of type CollectionViewSource; Column1 and Column2.

Here is the code I use to initialize the ObservableCollection<TheRowClass>:



Code Snippet



  1.             theDataContext = new ObservableCollection<TheRowClass>();
  2.             theDataContext.Add(new TheRowClass(theMainCollection));
  3.             theDataContext.Add(new TheRowClass(theMainCollection));



We have two items in the ObservableCollection<TheRowClass>, each item has two properties of type CollectionViewSource for a total of four CollectionViewSource objects ? one for each cell.  We are passing the main collection into the constructor and using it as the Source property for all of our CollectionViewSources.  All we have left to do is add a filter to each of these collections.  The filter we are adding is very simple:

if TestClass.SomeName contains the letter A and TestClass.SomeValue contains the number 1 display the item in row 1, column 1

if TestClass.SomeName contains the letter A and TestClass.SomeValue contains the number 2 display the item in row 1, column 2

if TestClass.SomeName contains the letter B and TestClass.SomeValue contains the number 1 display the item in row 2, column 1

if TestClass.SomeName contains the letter B and TestClass.SomeValue contains the number 2 display the item in row 2, column 2

Here is the code to pull that off:



Code Snippet



  1.             AddFilter(theDataContext[0].Column1, 1, 1);
  2.             AddFilter(theDataContext[0].Column2, 1, 2);
  3.             AddFilter(theDataContext[1].Column1, 2, 1);
  4.             AddFilter(theDataContext[1].Column2, 2, 2);



And the AddFilter method:



Code Snippet



  1.         private void AddFilter(CollectionViewSource cvs, int row, int column)
  2.         {
  3.             cvs.Filter += delegate(object o, FilterEventArgs eArgs)
  4.             {
  5.                 //Simple filter saying if column = 1 look for an A in the SomeName property
  6.                 //Else look for a B.  If we are in Row 1 look for a 1 in the SomeValue property
  7.                 //Else look for a 2
  8.                 string columnFilter;
  9.                 if (row == 1)
  10.                     columnFilter = "A";
  11.                 else
  12.                     columnFilter = "B";
  13.                 if ((eArgs.Item as TestClass).SomeValue.ToString().Contains(column.ToString()) && (eArgs.Item as TestClass).SomeName.Contains(columnFilter))
  14.                     eArgs.Accepted = true;
  15.                 else
  16.                     eArgs.Accepted = false;
  17.             };
  18.         \



Run the app, notice that the item with SomeName=?AB? and SomeValue=21 is in all of the cells, this is because that item satisfies the filter criteria for each CollectionViewSource in each cell.  Very cool. 

 

Add the letter ?A? to the first textbox and 111 to the second textbox and click the add button.  Notice that an item is added to column 1 and row 1 of the grid without doing anything more than making a call to Collection.Add.

image

Play around with a few more combinations and see how the grid updates.

Now let?s look at the bottom 3 buttons, the first button changes the SomeName value of the last item in the main collection to ?B?.  Here is the event handler:

theMainCollection[theMainCollection.Count-1].SomeName = "B";

Again, add the letter ?A? to the first textbox and 111 to the second textbox and click the add button.  Now click the Change Name button.  Notice that nothing happens.  I assure The item has changed.

Now click the Change Value button.  Notice that the last item in row 1 cell 1 has changed, the value has become 25.

image

We changed the SomeValue property to 25 in the button click event handler.   Here is the event handler:

theMainCollection[theMainCollection.Count - 1].SomeValue = 25;

Why did SomeValue change while SomeName remained the same?  Take a look at the TestClass and you will notice that it implements the INotifyPropertyChanged interface and in the SomeValue setter we call NotifyPropertyChanged which raises an event.  Silverlight has a Pub/Sub eventing system that the UI uses to determine when to update databound objects.  With just a few lines of code we were able to wire TestClass into this eventing system and force the UI to update anytime our SomeValue property changes.

This is all fine and good, call that event for SomeName and we are good to go, well?almops.  We still have a problem with the UI!  The last item in cell 1 column 1 is displaying an incorrect SomeName value, and since we changed SomeValue to 25 it should no longer be visible in this cell.  What is happening is that the Filter on the collection view source is applied when an item is added to or removed from the collection but NOT when an item changes.  To manually reapply the filter call CollectionViewSource.View.Refresh (for each CollectionViewSource) and you will see the grid display correctly.  Click the Refresh CVS button to see this functionality in action:

image 

Below is all of the source code.

Click here to see how to sort the grid cells.

Here is the XAML:



Code Snippet



  1. <UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="SilverlightApplication1.MainPage"
  2.    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5.    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
  6.     <UserControl.Resources>
  7.     </UserControl.Resources>
  8.   <Grid x:Name="LayoutRoot">
  9.         <StackPanel Orientation="Vertical">
  10.             <data:DataGrid x:Name="TheMainDataGrid"  AutoGenerateColumns="False" ItemsSource="{Binding}" HeadersVisibility="Column">
  11.                 <data:DataGrid.Columns>
  12.                     <data:DataGridTemplateColumn Header="Column 1">
  13.                         <data:DataGridTemplateColumn.CellTemplate>
  14.                             <DataTemplate>
  15.                                 <ListBox MinHeight="15" ItemsSource="{Binding Path=Column1.View}" >
  16.                                     <ListBox.ItemTemplate>
  17.                                         <DataTemplate>
  18.                                             <StackPanel Orientation="Horizontal">
  19.                                                 <TextBlock Margin="10" Text="{Binding Path=SomeName}" />
  20.                                                 <TextBlock Margin="10" Text="{Binding SomeValue}" />
  21.                                             </StackPanel>
  22.                                         </DataTemplate>
  23.                                     </ListBox.ItemTemplate>
  24.                                 </ListBox>
  25.                             </DataTemplate>
  26.                         </data:DataGridTemplateColumn.CellTemplate>
  27.                     </data:DataGridTemplateColumn>
  28.                     <data:DataGridTemplateColumn Header="Column 2">
  29.                         <data:DataGridTemplateColumn.CellTemplate>
  30.                             <DataTemplate>
  31.                                 <ListBox MinHeight="15" ItemsSource="{Binding Path=Column2.View}" >
  32.                                     <ListBox.ItemTemplate>
  33.                                         <DataTemplate>
  34.                                             <StackPanel Orientation="Horizontal">
  35.                                                 <TextBlock Margin="10" Text="{Binding SomeName}" />
  36.                                                 <TextBlock Margin="10" Text="{Binding SomeValue}" />
  37.                                             </StackPanel>
  38.                                         </DataTemplate>
  39.                                     </ListBox.ItemTemplate>
  40.                                 </ListBox>
  41.                             </DataTemplate>
  42.                         </data:DataGridTemplateColumn.CellTemplate>
  43.                     </data:DataGridTemplateColumn>
  44.                 </data:DataGrid.Columns>
  45.             </data:DataGrid>
  46.             <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
  47.                 <TextBlock Margin="5" Text="Name:"/>
  48.                 <TextBox x:Name="tbNAme" Margin="5" Width="100"></TextBox>
  49.                 <TextBlock Margin="5" Width="100" Text="Value:"/>
  50.                 <TextBox x:Name="tbValue" Margin="5" Width="100"></TextBox>
  51.                 <Button x:Name="AddButton" Margin="5" Content="Add" Click="AddButton_Click"/>
  52.             </StackPanel>
  53.             <StackPanel HorizontalAlignment="Left">
  54.                 <Button x:Name="ChangeNameButton" Width="100" Content="Change Name" Click="ChangeNameButton_Click"/>
  55.                 <Button x:Name="ChangeValueButton" Width="100" Content="Change Value" Click="ChangeValueButton_Click"/>
  56.                 <Button x:Name="refresh" Width="100" Content="Refresh CVS" Click="refresh_Click" />
  57.             </StackPanel>
  58.         </StackPanel>
  59.   </Grid>
  60. </ UserControl>




Here is the code behind:



Code Snippet



  1. using System.Collections.ObjectModel;
  2. using System.Windows.Controls;
  3. using System.Windows.Data;
  4. namespace SilverlightApplication1
  5. {
  6.     public partial class MainPage : UserControl
  7.     {
  8.         ObservableCollection<TestClass> theMainCollection ;
  9.         ObservableCollection<TheRowClass> theDataContext;
  10.         public MainPage()
  11.         {
  12.             InitializeComponent();
  13.             theMainCollection = new ObservableCollection<TestClass>()
  14.                 { new TestClass() {SomeName="A",SomeValue=11},
  15.                     new TestClass() {SomeName="A",SomeValue=12},
  16.                     new TestClass() {SomeName="B",SomeValue=13},
  17.                     new TestClass() {SomeName="B",SomeValue=14},
  18.                     new TestClass() {SomeName="AB",SomeValue=21},
  19.                     new TestClass() {SomeName="BC",SomeValue=22},
  20.                     new TestClass() {SomeName="AB",SomeValue=23},
  21.                     new TestClass() {SomeName="BC",SomeValue=24}
  22.                 };
  23.           
  24.             theDataContext = new ObservableCollection<TheRowClass>();
  25.             theDataContext.Add(new TheRowClass(theMainCollection));
  26.             theDataContext.Add(new TheRowClass(theMainCollection));
  27.             TheMainDataGrid.DataContext = theDataContext;
  28.             AddFilter(theDataContext[0].Column1, 1, 1);
  29.             AddFilter(theDataContext[0].Column2, 1, 2);
  30.             AddFilter(theDataContext[1].Column1, 2, 1);
  31.             AddFilter(theDataContext[1].Column2, 2, 2);
  32.            
  33.         }
  34.         private void AddFilter(CollectionViewSource cvs, int row, int column)
  35.         {
  36.             cvs.Filter += delegate(object o, FilterEventArgs eArgs)
  37.             {
  38.                 //Simple filter saying if column = 1 look for an A in the SomeName property
  39.                 //Else look for a B.  If we are in Row 1 look for a 1 in the SomeValue property
  40.                 //Else look for a 2
  41.                 string columnFilter;
  42.                 if (column == 1)
  43.                     columnFilter = "A";
  44.                 else
  45.                     columnFilter = "B";
  46.                 if ((eArgs.Item as TestClass).SomeValue.ToString().Contains(column.ToString()) && (eArgs.Item as TestClass).SomeName.Contains(columnFilter))
  47.                     eArgs.Accepted = true;
  48.                 else
  49.                     eArgs.Accepted = false;
  50.             };
  51.         }
  52.         private void AddButton_Click(object sender, System.Windows.RoutedEventArgs e)
  53.         {
  54.             TestClass tc = new TestClass();
  55.             tc.SomeName = tbNAme.Text;
  56.             tc.SomeValue = int.Parse(tbValue.Text);
  57.             theMainCollection.Add(tc);
  58.         }
  59.         private void ChangeNameButton_Click(object sender, System.Windows.RoutedEventArgs e)
  60.         {
  61.             theMainCollection[theMainCollection.Count-1].SomeName = "B";
  62.         }
  63.         private void ChangeValueButton_Click(object sender, System.Windows.RoutedEventArgs e)
  64.         {
  65.             theMainCollection[theMainCollection.Count - 1].SomeValue = 25;
  66.         }
  67.         private void refresh_Click(object sender, System.Windows.RoutedEventArgs e)
  68.         {
  69.             theDataContext[0].Column1.View.Refresh();
  70.             theDataContext[1].Column1.View.Refresh();
  71.             theDataContext[0].Column2.View.Refresh();
  72.             theDataContext[1].Column2.View.Refresh();
  73.         }
  74.     }
  75. }




Here is the TestClass Code:



Code Snippet



  1. using System;
  2. using System.ComponentModel;
  3. namespace SilverlightApplication1
  4. {
  5.     public class TestClass:INotifyPropertyChanged
  6.     {
  7.         public String SomeName { get; set; }
  8.         private int _someValue;
  9.         public int SomeValue
  10.         {
  11.             get
  12.             {
  13.                 return _someValue;
  14.             }
  15.             set
  16.             {
  17.                 if (value != this._someValue)
  18.                 {
  19.                     this._someValue = value;
  20.                     NotifyPropertyChanged("SomeValue");
  21.                 }
  22.             }
  23.         }
  24.         #region INotifyPropertyChanged Members
  25.         public event PropertyChangedEventHandler PropertyChanged;
  26.         #endregion
  27.         private void NotifyPropertyChanged(String info)
  28.         {
  29.             if (PropertyChanged != null)
  30.             {
  31.                 PropertyChanged(this, new PropertyChangedEventArgs(info));
  32.             }
  33.         }
  34.     }
  35. }




Here is TheRowClass:



Code Snippet



  1. using System.Windows.Data;
  2. using System.Collections;
  3. namespace SilverlightApplication1
  4. {
  5.     public class TheRowClass
  6.     {
  7.         public TheRowClass(IEnumerable source)
  8.         {
  9.             Column1 = new CollectionViewSource();
  10.             Column1.Source = source;
  11.             Column2 = new CollectionViewSource();
  12.             Column2.Source = source;
  13.         }
  14.         public CollectionViewSource Column1 { get; set; }
  15.         public CollectionViewSource Column2 { get; set; }
  16.     }
  17. }





Posted by: Tim Star
Posted on: 7/5/2010 at 8:51 PM
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

The Role of Select and Direct Select in Blend

The Blend IDE can be a strange and mysterious place for many developers. As you most likely already know, Blend, in general, is a tool to generate XAML for WPF and Silverlight applications, using a fancy graphical environment.

Similar to Visual Studio, Blend maintains a pallet of controls which can be placed on the visual designer surface, after which point they can be configured using the Properties window.

Naturally, as you are creating a UI, you may need to relocate or reposition items on the designer, and this simple task smacks you right into the odd distinction of a "Selection" and "Direct Select". If you examine the Blend Tools pallet, you will see two selection icons to represent each possibility:

image

Now, assume you have placed a Button control and a rendered geometry (via the Pencil tool) onto the designer.

If you wish to simple select an item for the purpose of resizing, moving or rotation, you will want to use the Select option (press the V key as a keyboard shortcut).  Once you have done so, you will be able to tweak the selected item as you see fit:

image

This being said, what is a "Direct Selection" used for (press the A key as a keyboard shortcut).  Well, the answer is "it depends".  If you perform a direct select on a rendered vector graphic, it will allow you to redefine the path itself using a set of pull points:

image

If you perform a direct select on a ContentControl derived item (such as a Button) it provides a way to drill into the internal content of the control. This can be helpful if you have (for example) a Button which set the Content property to a layout manager (a Canvas perhaps) which contains some number of sub items. In this case, a direct select will allow you to edit the internal aspects of the Button, rather than its general dimensions and placement.

Enjoy!


Posted by: Andrew Troelsen
Posted on: 6/29/2010 at 1:48 PM
Actions: E-mail | Kick it! | DZone it! | del.icio.us
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed