Archive

Archive for the ‘.NET’ Category

A Centered and Resizable Text Header

September 27, 2010 4 comments

Tweeted by @Pete_Brown recently:

Attention WPF (and SL4) Devs: Get back to blogging. You’re getting lost in the amazing amount of #wp7dev content 🙂

Well, when Pete says “Jump!”… I’ve actually been meaning to post this for a few months, so thanks to Pete for giving me a push 🙂

The Problem

A friend of mine is learning Silverlight and in prototyping a simple app he wanted to just use a TextBlock for his header.  When the application has a fixed size, it works fine, but when the size is flexible he ran into one of the issues with TextBlock: it doesn’t resize or scale.  When you set the properties for a TextBlock, you set the size of the font and it never changes.

Here is the default layout we are discussing:

image1

And here it is expanded to full screen on a large resolution:

image2

The text stays centered, but the size stays static.  It makes the header seem small and out of proportion.  And similarly when the window is much smaller the text seems too large and out of proportion.  If you get extreme you can even see some weird results:

image3

Some Ideas

One way to solve this would be to listen to the UserControl’s SizeChanged event and do some math to calculate the new FontSize, but that just feels so WinForm. I’d much rather find a way to do this without code behind.

You could try to bind the FontSize of the TextBlock to a property in the ViewModel, but you still have to find a way to trigger the action.  If you bound the UserControl width and height to the ViewModel you could have its Set method raise the PropertyChanged event for the FontSize property.  And of course, you’d still have to write all the code to calculate it, which I’m sure would include measuring the text, calculating buffer zones and margins, etc.

These are just ideas, I haven’t tried either approach.  You may find a situation where you need to do one of these things or come up with something different, but honestly, these ideas just somehow feel wrong in a XAML world.  In this particular case, where the Text is static, I have a better solution.

Convert Text To A Path

The solution starts with taking advantage of the vector graphic nature of XAML. While Text may not expand and contract as desired, a Path certainly will, so the first step is to convert the Text to a Path.

In Blend, select the TextBlock item and right click, select Path –> Convert to Path (or go to Object –> Path –> Convert to Path).  This will convert the text into a Path object (outlined in Red in the screen shot below).  You’ll also notice the Horizontal and Vertical alignments have both been changed to Stretch and the Margins have been set (outlined in Yellow).

image4

If you reset the Margins to 0, you will see the Text take up the entire space.  If you change both the alignments to Center it will look OK in Blend, but when you execute the application you’ll see we actually get the same behavior as the Stretch.  This is because of Width and Height are set to Auto, which is what we want: if we set these to fixed sizes we are right back where we started.

The good news is that if you resize the window now, either bigger or smaller, you’ll see the header resize itself, so we must be on the right track!

image5

Margins and Proportions

At least in this case, we don’t want the text bumping up against the edges of its Border: it’s distracting and not very clean.  Instead, we’d like a little space surrounding it on all sides. 

You might be thinking “No big deal, I’ll just add a Margin” and you wouldn’t be totally wrong.  The problem is that hard coding the Margin, like hard coding the Text’s FontSize, means it can never change.  So a Margin that looks good when the window is small doesn’t necessarily look good when the window is large. 

What we want is the effect of a Margin, but we want that Margin to be proportional to the available space.  We really can’t solve this with the Margin property, at least not without a lot of work and calculation, which I’m just too lazy to figure out.  So the real solution is not Margins, or even in the Text (now Path) itself: the real solution is in Layout.

Solving the Problem Using Layout

One of the things I see developers new to XAML struggling with is the power of layout.  I’ve started labeling my approach “Container Driven Design” which really relies on the containers to manage the size and spacing of it’s child elements.  It frequently involves nested containers, which is what we are going to use to solve this problem.

What we really want is for our Margins to float and resize in proportion to their parent container.  Fortunately we have a container type built in that is perfect for this: the Grid.  With a Grid, we can specify percentage based sized rows and columns.  (NOTE: Yes, I know they are not *really* percentage based, but an explanation of the Star system is beyond the scope of this article.)

So to solve this problem using layout we are going to wrap our header in a 9-celled Grid: three rows and three columns, with the center cell holding our header.  Right click the Path and select Group Into –> Grid.  If you look at your Objects and Timelines panel you will see the Path is now the child of a Grid:

image6

With Grid selected, you can use the blue bars along the top and left to position the rows and columns:

image7

While I avoid editing XAML, there are a few times that it is simply faster and easier: editing Grid row and column sizes is one of those times.  In the screen shot below, you’ll see that I’ve effectively created floating margins by defining star sizes for the top and bottom rows and right and left columns.  The center row and center column have no size definition, so they will take up the remaining available space.

image8

Execute this and you’ll find that as you resize the window the margins will resize themselves proportionally, the text will remain nicely centered and will also resize itself proportionally.

image9

Wrapping it Up

So there are a couple of lessons I would want you to take away from this exercise.  First, the problem we were having was with static text, so we solved that by turning that text into something else.  We found a graphical solution to our graphical problem! 

Second, we had a problem with Margins, so we used grid rows and columns instead of the Margin property.  We solved that issue by relying on a Layout Container instead of a single property.

In both cases, we found simple and elegant solutions by thinking outside the box.  I’ll grant that this example is not overly complex, but it does illustrate the power of XAML to solve design problems.  And of course, a chance to play around in Blend is always welcome!

Advertisements
Categories: .NET

Blend-O-Rama Update

August 18, 2010 Comments off

Hi Folks!  Hard to believe it has been almost a month since the first ever Blend-O-Rama event!  I’ve gotten lots of questions about the videos and the website, so I wanted to put this together to give everyone an update.

The BOR Videos

When I wrote about the LiveMeeting experience, I failed to discuss recording the sessions.  Kevin recorded the sessions, but the videos themselves are really small.  I’m sure this is partly because I had to set my resolution to 1024×768 for LiveMeeting to display well, and partly to limit storage size.  The actual WMV files came out as 704×528 – like I said, they are small.

They are so small that on my 1600 monitor they are almost unreadable. They view acceptably at 1024×768, so I hate to recommend it but you may want to resize your display in order to watch the videos.  They will be available for download on the BOR site when it goes live.

I hate to make people wait any longer, so the good news is you can download them now in an all-in-one Zip file.

The BOR Website

I’m in the process of moving service providers, so the BOR website has been on hold while I get that set up.  I’m also hampered a little bit by my lack of ASP.NET experience.  Fortunately, I have friends who know a little something about all this stuff! I can happily report that I am about 98% there, with the help of Kevin Griffin and the great team at OrcsWeb.

I have a handful of videos already out there but many more to produce.  Most of the content from the BOR sessions will be available in a series of shorter videos.  I’ll also take suggestions and requests, so for now if you have any put them in the comments here. 

At any rate, progress has been made, albeit not as swiftly as I would have liked.  Such is my never ending story.  Thanks as always to all the supporters out there, you guys make it all worthwhile!

Categories: .NET

Blend-O-Rama Redux

July 26, 2010 Comments off

In case you missed it, July 19-23rd we held the first ever Blend-O-Rama Event.  Hosted by HRNUG, moderated by Kevin Griffin (MVP and HRNUG President), and presented by yours truly, the event was a week of Lunch and Learn webcasts all about Expression Blend and presented over Microsoft Live Meeting.  This was the first event I’ve done of this type and scope, so I wanted to post my review of the event.

Lessons Learned: Live Meeting

This is my first time presenting over Live Meeting (LM), so I’ve definitely learned a few things. The experience itself is a little strange, and I feel disembodied not being able to see the attendees.  As a speaker, I frequently take cues from the audience: I can usually tell whether or not I need to reiterate something, or I can see that they get it and we can move on.  You’d be surprised how much you can get out of a few nodding heads!  Over LM, it’s just me staring at my screen and talking at the microphone, and I really miss the feedback.

We did some testing over LM before the event and intentionally slowed the pace down, but on Day 1 it wasn’t enough.  Day 2 was better and by Day 3 I’d say we had the pace just right.  One side effect is that I can’t cover as much material as I might with a live audience.  I also had to adjust the content I would usually share because Live Meeting has serious issues with things like gradients and animation. 

The hardest thing for me to get comfortable with was Context Switching.  I’m an Alt-Tab fanatic, my left hand is practically deformed from sitting in the Alt-Tab position.  In this case, I couldn’t use it because I took some advice from a friend of mine and did not share my entire desktop over Live Meeting.  This meant that I had to actively select the shared application every time I wanted to switch from one to another.  BTW – if you are a seasoned LM presenter, I’d love to hear how you handle this problem: post them in the comments below.

This also takes time and in the videos you will see a lot of “gray space” while I’m fumbling through the Live Meeting controls.  I found that narrating it helps: letting the attendees know what I’m doing not only helps them understand what they are seeing, it also helps fill the silence which made me feel like I wasn’t just sitting there all alone.

I’m really glad we did a test run using Live Meeting the week before with some unbiased attendees.  We also got some help from David Makogon who gave us some great tips for dealing with Live Meeting and also stepped in a couple of times to moderate when Kevin had other responsibilities.  The biggest lesson here is the Boy Scout Motto: Be Prepared.

Lessons Learned: The Event Itself

I also learned that this sort of commitment is tough.  While the presentations were “only” an hour and a half per day, the event really consumed my entire week.  I’ll admit to underestimating this when we set the event up, mostly because I had given the first 4 presentations many many times: only the last day contained new material.  The pressure I felt to deliver a quality event, however, made me spend all my free time prepping for the next day.

And it really bothered me when something went wrong, as it did nearly every day.  I try to joke about it, but nothing is more frustrating than something not working when other people are watching.  It’s especially tough when it’s something you’ve done a thousand times and for the life of you can’t figure out what’s wrong.  Lesson learned: Expect the Unexpected.

I haven’t done near as much in Silverlight as I have in WPF.  Usually that isn’t an issue, and in Blend it rarely causes a problem, but a few of the issues had to do with my lack of familiarity with Silverlight.  More specifically, I’m accustomed to doing things one way in WPF that I may have to do differently (or can’t do at all) in Silverlight.  In this case the point I was trying to illustrate was that Blend is essentially the same in both platforms, and I do think we succeeded there.

I also learned that I didn’t know as much as I thought I did.  I’ve never been afraid to say “I don’t know” during a presentation: it’s honest and fair.  But I haven’t had to say it so often as I did this week.  Some of the questions really stumped me: in fact, I said several times it was “Stump the Presenter” week.  While I wish I knew all the answers, I also appreciate the tough questions: it means the attendees were really paying attention and thinking about Blend.  What presenter could ask for more?

Day 1 – An Introduction to Blend

I watched the playback and realized I needed to go even slower: there were too many times when the speaking got ahead of the presentation, or when I could have hovered over an item longer to let the Tooltip show through.  I also learned that Gradients are a no-go.  If you watch the video they just show up as huge blocks of color.

Day 2 – Data Binding

They say every presentation has a glitch, and this one was no exception.  I ran into a problem near the end trying to bind a Grid’s DataContext to the SelectedItem of a ListBox.  I can’t tell you how many times I’ve done this, hundreds at least.  It is simple and straightforward… until today.  It just didn’t work and I tried it several times in different ways.  I finally cleared all the DataContexts and Bindings out of the details within the Grid and rebound the DataContext and finally everything worked.  I’m still not sure what the cause was, but I’m starting to think there was something left over from when I tested the Solution earlier that day.

Day 3 – Styling and Templating

I really think on this day we finally nailed the speed and delivery.  The only glitch that stands out in my mind was trying to get Smooth Scrolling to work in Silverlight.  Turns out this is one of those areas that just isn’t the same as WPF. The downside for me is that I feel I spent too much time trying to get it to work.

BTW, the solution is to go to the ItemsPanel Style and add a FluidMoveBehavior to the VirtualizingStackPanel.  Nifty trick!

Attendees: 98

Day 4 – Animations

So understanding that Gradients and things like that don’t work well over LM, I really wasn’t expecting much out of this session.  That being said, it turned out very well.  The Live Meeting video has some odd happenings between minutes 40 and 48, but the WMV file is just fine, so be sure to watch that.

Day 5 – Advanced Topics

By and far this was my favorite day.  As I said in the presentation, this stuff is just plain fun!  I think overall I was happiest with this presentation.  I’m sure some of that has to do with my growing familiarity with Live Meeting, but naturally just as I’m getting comfortable with the format we finish!  Based on the feedback, this one was as much a hit with the audience as it was with me, so  you can expect to see me presenting this one out on the road.

Conclusions

Overall, this was a great event.  I’ll admit, I was drained by the end of it, but the steady stream of thanks and nice messages from attendees tells me we did something worthwhile here.  So much so, in fact, that I think I’d like to have one s
ession a month on different topics. 

What are your thoughts?  Your suggestions and comments will determine what shape this takes going forward, so comment below, email, or Twitter (tag #BlendORama) and let us know what you think.

Thanks again to everyone who took part, and watch out soon for the launch of www.blend-o-rama.com.

Categories: .NET

Sending Email From This Site

July 26, 2010 Comments off

Hi folks, this is just a house cleaning message.

I just wanted to let you know that apparently the email service from this server has been non-functional for quite a while and no one knew it.  It appears to have been corrected and I am receiving email submitted from this site again as of last week sometime.

On the off chance that you submitted an email to me through this site over the last couple of months and I never responded, then it means your message was never delivered.  I pride myself on responding to every message a reader sends me, so I don’t want anyone to think I’m ignoring their messages.

Just a reminder that you can also contact me on Twitter.

Categories: .NET

Holy Cow, what a week!

July 6, 2010 1 comment

This last week has been awesome, probably the best week of my professional career, and I’m just bursting to share.

Wednesday, June 30th

If you follow me on Twitter you may have seen me mention that I took and passed the .NET 3.5 Windows Forms certification exam.  While I already had an MCTS for WPF, I needed this one to qualify for the MCPD (Windows Developer).  If you check out the requirements, you’ll see only the Windows Forms exam counts.  I would go on a rant about how little sense this makes given that WPF is the future of Desktop development and WinForms is on it’s last legs, but this is a happy post so I’ll skip it this time.

I had debated about whether or not I was going to pursue MCPD, but then Microsoft sent me a voucher for a free exam, so I figured why not?  I won’t lie to you: I was surprised I passed.  It’s been 2 years and 2 versions of VS since I last developed for WinForms, and on top of that I barely had time to review.  Still, I did pretty well on the exam and so now I have another MCTS.

Thursday, July 1st

I posted last week about being awarded Expression Blend MVP: I am ecstatic, honored, and humbled.  I spent the next few days in a haze just trying to adjust, it really is a great honor.  A friend asked me at lunch the other day if I was going to slow down the crazy community schedule now.  Naturally I informed him that it would be quite the contrary!  I expect things will get even more intense and I can’t wait!

Monday, July 5th

Pathetic though it may be, I was in the office working yesterday while everyone else was out frolicking for the holiday weekend. It was near the end of the day and I was about to go home when an interesting email arrived informing me that I had been selected as an INETA Community Champion!  Looking at the past winners shows me I’m in pretty good company.  Again I am honored and humbled.

The accolades are certainly nice, everyone likes a little recognition once in a while.  I want to thank Microsoft and INETA, and specifically those who nominated me for these awards: I hope I can live up to your expectations.

However, while it may sound cliche, the truth is that being part of this community is its own reward: I wouldn’t change a thing.  Thanks everyone, and I’ll see you in the community!

Categories: .NET

Expression Blend MVP

July 1, 2010 4 comments

Hi Folks!

I wanted to share a bit of good news with you all: I just received the official announcement that I am a 2010 Microsoft Expression Blend MVP!  You can tell by my overuse of exclamation marks!!!!

I am truly honored, there aren’t a lot of Blend MVPs and it means the world to me that I’m now on that list.  I’ve got a great year planned and some big announcements coming up, so if you are interested in Expression Blend, watch this space!

In the meantime, make sure you attend Blend-O-Rama! Only 18 days left!

Categories: .NET

Processing Command Line Arguments in an Offline ClickOnce Application

June 23, 2010 11 comments

Several years ago when ClickOnce first emerged I thought it was a great idea.  Unfortunately at the time, I found it so difficult and tedious that I didn’t play with it for long.  My buddy Stuart was at my office recently and I was lamenting some distribution issues I was having with one of our WPF apps. 

One of our deployed apps changes frequently, and I don’t send out updates as often as I should because our users environments typical limit their install rights (not to mention many of them don’t even have IT staffs).  This means they rarely have the latest and greatest because it can be a serious ordeal to deploy the changes.  Stuart brought up ClickOnce, and the discussion quickly came to the question “what happens when the user doesn’t have administrator rights?”  Naturally, we had to put this to the test!

What I love about ClickOnce

I love how easy it is to setup and use – We spent about an hour playing with ClickOnce and I was amazed to find how far the tooling has come since I last checked it out.  The setup is pretty straight forward, so I’m not going to cover that right now.  If you specifically want me to write a post or do a video about it, respond in the comments below.

I love the control that I have – The options are pretty sophisticated: you can make an application available online only or online and offline.  When you make one available offline it creates a Start Menu option for you.  You can select Prerequisites, like the .NET Framework version, and where to download them if they aren’t present at install time. NOTE: Some caveats may apply to that, like needing administrator rights to install the Framework.

I love Automatic Updates – You can configure the application to automatically check for updates, the exact feature I needed.  You can configure when to check for updates, how often, etc.  You can even configure it NOT to check for updates.  I set mine to check for updates every time the application starts, which will prompt the user to install the new version before the software executes.

I love that users without administrator rights can install apps – This is a huge win for us.  We did a little testing and found that a user without Administrator rights can install these applications.  I presume this is because they operate in some kind of a sandbox, but I don’t actually know.  Pssst: if this is a bug, please don’t tell Microsoft – I love this feature!

Again, I was struck by how easy all this was to setup and configure, and once again I’m beating myself up for all the time I spent NOT using a supplied feature.

What I don’t love about ClickOnce

So far I have discovered two things I don’t like about ClickOnce: the first is that while I can easily create a desktop shortcut, I don’t seem to have any control over the Icon that gets displayed.  Even more strange is that the icon that is being displayed is a Blend icon of some sort.  It seems to me that this would be a pretty common requirement, so I’m surprised there isn’t a readily apparent way to assign an icon.

But far worse than that is the fact that conventional wisdom says that ClickOnce applications can’t handle command line arguments.  This was a deal breaker for me: virtually all of our desktop apps need to be executed from AS/400 sessions.  These sessions pass arguments into the applications via the command line, and losing that capability would negate most of the value of the software.  While I’m sure I’ll figure out the Icon issue, the command line arguments problem needed addressing immediately.

Command Line Arguments

So it is not entirely true that ClickOnce applications can’t handle command line arguments, but until .NET 3.5 SP1, they could only handle them as query string parameters.  This underscores the fact that ClickOnce is a heavily network dependent technology.  You may get an application that installs on your machine with a Desktop Shortcut, but this is still a network deployed application, and as such it relies on some URI scheming.  For me, this isn’t going to help much, so what I was after was the ability to pass in arguments in offline mode using a more traditional approach, like C:\> MyApp.exe arg1 arg2 arg3

I want to get to the nitty gritty of the blog post, so I won’t go into all the details, but here are a few things you need to know:

  • You have no idea where the app is actually installed.  If you do manage to find it, it will have a user-unfriendly name
  • The Shortcut name is the same as the “Product Name” field in the Publish Options Description in the ClickOnce configuration
  • The Shortcut on the Desktop has a special extension: .appref-ms
  • The easiest way to execute the application from the command line is like so: C:\> %userprofile%\Desktop\AppName.appref-ms
  • If your “Product Name” has spaces, you will need to wrap that in double quotes: C:\> “%userprofile%\Desktop\My App Name.appref-ms”

What would be perfect is if I could just append the Command Line Arguments to the end of that call, so it would look like this: C:\> “%userprofile%\Desktop\My App Name.appref-ms” arg1 arg2 arg3

Try it out, though, and you’ll quickly find that this does nothing: the standard args string array is empty.

Not A New Problem

Naturally I hit the Interwebs in search of a solution, I mean, it has to be out there, right?  I was quickly discouraged though to find hundreds of references all saying the same thing: you cannot pass command line arguments to an offline ClickOnce application.

Go ahead, go search for yourself, I’ll wait.

See what I mean?  The question has been asked a million times all with a resounding NO as the answer.  I was about to give up when I spotted something that gave me hope.  An article on MSDN entitled How to: Retrieve Query String Information in an Online ClickOnce Application has a note block with the following text:

Starting in .NET Framework 3.5 SP1, it is possible to pass command-line arguments to an offline ClickOnce application. If you want to supply arguments to the application, you can pass in parameters to the shortcut file with the .APPREF-MS extension.

That sounds like exactly what I want!  Problem is, I’ve already tried that and it doesn’t work.  And naturally, there is no related article on MSDN telling me how to do it, just that it can be done.  Finally, something to give me a little hope!

Finally, I hit the mother lode: an article by RobinDotNet explaining How to pass arguments to an offline ClickOnce application.  Robin’s blog is all about ClickOnce, and interestingly enough I found plenty of earlier posts and forum entries by Robin stating this couldn’t be done, even as late as January 2010, long after .NET 3.5 SP1 was released.  I’m not criticizing, but I am pointing out that even to those “in the know” it would appear this is a non-documented feature.

A Simple Solution

So I read through RobinDotNet’s post and it seemed like a lot of stinking work, and I try to avoid that as much as possible!  She explains a bunch of stuff I didn’t need, like how to locate the shortcut using SpecialFolders, and ho
w to programmatically execute the application.  She even discusses how to create and pass the arguments.  Good stuff, but overkill for me.

There is one key line of code I found in her post that was exactly what I needed:

//Get the ActivationArguments from the SetupInformation property of the domain.
string[] activationData =
  AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData;

What I found is that this will return the arguments provided on the command line.  Well, sort of …

From the code above you can see that ActivationData is a string[].  At first, I assumed I would be able to simply replace the old e.Args with this value, but I found that only the first argument is available in ActivationData.  I don’t know why this is, and it doesn’t make sense, but all my testing proved this out.

To solve this, I changed the way the arguments are passed in by making them comma delimited.  It doesn’t seem to matter what delimiter you use, as long as it isn’t a space.  Then, I parse ActivationData[0] using Split.  In the code sample below, I am also checking to see if this is a ClickOnce application and checking for NULL, the default value of ActivationData if no arguments are passed.

string[] args = null;
if (ApplicationDeployment.IsNetworkDeployed)
{
    var inputArgs = AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData;
    if (inputArgs != null && inputArgs.Length > 0)
    {
        args = inputArgs[0].Split(new char[] { ',' });
    }
}
else
{
    args = e.Args;
}

I assume if I wanted I could still deploy this with a traditional MSI, so doing it this way supports compatibility with a traditional command line execution. In this case, I am parsing it to build another string[] because I have existing code that works with Command Line Arguments.  This example is executing inside my WPF application’s App.xaml.cs file’s Application_Startup event handler.

Getting it to work in .NET 3.5 SP1

In order to get the code above to compile, you will need to add a reference to System.Deployment.Application, which is in System.Deployment.dll.  If you are coding against .NET 4.0, this DLL is available in the “Add Reference -> .NET” dialog.  When I tried to add the reference to an existing .NET 3.5 application, the DLL was not available in the list.  I thought this was a little strange, since the documentation claims it is supported as far back as 2.0.

I did a little digging and found the DLL location.  I was then able to Browse for the DLL and add a reference to it manually and everything seems to work:

C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Deployment.dll

The Results

The results are just what the doctor ordered: I can now call my application from the command line and pass it arguments like so: C:\> “%userprofile%\Desktop\My App Name.appref-ms” arg1,arg2,arg3

It’s not perfect, but it is easy.  The biggest change is sending all the arguments as a single string, but that is easily handled.  Now if I can just figure out that Icon …

Categories: .NET