Welcome to MSDN Blogs Sign in | Join | Help

Well, the link isn't QUITE active yet, but if you go to http://expression.microsoft.com/en-us/dd565875.aspx you will see the Overview information for Blend 3, and there's a link to download the Blend 3 RC. Now, at *THIS* particular moment, it doesn't seem like it's up there, but the link on that page should go live very shortly.

I've been with the team for 6 and a half years now, and Blend continues to be something I'm very passionate about. To be completely honest, since I wasn't a big Silverlight fanatic, the jump from Blend v1 to Blend v2 didn't feel like a huge jump. Yes, there were TONS of under-the-cover changes that went into the product, so it could support Silverlight and things like that... but the jump from v2 to v3 is TREMENDOUS. Sketchflow is awesome. Silverlight support is awesome. Behaviors are awesome. Sample Data is awesome. I can go on for days, believe me. (In fact, if you look at all the training classes and things I've done internally, I *HAVE* gone on for days).

Grab it. Check it out. Let me know what you think.

Time to change the world. Again :).

0 Comments
Filed under:

I am very passionate about Blend, there's no question there. But, I'm also a gamer... both board games and computer (PC/Console) games.I was having a conversation with a good friend yesterday, and the more I think about it, the more I just can't accept his argument. Please keep in mind that these are *MY* opinions, and don't reflect the opinions of Microsoft. Everything here is also based on publicly released and announced information, and has absolutely no information that isn't available to everyone else. I don't have some secret inside contact within XBox operations who's feeding me information... it's just the result of, what I thought, was a pretty interesting conversation.

So, the start of the conversation was about the XBox 360. Recently (I believe it was at E3), Microsoft said that they still see another good 5 years in the existing console, so there are no plans for another XBox until the 2014-2015 time frame.

My comment is that this is great. I would be ecstatic to have another five years in the hardware we've already bought. Besides, since we have two entertainment centers, we have TWO Xboxes, and I'm not in a huge hurry to replace them both.

This is where my friend (who I will call X) disagrees. His comment is that Microsoft should be persuing the enthusiast crowd more heavily. The PC Gaming crowd, which is getting more and more alienated, since more people are developing for consoles, are being forced to move to the consoles. Subsequently, they want the BEST graphics, the BEST sound, and everything like that. So, they're not going to want to move to a new console that's based on 3-4 year old technology.

X asserts that most 'serious' PC gamers upgrade their PC hardware on a once-a-year or once-every-two-year schedule. These gamers want a new console, so everything looks as good as it does on their $2000+ PCs, and if we want to entice them, which we certainly should, we need to make sure that our console is up to date with the biggest, strongest hardware to look it's absolute best.

This is where I disagree, for a handful of reasons...

1. The enthusiast crowd is an interesting crowd, but thanks to the advent of the Wii, a new crowd is getting into gaming. This is the crowd that likes casual games that they can pay $10-$15 for, pick up and play, and run with. The XBox 360 and the Live Arcade is VERY enticing to this crowd. We don't need new hardware to play games like Geometry Wars or Uno. The existing hardware is an excellent solution for these folks.

2. Microsoft loses money on every XBox they make, and recovers that money in accessories and gaming licenses. This is simply a fact. And, let's be honest, the 360 wasn't the smoothest launch ever (My wife is on her third XBox now, and I worry that this one won't last too long either). As time goes on, the amount MS loses on each box decreases as the cost of the parts goes down. Why would we reboot that loss amount when the current console still has life in it?

3. We are in an economy with the highest unemployment in a VERY long time. If, as a bread winner in a family, I had $400 to spend on our entertainment, would I rather buy a new console, and possibly not even have enough money for a game for said console, or would I use that $400 to buy about 6 new games? I firmly believe that the percentage of Americans, right now, who could go out to the store and drop $400 without having to think about it for a few minutes, is pretty low.

4. The console market is *NOT* a market that can keep up with computers. Period. Let's say, for the sake of argument, that the XBox 360-II was announced. We'd end up sending out development kits to various studios, who would know the expected specs of the machine. Now, let's release the 360-II to the market 6 months later. How many of those initial games would really be worth playing? They may have started development sooner, but let's face it... the developers don't know the tricks to make the 360-II really sing the way they can with the 360 today. So, those first wave of games are going to be... meh. Now, let's release the 360-II to the market 2 years later instead of 6 months. Developers have time to make some better games, but now the hardware is 2 years old! The people who upgrade their PC on a once-every-two-years model are already talking about a console that's on par with their LAST generation.

5. And... let's take a look at games right now. How many games out there are people buying based on their graphics? Sure, good graphics are a good selling point, but I'm going to go back to an old argument here, that the technology is not what makes better games. It's game DESIGN. The 360 is plenty strong enough for sophisticated and in-depth games. Don't believe me, check out GTA or Resident Evil 5. Gears of War II has awesome graphics and might have pushed the console further then anyone else did, but everyone says that the gameplay and storyline were lackluster (Although, I enjoyed the daylights out of the game). Stop spending time trying to make the graphics look awesome, and make us a game that's fun, immersive and things like that. Case in point, I was reading an article on IGN about RPGs, and they said that the only RPG that's exclusive to the PS3 that's really worth getting excited about is Valkyria Chronicles. That's not a game that pushed their hardware too hard... from a technical standpoint, it didn't really do much that you couldn't have done on a PS2 or original XBox, aside from more Polygons. But, the GAMEPLAY was what drew you in. Which would you rather have... a pretty game or a good one?

So, considering all of this, and thinking about this from a strategic point... let's say you're the Microsoft exec in charge of making the call...

Do you invest in new technologies that extend the longevity of your console...
>> A console that many development houses are getting DAMNED good at coding against
>> A console that is currently dominating the market, without any strong competition (In my less then humble opinion, Sony is not really a competitor, and Nintendo is a different market segment)
>> A console that is having a lower and lower cost every day as the components get cheaper
>> A console with a damned impressive games library

Or, do you spend the time making a new console...
>> A new console that new developers are going to have to learn how to make it sing
>> A new hardware model, especially after you finally got your existing console working reliably (for the most part)
>> A console that will either require backward compatibility (more work), or not (reducing your game library, and piss off your faithful audience)
>> A console that will have technical capabilities that are at least one generation behind current PCs

To be honest, as I type this out, it almost becomes a shame that we have to rev our consoles every couple years. Because, every couple years, we have to start spending our development dollars on learning the new technology, and less dollars on good, sophisticated and immersive game play. I would argue that the BEST games come out a couple years after one console has come out, when developers know the box and focus on the game, but before the next console comes into play, when the developers stop thinking about this console and start looking ahead to the next.

Viva la 360...

1 Comments
Filed under: ,

Ok folks, I've got a few minutes, and I've been seeing some discussion on our internal lists about Drawing Brushes, how to use them, what they're good for, etc... Sounds like a good time for a Blog post.

First off, what is a DrawingBrush? It's one of the three kinds of TileBrushes that WPF recognizes (along with ImageBrush and VisualBrush). Simply put, it allows you to use a Drawing as a brush, to use on any arbitrary shape.

So, what's a Drawing? A Drawing is simply a set of Visual Elements. You can use pretty much any visuals, rectangles, buttons, Images, etc... but once they become a Drawing, they are no longer interactive. In other words, you can use a Button in a Drawing, but it becomes a 'picture' of a Button, not a real button that can take events, rollover effects, etc... A Drawing is also static. You can't really change it after the fact (Ok, you MAY be able to do it withing code, but I've never tried that).

And, well, since Blend is my area of expertise, I'll demonstrate how to do it with Blend.

(Advance warning... as of right now, Silverlight doesn't support Drawing Brushes, so until they do, this post is only for the WPF crowd, but since we keep seeing more and more of WPF's capabilities moved into Silverlight, it wouldn't surprise me to see DrawingBrushes over there eventually. Also, no, I don't know of any specific plans to move DrawingBrush into Silverlight, and it's possible they never will... I can't tell the future, and I'm not actually on the Silverlight product team).

So, go ahead and fire up Blend and create a WPF Project.

Using the Rectangle Tool, create a rectangle that is 200x100 pixels. Remove the Stroke and set the Fill to a 50% Alpha Red. (#7FFF0000).

Copy and Paste that rectangle, and place the new rectangle adjacent to the first, so it looks like you have a square. Set the Fill of the new Rectangle to have a Fill of 50% Alpha White (#7FFFFFFF).

You should have something like what I'm showing here:

Ok, now group those two rectangles into a Canvas. (You can do Grid too, if you prefer. It really doesn't matter).

Copy and Paste the Canvas, then rotate it 90 degrees. You should start to get an idea of the effect we're looking for here. (Anyone want to go out for pizza?)

Ready to convert to DrawingBrush

To create a DrawingBrush, you need a single base element, and Blend will take all the children and move them into the DrawingBrush for you. At the moment, the two Canvas elements are siblings, and their only parent is the LayoutRoot, which isn't going to work. So, group the two Canvas elements into another Canvas. The visual tree should look like this:

 Visual Tree

Now, select that base Canvas (the one I have selected in the image above), and in the Tools Menu, select "Make Brush Resource" and "Make Drawing Brush". You're going to get the "Create Resource" dialog, since TileBrushes are handled, by Blend, as resources. Go ahead and set the name and scope to whatever you want and hit Ok.

If it looks like nothing happened, that's Ok. What it did was copy all of the contents of the selected Rectangle and create a Drawing out of them, and create a resource for you. If you don't believe me, switch over to the Resources pane, and look for the Drawing Brush.

In fact, now, we don't even need the original canvas any more, so go ahead and delete it.

Now draw a nice big rectangle (or you could even use your LayoutRoot, if you wanted to).

Go to the Property Inspector, and set the Fill to the new DrawingBrush. It should paint your rectangle with our red cross hatch. Grab the BrushTransform tool, and your rectangle's corner adorners will show up, just as if you were going to resize it. However, when you grab the bottom right adorner and drag it up, you'll see that you're resizing the BRUSH, and not the whole rectangle. That's exactly what you want. In the Image below, I've given my rectangle a thick, black stroke, so you can see that I've resized the FILL, and not the whole rectangle.

You'll also notice that as soon as you do this, you'll break the connection from the DrawingBrush resource. That's Ok. The Resource is of the two rectangles at a specific size, and in this case, we want to have our brush tile for us.

The final step is to set the TileMode on the Fill to Tile. You can also use the BrushTransform tool to mess with the Brush a bit more... maybe rotate it or give it a skew, and you'll get something like this:

There you have it. You can continue to experiment... try taking the Stroke off the rectangle you just made, and convert *IT* into a Drawing Brush, and reuse it. Maybe try with a FlipXY, and you can get wild pretty quickly. Here's a quick example of something I threw together using two rectangles and an ellipse, and just keep reusing the "Make Drawing Brush" command with the Brush Transform tool.

Now, a feature that some folks probably don't know about, is that you can actually edit a DrawingBrush after the fact. To do so, follow these instructions:

Switch to the Resources pane.

Drag the DrawingBrush that you want to edit to the artboard. You'll get a context menu as normal, but there's an option to "Copy DrawingBrush to Canvas". When you do this, you'll get a Canvas with your DrawingBrush exactly as it was before you made it into a DrawingBrush at the beginning.

Go ahead and make any changes you want. (Maybe if you were following the steps here, you'll want to change the 50% white rectangle to a completely empty rectangle, which will change the visuals slightly.

When you're done, right click the BASE canvas that was created, in the Timeline, and you'll find commands for "Move into Source DrawingBrush" or "Copy into Source DrawingBrush". If you choose Move, the canvas that was created will be destroyed, but the information will be moved into your resource. If you choose Copy, it will update the resource, but not delete your Canvas. This latter option is helpful if you just want tweak your brush, see how it looks, and so on.

Remember, though, that as soon as you used the Brush Transform tool, it broke the resource connection, so if you followed THESE steps, you're not going to see it update immediately in the full tablecloth, but you should be able to take the updated DrawingBrush and go through the last steps pretty easily.

Have fun with it!

0 Comments
Filed under:

This is particularly overdue, but now that I've been featured on the Expression community site, I really need to get back to posting again :).

Last month ago, I got the opportunity to present at the Cre8 summit in Orlando. I gave two presentations, one on Sketchflow in Expression Blend and one on Blend in general. I was also given the chance to give a keynote during lunch on the second day to show off Sketchflow.

I have to say, that this was one of the most nerve wracking things I've done, speaking to a community of Designers who are still only starting to look at Microsoft products seriously for Web Design. Every eye was on me, and I'm quite happy with how Blend behaved for me. I had plenty of folks compliment the speech, and they were shocked when they heard it was my first.

Blend is really starting to take a serious turn toward being a real competitor. I can feel that by the questions I get asked when I'm talking to folks. We're getting less of the "So, what is this all about?" questions, and more people asking about specifics like Silverlight penetration and the relative complexities between making a prototype in Sketchflow versus working in Visio or Powerpoint. It's building a lot more excitement, and that's just awesome to be a part of.

Recently, I also got a chance to go visit ZAAZ here in Seattle. These are folks who do both Silverlight and Flash development, but they're looking at Sketchflow for doing prototypes in either environment. The most priceless moment was when I was showing off Behaviors in Sketchflow, and one of the designers looked up and said "That f***ing rocks!" It's one thing to work six years on a product, and know in your heart how great it is, and how much it's going to change the landscape, but to have someone looking at it for the first time put it into words... it's a real thrill.

We're doing more internal training classes. More folks are getting interested, and the momentum is really building. I can't wait to unleash this one.

And yes, I'm going to get back on the technical tricks very soon. I've been getting some good questions on DrawingBrush, and there's some functionality that isn't as discoverable as I'd like.

1 Comments
Filed under: ,

Well, I know it’s been a while since I’ve posted… although I’m going to be paying a LOT more attention to this moving forward. I’m hoping though that my first post back will be worth it.

We’ve been talking a lot to designers about what they want, and one thing I keep hearing is a desire for Tracking and Kerning. Currently, Silverlight doesn’t really support those, and there really isn’t an easy way to do it.

So, on the bus this morning, I started thinking, and this is what I came up with. As always, feel free to use any code here that you like, but use at your own risk. I’m certainly up for hearing possible improvements on it. Right now, this only does Tracking, but I’m trying to figure out how to do the kerning as well.

So, naturally, I’m doing this in Blend (not a huge surprise), but I want to do this in Silverlight. So, I’m creating a new Silverlight project, and I’m going to be creating a custom control. Start off by drawing a TextBlock, selecting it, and choose “Make User Control”. Blend will ask you for a name, and I chose “KerningTextBlock”.

Here’s my strategy. I’m going to use a StackPanel to take care of lining up all my characters, and create TextBlocks for EACH character. Then, set the padding on those TextBlocks to increase the space uniformly between consecutive characters.

So, get rid of the TextBlock that’s already there, and drop in a StackPanel. Rename that StackPanel since we’re going to be interacting with it in code. I named it “TextDisplay”, since that made sense to me. Also, set the Width and Height to Auto and set the Orientation to Horizontal.

Cool, now I want to create two DependencyProperties on our control, and that means code. So, open up the Solution in VS, and let’s get cracking. I want to implement “ActualText” as a string, so I can set it in one place, and have the StackPanel create it’s TextBlocks, and then a “Tracking” as a double that I can set and see the results.

First, the ActualText:

public static readonly DependencyProperty ActualTextProperty = DependencyProperty.Register("ActualText", typeof(string), typeof(KerningTextBlock), new PropertyMetadata(ActualTextValueChanged));

 

public string ActualText

{

      get

      {

            return (string)GetValue(ActualTextProperty);

      }

 

      set

      {

            SetValue(ActualTextProperty, value);

      }

}

private static void ActualTextValueChanged(DependencyObject caller, DependencyPropertyChangedEventArgs e)

{

}

This is all pretty much “boilerplate” code. I can use “ActualText” to get and set the property, and every time it changes, ActualTextValueChanged will get called. I’ll use that later to update the contents of my StackPanel.

Next, let’s go ahead and get Tracking:

public static readonly DependencyProperty TrackingProperty = DependencyProperty.Register("Tracking", typeof(double), typeof(KerningTextBlock), new PropertyMetadata(TrackingValueChanged));

 

public double Tracking

{

      get

      {

            return (double)GetValue(TrackingProperty);

      }

 

      set

      {

            SetValue(TrackingProperty, value);

      }

}

 

private static void TrackingValueChanged(DependencyObject caller, DependencyPropertyChangedEventArgs e)

{

}

All right, we’ve got our DPs, now let’s hook them up. For starters, let’s do the ActualText. Whenever it gets changed, we want to clear out the children of the TextDisplay StackPanel, and add new TextBlocks for each character. Here’s the code, and I’ll go through it after:

private static void ActualTextValueChanged(DependencyObject caller, DependencyPropertyChangedEventArgs e)

{

      StackPanel textDisplay = ((KerningTextBlock)caller).TextDisplay;

      textDisplay.Children.Clear();

      foreach (char thisChar in e.NewValue.ToString().ToCharArray())

      {

            TextBlock newTextBlock = new TextBlock();

            newTextBlock.Text = thisChar.ToString();

            newTextBlock.Padding = new Thickness(0, 0, ((KerningTextBlock)caller).Tracking, 0);

            textDisplay.Children.Add(newTextBlock);

      }

}

So, what am I doing here? First, I’m grabbing the TextDisplay control. Since the method is static, I can’t simply go to “this.TextDisplay”. But, “caller” is the my KerningTextBlock, and all I need to do is cast it to KerningTextBlock and get my control.

Once I’ve got the TextDisplay, I’m going to empty it, with the Children.Clear command.

Finally, I’m going to iterate through each character in the new value, and create a new TextBlock for it. Those TextBlocks are going to get the character as their text, and set the Padding on it to my Tracking property. If you follow the logic, let’s say ActualText is “Dante” and Tracking is 4.0. We clear out the TextDisplay StackPanel, so it’s empty. We then create a TextBlock with only the letter “D”, and give it a 4 pixel padding (to the right). We then add it to the TextDisplay, and continue on with the next letter. We move on to the “a”, add the TextBlock, and so on.

I realize that this could be optimized to only add additional TextBlocks as needed, and changing the text on the characters instead of destroying and recreating them, but I wanted to start simple. When the handler is called, the “e” argument contains both the OldValue and the NewValue, so writing an optimizer wouldn’t be impossible.

So, if I wanted to, I could just tell the Tracking property to call the same method as ActualText. But, I decided that I wanted to clean it up just a bit.

private static void TrackingValueChanged(DependencyObject caller, DependencyPropertyChangedEventArgs e)

{

      StackPanel textDisplay = ((KerningTextBlock)caller).TextDisplay;

      foreach (UIElement thisCharacter in textDisplay.Children)

      {

            ((TextBlock)thisCharacter).Padding = new Thickness(0, 0, ((KerningTextBlock)caller).Tracking, 0);

      }

}

In this case, we’re just iterating through all of the children and resetting the padding. Much less destructive then going through and destroying all the children and rebuilding them every time.

The cool part about all of this is that you can set properties like the Foreground or FontFamily on the UserControl, and they end up getting inherited. So, once you’ve put all this in, rebuild the project in Blend, and instantiate one of our controls in Blend.

Once you’ve got it in Blend, go down to the Misc category, and you’ll see that both Tracking and ActualText show up as properties. You can set them there and see the effects immediately.  Plus, like I said, go ahead and change the Foreground, Font Family, Size, etc… and they’ll update immediately.

I don’t know how to make Blend think that the control is a Text control, so F2 isn’t going to work like a normal TextBlock, but the concept is there.

Now, the part that I’m trying to figure out, is a good way to do the Kerning. Tracking is easy since it’s a uniform spacing between all the characters, but to be able to adjust the positions of individual pairs of characters would be cool. It would be even cooler to automatically detect it, and since you technically can get the geometry of text, it SHOULD be calculatable, but making that performant may be tricky.

Well, hope ya’ll enjoy J.

Well, I just had it pointed out to me, that my blog is now listed on http://expression.microsoft.com. I'm actually pretty excited to see myself up there. It does, however, make me realize that I should spend a little time sprucing up the look of my blog.

The current theme is something like "Fire", which I chose because of my love for the elements. You can figure it out... a thought of 'hot software' and the like. But, I'm not saying that Blend isn't 'hot' any more... just that I need to figure out a new 'feel' for my blog.

So, if it seems to change a couple times in the near future, it's just me getting a feel for new looks and such. I will continue to post new thoughts and ideas as I go along. Now that the big thing I was working on (which, naturally, I can't say anything about yet), I should have some more time to play and find new things out. I've been experimenting with font embedding and custom characters, and that should be a pretty cool post coming soon!

1 Comments
Filed under: ,

I was playing around with Blend today, and decided that I wanted to write a function to allow my user to drag the shapes around on my artboard. Granted, this could use a bit of polish, since there are some tweaks that would make it a bit better, but the overall sense should work.

For starters, we need to add a couple private variables to our Window... so I added these three lines:

private Point initialDragPoint = new Point(0, 0);

private Point currentDragPoint = new Point(0, 0);

private bool dragging = false;

My naming may not be the best, but what I'm setting up here is to keep track of where I clicked the mouse on my object, and for any mouse event, where I am now. I'm also setting up a boolean flag, just to make the code a bit cleaner.

Now, I've already opened Blend, and created a Rectangle. Also, since I don't really want/need all of the layout power of the Grid, I've changed the layout type of my root element to a Canvas. You COULD do this whole thing with RenderTransforms, but in this case, I think that's just more hassle then it's worth.

We need to hook up three events... MouseDown, MouseMove and MouseUp. We'll start with MouseDown...

private void StartMove(object sender, System.Windows.Input.MouseButtonEventArgs e)

{

      if (!this.dragging)

      {

            this.initialDragPoint = e.GetPosition(this);

            this.dragging = true;

      }

}

Ok, this method won't do a whole heck of a lot, but what it does is remember the point where the mouse came down, then swap the flag to remember that we're now moving. You'll want to hook this event up to the "MouseDown" event on your shape(s) that you want to allow the user to move.

Next, we'll do MouseUp (which is when the user releases the mouse button).

private void EndMove(object sender, System.Windows.Input.MouseEventArgs e)

{

      if (this.dragging)

      {

            this.dragging = false;

      }

}

Again, another simple one... all we do is tell our system that we're done dragging when the user lets go of the mouse button. Finally, the workhorse... MouseMove.

private void Moving(object sender, System.Windows.Input.MouseEventArgs e)

{

      if (this.dragging)

      {

            this.currentDragPoint = e.GetPosition(this);

            Vector dragVector = this.currentDragPoint - this.initialDragPoint;

            FrameworkElement actor = sender as FrameworkElement;

            double curLeft = (double)actor.GetValue(Canvas.LeftProperty);

            double curTop = (double)actor.GetValue(Canvas.TopProperty);

            curLeft += dragVector.X;

            curTop += dragVector.Y;

            actor.SetValue(Canvas.LeftProperty, curLeft);

            actor.SetValue(Canvas.TopProperty, curTop);

            this.initialDragPoint = this.currentDragPoint;

      }

}

Let's look at it piece by piece.

First, obviously, we only want to do something if we're currently dragging. So, we gate the whole method to only act if this.dragging is true.

Next, we figure out where the mouse currently is. e.GetPosition(this) does that for me. For the record, the 'this' as an argument just says that I want the point relative to the window. Since I used (this) in MouseDown as well, both of my points will be in the same scale.

To see how this works, let's say that when we did the MouseDown, we were 100 pixels down and 100 pixels to the right of the top left of our window. Then, when we got the MouseUp, we were 120 pixels down and 120 pixels to the right. So, this.initialDragPoint would be (100,100). The e.GetPosition(this) sets the currentDragPoint to (120,120).

The next step is to calculate our dragging Vector. We want our rectangle to move down and to the right, so the calculation is easy... Now, I could have used a Point just as easily, but I tend to think of this as an actual movement vector, so I used the Vector class instead. dragVector gets set to (120-100, 120-100) or (20,20), which is the movement we want on our rectangle.

Next, I need to get the actor (or the shape that my user is moving), and I need it as a FrameworkElement. By casting it to a Framework element, this same code would work for anything... a Grid with a bunch of elements in it... a single rectangle... pretty much anything.

Then, I just use GetValue to get the current Left and Top (see why I wanted to use a Canvas), adjust them by the dragVector, and then set those values.

Finally, the last thing I need to do, is set THIS point as my new base point, so that the new drag is relative to here. Imagine if I hadn't reset it, and then moved the mouse 20 more pixels to the right. The initial point would be 100,100, but the new dragPoint would be (140,120), so my rectangle, which I already moved 20x20 pixels, would be moved another 40x20 pixels. That's no good. So, updating that base value is important.

Granted, there are still some quirks here... for instance, you could start the drag with the left mouse button, then press the right mouse button while dragging... then releasing EITHER button would drop it. I'm sure there are plenty of refinements... but the point is that this should give everyone a good starting point.

3 Comments
Filed under: ,

Well, it's official, I'm going to be presenting at the MIX convention!

I'm really excited for this, although it's going to be busy for a little while. We need to do dry runs and such. The tentative title is "Unlocking the Creative Genome", but that title may change.

I don't know how many folks who read my blog will be at MIX, but if you are, please stop by and introduce yourself. I'm also going to be in the Expression Blend sandbox, helping folks out with labs and such. I'll send more information as I get it.

If you're not familiar with the MIX, check out http://www.visitmix.com.

CORRECTION: Due to technical issues, my trip to the MIX has been cancelled. My apologies to anyone who was planning on coming to see me there. :(

1 Comments
Filed under:

Here's the problem. Launch Blend, and create a window that has AllowsTransparency set to true (which sets the WindowStyle to None). Looks good, yes? Now, add a button, and in the Click handler, set the WindowState property to Minimized.

Run the application on Vista, and click your minimize button. You'll notice that the window just vanishes! It doesn't get the nice minimize behavior like you'd expect (You know, the animation. Minimize any other app, and you'll see what I mean).

One of my teammates, Brian, was trying to get this to work, and he did some research. Apparently, if you set this.WindowStyle to WindowStyle.SingleBorderWindow, it will minimize correctly. He was pretty excited about this, and brought it to me. I'm not completely sure why it works, because when you restore the window, it's still the "none" WindowStyle, but it does work.

Problem is, if you run the minimize behavior a second time, it fails with an InvalidOperationException, and your app comes crashing down.

We tried resetting the WindowStyle back to None after Minimizing it, but that caused a different InvalidOp.

So, we played with it a bit, and it seems (oddly enough) that if you set the property ONCE after the window has been created, that you get the minimizing behavior. You just can't set it twice.

So, try this method:

private void MinimizeIt(object sender, RoutedEventArgs e)

{

      if (this.WindowStyle != WindowStyle.SingleBorderWindow)

      {

            this.WindowStyle = WindowStyle.SingleBorderWindow;

      }

      this.WindowState = WindowState.Minimized;

}

And from our experimentation, this would consistently cause the window to minimize in the "Vista" way, didn't cause a crash, and oddly enough, didn't change the appearance at all. Oddly enough, for grins, I tried putting that line in the Window1.Loaded event handler (Well, without the If statement), and it gives me the InvalidOperation exception.

A teammate came to talk to me, because he's working with a semi-transparent window, with no Border, and he wants to be able to resize it. Allowing a user to move, minimize, maximize and close it are easy, but how to resize?

Well, this is actually pretty easy. There is a property on the Window named ResizeMode, and if you set it to "CanResizeWithGrip", then a ResizeGrip control is placed on your window automatically, and if you run the app, it will, correctly, resize your window.

But, the default grip is six small squares arranged in a SE-pointing triangle. And, if you look in Blend, that control isn't actually in the XAML, so there's no way to modify it. You can't even make it transparent to put a customized control 'under' it. And it's magical, in that it you can't possibly get another control on top of it, even with Bring To Front... So, how do you customize the look and feel of that control?

You'll find that you can create a new ResizeGrip control, and place it in the bottom right, but you will find that it will be rendered under this mystical ResizeGrip, so no customization there either...

Under the covers, here's the problem. That ResizeGrip is not 'technically' part of the Window. Just as WPF is about to instantiate the window, it sees that the ResizeMode is set to CanResizeWithGrip, and it creates that grip for you in the SE corner. It simply instantiates the control, sets the properties to show up in the bottom left, and that's it. Unfortunately, this means that you can't modify any of the properties on it, since it's actually created when the Window is Instantiated.

I suppose, if you wanted to, you could, in code, track down that control by looking at the children of the Window, although I'm not even sure it *IS* technically even there.

You could also create your own custom control (probably inherited from Thumb), and implement all your own MouseDown/Up/Move code to do your resizing for you. 

Thankfully, here's a better solution. Drop a ResizeGrip somewhere on your document, and edit a copy of the template. But, when the dialog comes up to let you know that you're creating a new Template, click the "Apply To All" radio button, instead of supplying a name. By specifying Apply To All, you're telling WPF that any new controls of this type, unless they specify a new template, should use the template that you're creating at this moment. Go ahead and trick out the ResizeGrip however you want in the Template, then delete the ResizeGrip that you created at the beginning of this process.

Now, you've got a new default ResizeGrip template, so now when WPF instantiates that ResizeGrip, it gets your new default template, and gets the look and feel you want.

The obvious question, now, is that once you've done this, what if you want different ResizeGrips for different areas of your window? I.e. maybe my dialogs that show names have a resize grip with a yellow smiley face, but my dialogs for error messages show a little horned devil.

Two points here... First, when you define your new default template, you can specify the scope. So, if you scope your new template to the Application, then ALL your ResizeGrips will look identical. However, if you scope it to individual windows, those windows can have different templates.

Second, there's nothing stopping you from creating a template whose appearance is a transparent block. Then, you can simply put whatever you want your grip to look like at the bottom right, and the real resize grip will be rendered on top, but since it's transparent, you won't see it... you'll just get the functionality. Of course, if you're going to try this route, make sure your little smiley faces or whatever are laid out in such a way that they'll stay at the bottom right :).

Enjoy!

PS: If folks don't know how to set up moving/close/minimize behavior on transparent windows, give me a shout, and I'll put something here for those scenarios.

1 Comments
Filed under:

The honorable Major Nelson just posted that someone created a Vista Gadget that will display the GamerTag information for your friends on XBox Live. Well, for those who don't know, if I'm not mistaken, Vista Gadgets are generally written in Silverlight (Please correct me if someone knows better).

But, how to get that information? Well, Silverlight doesn't have Databinding yet, but WPF does. If you check out the Major's post, he points you to a Microsoft Evangelist by the name of Adam Kinney who wrote the gadget. Following him, you end up with Duncan Mackenzie who talks about how to connect to his services and get GamerTag info.

Here, to make things easier, open up Blend, and try this:

Create a new project, and then click +XML in the Data Pane. The dialog will come up asking for DataSource info. Paste this into the URL for the XML Data:

http://duncanmackenzie.net/services/GetXboxInfo.aspx?GamerTag=LordSaphon

Now, check out the Data pane. You'll see XboxInfoDS, which you can expand to see a bunch of data fields. If you drag out the "Gamertag (string)" onto the design surface and create a Label, it will proudly display "LordSaphon" (since that was the Gamertag you supplied in the URL, and yes, I am LordSaphon).

You can go ahead and drag the GamerScore, ReputationImageUrl and so on out to the artboard and you'll see those fields displayed as well. Please, don't laugh at my GamerScore... I know it's not huge :).

Remember that any field that is an image Url should probably be bound to an Image control. You can create your own WPF Gamertag application. It also shouldn't be too difficult to modify the Source property of the DataSource, so you can dynamically switch which user you want to display!

Enjoy!

--Dante (LordSaphon on XBox Live)

PS: The service I'm using for getting the XBox Live information is on a webpage not affiliated with Microsoft (as far as I know), so if that information changes, the results you get are beyond my control. This information is provided "as-is", so don't hold me or Microsoft responsible if you get anything unexpected from the site.

References:

Duncan Mackenzie: http://duncanmackenzie.net/blog/put-up-a-rest-api-for-xbox-gamertag-data/default.aspx

Adam Kinney: http://adamkinney.com/blog/272/default.aspx

Major Nelson: http://majornelson.com/archive/2007/10/25/xbox-friends-watch-a-sidebar-gadget-using-silverlight.aspx

I just worked through figuring this out today, and since it was sufficiently irritating, I thought I'd post it here. Plus, I don't want all my 'loyal readers' to think I've disappeared :).

Anyway, in the application I'm working on, I have a ListBox with specific elements in it. If it were databound, I know how to change the selection color, but in this case, I couldn't seem to figure out how to get anything except the System Colors.

If you want to see what I mean in Blend, drop a ListBox on the artboard and using the Right Click menu, add a couple ListBoxItem to it. Now, run the app. When you select any of the items, it is highlighted in whatever color your computer has as the SelectedItem color (On my machine, it's Silver, because I'm using XP with a Silver color scheme).

However, digging through the properties of the ListBox, I couldn't find which property was bound to that System Resource. So, how could I override it and change it.

Here are the steps I eventually had to do: (These are the steps from the start)

  1. Create a ListBox
  2. Add a ListBoxItem to the ListBox
  3. Select the ListBoxItem
  4. Edit the Template of the ListBoxItem (Remember the name, you'll need it later)
  5. Now, in the Triggers pane, choose the property trigger for "IsSelected = True"
  6. Select the Bd element
  7. In the Property pane, you will see that next to the Background brush, the property marker (the little square) is green. This is indicating that it's using a resource. Just change the brush type to something else, and the marker will switch back to white. You can now edit the background to whatever you want.

Now, with the IsSelected Property Trigger selected, any changes you make will be put into the Template, and you're good to go. You will probably want to go back to the root (using the scope up button), and assign that Style to all the ListBoxItem, so select them all in the Object Tree, and set the Style property to the local resource that was created in step 4 above.

1 Comments
Filed under:

It may not be a huge revelation for other folks, but with some of the testing I've been doing, I really started getting frustrated with some stuff.

Specifically, I've got a fairly complex animation, and I want it to repeat forever. However, setting the repeat count on each and every property that I'm animating is rather tedious. "Set the repeat count on the X Rotation... set the repeat count on the Y Rotation... set the repeat count on the X Skew... *sigh*... Set the repeat count on the Y Skew..." etc...

Now, you can't set the repeat count on a composite keyframe in the hopes that the RepeatCount will be propogated downward inside Blend (Yes, I've asked my PMs for that feature, and MAYBE it will make it into the product some day, but who knows...).

So, how do you handle this? Now, I'm sure the XAML experts already knew this, but if you go into the XAML, you can just add RepeatBehavior="Forever" to the Storyboard. The XAML now looks like:

<Window.Resources>
  <Storyboard x:Key="Timeline1" RepeatBehavior="Forever">

And that's it. Now, the animation will start at the beginning, run until the time when all the existing animations have ended, then start over again.

Again, most of the XAML folks probably already knew this, but I thought it was pretty cool.

1 Comments
Filed under:
I've been asked how to create a Value Converter that you can configure the behavior of. In my Total Training series I showed you how to create a Value Converter that would return a color, Red if the value was below a hard coded value, Yellow if it's between the Red threshold and another value, and Green otherwise.

But, in that case, the Red, Yellow and Green thresholds were hard coded into the VC. What if I want a more generic Value Converter that I can specify the thresholds, and even further, what if I want to specify all the colors myself. (I.e. if the value is 100, turn Blue or something).

This can be done in WPF by creating a parameterized Value Converter, and Blend does support working with those kinds of VCs, so let's look at how to do it.

First, create the simple "Rectangle, TextBlock, Slider" combination. We're going to bring the Slider to the text of the TextBlock, and change the Fill of the Rectangle based on the value.

Here's some XAML if you want to just use what I've got:

<Grid Margin="77,60,255,0" VerticalAlignment="Top" Height="34">

     <Rectangle HorizontalAlignment="Left" Margin="0,0,0,6" Width="49" Fill="#FFFFFFFF" Stroke="#FF000000" RadiusX="7.5" RadiusY="7.5"/>

     <TextBlock HorizontalAlignment="Left" Margin="53,6,0,0" Width="19" Text="{Binding Path=Value, ElementName=Slider, Mode=OneWay}" TextWrapping="Wrap"/>

     <Slider Margin="76,0,0,6" x:Name="Slider" LargeChange="10" Maximum="100" SmallChange="1" IsSnapToTickEnabled="True"/>

</Grid>

 

Ok, so now let's create the basic three-tier Value Converter. For now, we're going to say a value of 80 or higher is Green, 50 or higher is Yellow and anything else is Red. Here's the code:

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Data;

using System.Windows.Media;

 

namespace ValueConverters

{

    class ThreeTierHardCodedVC: IValueConverter

    {

        #region IValueConverter Members

 

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            try

            {

                double inputValue = System.Convert.ToDouble(value);

                if (inputValue >= 80)

                {

                    return new SolidColorBrush(Colors.Green);

                }

                else if (inputValue >= 50)

                {

                    return new SolidColorBrush(Colors.Yellow);

                }

                else

                {

                    return new SolidColorBrush(Colors.Red);

                }

            }

            catch (Exception e)

            {

                return new SolidColorBrush(Colors.White);

            }

        }

 

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

        {

            throw new Exception("The method or operation is not implemented.");

        }

 

        #endregion

    }

}

 

Ok, so far so good. If you databind the fill of the Rectangle to the Text of the TextBlock, and pass it through this VC, you'll see that the Rectangle changes color as you move the slider. So, the next step is how we're going to parameterize the VC. For now, I'm just going to accept a two element string, seperated by semicolons. I use semicolons instead of commas just so I don't need to worry about internationalization right now.

So, in effect, if the user passes 80;50, I'm stating that when the value is greater then 80, turn green, between 80 and 50, turn yellow, and below 50, turn red. The parameter will be passed to us (unsurprisingly) as the parameter named "parameter". We'll take it, split it into an array, and just parse those values out. To see how this works, try hooking up one slider like this using 80;50 and another at 95;5.

This is better, except that you're forced into the red/yellow/green colors. What if you want to expand on that? What if you want to be able to configure those colors yourself. To do this, I wrote a quick function that takes a string and converts it to a color. It's not too difficult to write, but if you want to use it, here it is:

private Color GetColorFromString(string input)

{

      Type colorClass = typeof(Colors);

      List<MethodInfo> colorMethods = new List<MethodInfo>(colorClass.GetMethods());

      foreach (MethodInfo info in colorMethods)

      {

            string methodName = info.Name;

            if (info.Name.StartsWith("get_"))

            {

                  string actualColor = info.Name.Split('_')[1];

                  if (String.Compare(input, actualColor, true) == 0)

                  {

                        // We have a match!

                        return (Color)info.Invoke(null, null);

                  }

            }

      }

      return Colors.Black;

}

This method will take any color that is in the Colors object, and return the appropriate color. (If someone knows an easier way to do this, I'd love to hear it). You will need to add System.Windows.Media, System.Windows.Data and System.Reflection to use my method. If you want more info on the Colors class, take a look at: http://msdn2.microsoft.com/en-us/library/system.windows.media.colors.aspx

Ok, so now with this, we're going to modify our VC one more time, to allow it to take three arguments. My plan is to go through the arguments one at a time, check to see if our input is greater then the first, if so, return the color, if not, go on to the second. I'm actually going to ignore the first piece of the final argument, since it will simply end up as the default. I'm going to seperate the arguments with ';', and individual pieces with a ':'. So, here's the new code:

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

{

      try

      {

            string paramAsString = parameter.ToString();

            string[] splitParam = paramAsString.Split(';');

            string[] firstArgument = splitParam[0].Split(':');

            string[] secondArgument = splitParam[1].Split(':');

            string[] thirdArgument = splitParam[2].Split(':');

            double firstInterval = System.Convert.ToDouble(firstArgument[0]);

            Color firstColor = this.GetColorFromString(firstArgument[1]);

            double secondInterval = System.Convert.ToDouble(secondArgument[0]);

            Color secondColor = this.GetColorFromString(secondArgument[1]);

            Color thirdColor = this.GetColorFromString(thirdArgument[1]);

            double inputValue = System.Convert.ToDouble(value);

            if (inputValue >= firstInterval)

            {

                  return new SolidColorBrush(firstColor);

            }

            else if (inputValue >= secondInterval)

            {

                  return new SolidColorBrush(secondColor);

            }

            else

            {

                  return new SolidColorBrush(thirdColor);

            }

      }

      catch (Exception e)

      {

            return new SolidColorBrush(Colors.White);

      }

}

If you give THIS one a try, set the parameter to something like: 95:Azure;10:Coral;0:SeaGreen. Now we've got a VC that you can configure all three brackets, as well as the colors that go with them.

Of course, this is hideously inelegant, so I want to make one last change. Instead of hard coding and breaking the parameters apart this way, I'm going to just create a loop. This will tighten up the code, and actually allow us to go an arbitrary number of brackets. So, if you need to have 15 different brackets of different colors from Indigo to MintCream, you can do it. So, we'll pull those ugly "secondParameter" lines out, and replace it with a foreach. (I've included the entire class here, in case you want to cut and paste)

using System;

using System.Collections.Generic;

using System.Text;

using System.Windows.Data;

using System.Windows.Media;

using System.Reflection;

 

namespace ValueConverters

{

      class FullyVariableVC : IValueConverter

      {

            private Color GetColorFromString(string input)

            {

                  Type colorClass = typeof(Colors);

                  List<MethodInfo> colorMethods = new List<MethodInfo>(colorClass.GetMethods());

                  foreach (MethodInfo info in colorMethods)

                  {

                        string methodName = info.Name;

                        if (info.Name.StartsWith("get_"))

                        {

                              string actualColor = info.Name.Split('_')[1];

                              if (String.Compare(input, actualColor, true) == 0)

                              {

                                    // We have a match!

                                    return (Color)info.Invoke(null, null);

                              }

                        }

                  }

                  return Colors.Black;

            }

 

            #region IValueConverter Members

            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

            {

                  try

                  {

                        string paramAsString = parameter.ToString();

                        string[] splitParam = paramAsString.Split(';');

                        double inputValue = System.Convert.ToDouble(value);

                        foreach (string bracket in splitParam)

                        {

                              string[] bracketParameters = bracket.Split(':');

                              Double bracketLimit;

                              try

                              {

                                    bracketLimit = System.Convert.ToDouble(bracketParameters[0]);

                              }

                              catch (InvalidCastException)

                              {

                                    return this.GetColorFromString(bracketParameters[1]);

                              }

                              if (inputValue >= bracketLimit)

                              {

                                    return new SolidColorBrush(this.GetColorFromString(bracketParameters[1]));

                              }

                        }

                  }

                  catch (Exception e)

                  {

                        return new SolidColorBrush(Colors.White);

                  }

                  return new SolidColorBrush(Colors.White);

            }

 

            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

            {

                  throw new Exception("The method or operation is not implemented.");

            }

            #endregion

      }

}

 

Give this one a fly, and set the parameter to: 95:Azure;75:HotPink;40:Blue;10:Coral;-9999:Orange.

Enjoy :).

(As always, code here is provided "As-is" with no warranty, implied or otherwise. Use at your own risk)

Is when I can't talk about something!

By now, most folks should be downloading the August preview of Expression Blend 2, which I'm super excited to see finally getting out. (If you didn't already know... Get it HERE)

But, one of the features that I've been testing personally is one I'm really excited about, but got cut from the August preview for a variety of reasons. For obvious reasons, I can't say what the feature was and why it was cut. And that drives me CRAZY sometimes!

I can't wait to start talking about the new feature, what you can do with it, how much fun I've had playing with it and so on... and now I have to wait until our next tech preview. And even *I* don't know how long that's going to be. (No, it's not a case of "I know but can't say in a public forum", but I actually don't know).

Ah well, I guess my loyal readers will just have to wait and see.

And, for the record, I should have a new blog post coming out soon that describes how to do a parameterized Value Converter. In the Total Training series (Check out http://totaltraining.com/microsoft for details), I go over how to create a Value Converter that changes a control different colors based on the value (I.e. if it's greater then 90, turn it green, etc...). However, I had someone ask me privately how to create a generic one that you could configure. I've got a couple solutions, and I'm putting the polish on those now.

0 Comments
Filed under: ,
More Posts Next page »
 
Page view tracker