Page 1 of 1

Making the transition from WinForms to WPF - part 1

#1 tlhIn`toq  Icon User is online

  • Please show what you have already tried when asking a question.
  • member icon

Reputation: 5511
  • View blog
  • Posts: 11,815
  • Joined: 02-June 10

Posted 26 April 2013 - 11:58 AM

*
POPULAR

Learning C# Series

WPF for the WinForms coder

Part 1 - Binding GUI controls to class properties, or "How to make smarter GUI's with less code"




Summary
There are lots of tutorials on doing things in WinForms. It has been the primary GUI development for the .NET developer for years and years. Like any technology people want more from it. More options, faster processing, greater flexibility, more compatibility with other devices. Thus Windows Presentation Foundation was born. WinForms is not dead. I don't expect it to even be retired for years. But (like me) it is about ready for senior discounts and Denny's and IHOP. WPF leverages the power of DirectX for its drawing making it fast and powerful. There is a bit of a learning curve for the WinForms developer making the transition. I hope this tutorial helps reduce that curve for you and make that transition a little easier. I am by know means a deep expert on WPF. Compared to many who jumped to WPF when it first hit the scene I'm still groping to *really* being good and elegant with it. But I am someone who has successfully left WinForms behind and gone 100% WPF, just like you are looking to do or you wouldn't be reading this. Hopefully you can learn from my mistakes rather than repeat them.

To be clear... This is not an introduction to C# tutorial, and I'm not planning to go deep into repeating explainations on how to make methods, properties or class design. I've written tutorials for those topics if you want them. This is for the coder that is already reasonably comfortable with WinForms (more than just drag-n-drop a button) and is looking to move forward into WPF.

Why is this important to learn about?
WPF is the direction Windows development is being directed. You're not going to stop that. If you are new to development then you are already competing with all the developers with years of experience under their belt.
  • If you stay with WinForms then you are giving them the advantage. But if you latch on to the newest technology you have a skill for your resume that many established coders (the ones that have grown complacent) don't have.
  • If you are one of those complacent coders look in your review mirror. That kid behind *is* reading this and will be filling out a job application with your employer and will have something new, modern and hip to bring to the negotiations table.
  • WPF is the basis of development for WinPhone8 so if you want to join that quickly growing market of mobile device developers you need this.


Terms you should be familiar with:
Spoiler





Note: All examples were created using Visual Studio 2012, targetting the .NET Framework 4.5, using a WPF project. We'll do our best to point out anything that might not work in older versions or ways of using the same techniques in WinForms.



WPF for the WinForms coder




One of the needs addressed by the creators of WPF was to let designers work independently from coders. If you are on a team the graphic artist with all the design experience can work on the look of the Window while the coder works on the code that makes the program run. This helps speed programs from concept to delivery, while putting the appropriate task with the person with the right talent. If you are both the designer and the coder, that's fine. But the option is there to split the tasks.

There are a couple realities the WinForms coder needs to accept and stop fighting to successfully make this transition.
  • WPF means learning a new coding language: XAML. The Windows you will be making ('Windows' not 'Forms') are coded in XAML not C# or VB.NET
  • Drag-n-Dropping controls from the Toolbox, to the designer, then double-clicking on them to make your handlers is pretty much behind you know. XAML is more typing and less mousing.
  • This is not a one-for-one replacement. There are conceptual differences between WPF and WinForms that are going to change the way you look at, design for, and code for.

These are the things that I think make the transition a little daunting for people. Trust me when I say the learning curve isn't insurmountable and it is VERY much worth tackling. The results you can achieve with WPF are significant, and a significant improvement over WinForms. I am going to show you some shortcuts that should help nullify the extra typing, and show you some of the power you gain that in the long run actually makes big changes easier and faster.

WPF is going to give you unprecidented control over the look of your GUIs, like you've never had with WInForms. If you want a border around your group of controls, you can make it 2 points on the left, 4 on the right, with radius corner on the top, and square on the bottom, in blue, with a 4 point margin on the left and 10 point margin on the right.

And you can define styles for your looks. So if you can define the 'normal' font for your application as Dark Grey, Arial Sans, italic, with an underline. Then every label you make you just set to 'normal'. Later when the boss says to change the font to Blue Courier you only change it in one place and everything that uses 'normal' is suddenly changed.

XAML is an evolution of HTML. Most of use, even the oldies amongst us have played with HTML. It is a language of content surrounded by tags. For example:
<Window>
    <!-- This is a XAML comment -->
    <!-- Notice how the Window tag has a matching closing tag -->
</Window>


Your entire Window design is built like this. A <Window> houses a <grid> which houses a variety of different <Panel> types which might have your <Button> <TextBox> <Label> and so on. Some of those look pretty much like their WPF counterparts and some are going to be new like the <StackPanel>.

WPF is very much about binding controls and their attributes to properties in the code behind the window. WPF presses the developer to do the right thing and make objects for their data and not just sloppily use textbox4.text like it was their private variable to hold the user's first name etc. You might have to accept cleaning up bad habit as we go so try to see that as another benefit about this transition not an evil side effect.

Let's make our first WPF application. It doesn't have a solid real-world purpose like becoming an address book or anything. Its purpose is demonstration and to be a place we can learn these new concepts through hands-on play. So make a new WPF application in Visual Studio. I called mine 'WpfTransitionOne'.
Spoiler


When Visual Studio is done doing all its magic, you should see a basic blank Window and the XAML that created it, much like this:
Attached Image
Lines 1 & 8 are the opening and closing tags for the Window itself.
Lines 5 & 7 are the opening and closing tags for a <Grid> control. We'll discuss that more in a bit.
Likes 2 & 3. I like to think of these as corresponding to the USING statements at the top of a WinForms project. They specify namespaces we are using and make a handy shorter name we can reference rather than type out those long paths every time we want something from within the namespace. Just like using System.Windows.Forms means you can just type Button in your WinForms code. These first two lines are in every WPF project you're going to be doing, and VS puts them there for you. So just leave them.

THe line I really want to draw your attention to is line 4. These are the first two attributes of a WPF control we are going to play with. Notice that in WPF all attribute values are specified as strings within quotes. The conversion from "350" to a numeric type will be made for us automatically because the Height attribute is a numeric type. This makes it easy for us to store and reload our settings since they are all going to be strings. Go ahead and change the Title of the window along with the Height and Width. Notice the designer updates in real time as you would expect.

Now we are going to add a button to our window, but we're going to do it the 'WinForms' way first to demonstrate why we don't do it that way in WPF. So select a Button from the ToolBox and drag it anyplace onto the window designer. It will look something like this with slightly different values depending on where you dropped it.
Attached Image

Notice the designer graphically shows the margins for where this is placed, and a line of XAML has been added for this button.
<Button Content="Button" HorizontalAlignment="Left" Margin="54,46,0,0" VerticalAlignment="Top" Width="75"/>
First lets point out 'Margin' not 'Location'. WPF controls set their margin on each side relative to the control on that side, rather than setting their .Location within the parent control like WinForms. By dragging the button directly to designer form we forced the designer to try to interpret what we are trying to do. The best guess it could make is that we want that button exactly where we placed it. Well that doesn't sound so smart and dynamic like we've been hearing about WPF does it? Select the line of XAML that makes this button and hit [delete]

Now we are going to do *almost* the same thing with one change. I want you to grab the button from the Toolbox and drag it directly to the XAML code, and drop it on a blank like between the <grid> tags, as shown:
Attached Image

What you should see probably looks wrong at first. It looks like a big grey box taking up your entire workspace of the window. That's because the XAML defining your button is just the button code with no attributes limiting it. <Button />
Since it has no margin, no content, no anything... It expands itself to the limits of its boundaries. I know your first instinct is to want to set a size for it. And you can. You could just set the Height and Width properties just like the line earlier did for the Window itself. And there are plenty of times, maybe even most times, that you would do this with a button.
<Button Width="100" Height="35" Content="Example"/>

But what about a time you wouldn't. Hmmm... Tic tac toe grid for example. When you are done playing with the code for that button, delete it so we have the empty Window we started with.
Since we have a grid, what is a grid made up of: Rows and Columns. So lets add defintions for 3 rows and 3 columns since that is what a tic-tac-toe grid looks like.
Attached Image

Notice how the designer guidelines even look like the TTT board we would expect. (those are just guidelines and not seen if you run the program.)
So at this point we have a <Grid> of rows and columns, but nothing else. So let's add some buttons.
Right after the row and column definitions we'll add the first button.
THis is the line for the button
<Button Grid.Row="0" Grid.Column="0" Margin="10"></Button>
At this point your entire XAML should be this:
Spoiler


To make the rest of the buttons, just select all of line 17 and copy, then paste 9 times. Now update the row and column values.
Spoiler


Hit the F5 key and run this. Resize the window. Make it bigger and smaller. Notice how the buttons automatically resize and reposition. This is with no code handling the .Resized event like you would do with WinForms. The buttons have no fixed size so they expand to fill their space which is the cell defined by a row and column, minus the margin of 10 we set. As the window gets bigger, the columns and rows grow, the buttons grow to fill the space.

So what's all this talk about 'binding to properties' ? That's a good question; I'm glad you asked.
In WinForms if you want a lable or textbox or button to have some dynamic response most people tend to just set the .Text property within their code. But the down side of that is it makes the code tightly bound to the GUI. It gets worse when you want that association to be two-way: You want a textbox to hold something interesting, like the path to a file. But you want the textbox updated when you programmatically change the path, and you want the path updated when the user types in the textbox. You text to get something like this:

void PathTextBox_TextChanged(object sender, eventargs e)
{
    MyPathProperty = PathTextBox.Text;
}

string MyPathProperty
{
    get { return PathTextBox.Text; }
    set { if (PathTextBox.Text != value) PathTextBox.Text = value; }
    // Have to do the check to avoid a never ending loop
}


Look at how our code is now so tightly tangled with our GUI that we can't really re-use it elsewhere without a lot of hassle.

But in WPF we can do something more dynamic, and cooler, that will let the C# code behind operate ever if we use the class in 10 different projects.

Unlike WinForms every object in WPF doesn't automatically get a name. You may have noticed that this window doesn't have a name and neither do all those nine buttons we made. Many times you will have controls in your designs with no names simply because there is no need for them. But in this case we need for the Window to have a name. We are going to use that name as the context for where some data should come from. So lets give the Window a name. In the XAML for our open project go to the end of line 3 and hit return to insert a new blank line 4 for this markup:
x:Name="thisWindow"

Line 18 should now be the markup for our first button, the one at row 0, column 0. The text you see on a button is not the .Text property like in WinForms: Its now 'Content'. "WHy would they do that?" you ask. Because you can nest other things in the Content of a button, like an image, and some text, and an aniation etc. For now we are just going to use it for text. Please update the markup for that first button to this:
<Button Grid.Row="0" Grid.Column="0" Margin="10"
                Content="{Binding ElementName=thisWindow, Path=ClickCounter}"></Button>

To bind to a property of an object we need to specify the property and the object. We do that with the attributes ElementName for the object, and Path for the property on that object. So we have just bound the Content of the button to the ClickCounter property of thisWindow. Now that they are bound, we need to make the actual ClickCounter property. I'm not going to repeat all about INotifyPropertyChanged event here as I covered it in my Properties tutorial. If this is the first time you've seen this, stop now and go read that tutorial then come back here.

At this point the your code behind is only what VS put in for you. Lets update it with the new property and a method for updating our ClickCounter.
Spoiler


Now that we have a method to run when the button is clicked, let's tell the button about in XAML. We add one more attribute for Click and now our button XAML looks like this
        <Button Grid.Row="0" Grid.Column="0" Margin="10"
                Content="{Binding ElementName=thisWindow, Path=ClickCounter}"
                Click="Increment"></Button>

Go ahead and save everything, then F5 this to run it. Notice that the button now displays '0' as the default value for an int. Click the button a couple times and notice the value increasing. Close our little sample and lets talk about it for a moment. I know its not exciting to look at, but realize that our C# isn't directly changing the displayed value. The button click handler only does one thing: It increments our counter. Clean and tidy. You could grab all the C# and reuse it right now on any other window. The button doesn't even have a name to be referenced by, so you won't break it if you rename it or use this code on another Window for a button, a lable, a textbox, whatever. Is that a little more exciting? Let's talk about how it is actually working.

You click the button, that executes the button handler 'Increment'.
That method increments the integer property ClickCounter.
When we set a new value to that property we announce it through the PropertyChanged event.
The XAML of the button has basically subscribed to the property so it gets notified by the PropertyChanged message and updates the content of the button.

I want you to stop and think about the implications this might mean for you. If you want to write a game, you could put a lable on your Window for the score of PlayerOne, which is an object other than window. You could bind the PlayerOneScoreLable to the .Score property of the PlayerOne object. The object doesn't know it has a subscriber. You don't change anything in the class. It doesn't reference the Window or the label in any way. Yet the score will stay up-to-date every time your player earns a point. Or it could be the name of the font the user selected in that text editor you're making, or the TO: address in that emailer you want to create.

This idea of binding WPF Window controls to class properties is everywhere in WPF. As a WinForms coder it might seem odd and you might be tempted to say "Oh hell with that, I'll just do it the same way I've always done for years." Why? So you can keep yourself using old techniques and not move forward? Then why bother with WPF at all if you're not going to accept the way things are done in it.

This has been an example of one-way binding: The property changes and the GUI follows. Lets move to two-way binding, like you would see with a textbox. You want to update the GUI when the property changes, and update the property when the GUI changes.

To make sure we are all starting at the same point, and kind of break up each example to something you can easily refer to later lets make a new solution. I've called mine "WpfTransitionOneTwoWayBinding". Not only are we going to do two-way binding but I'm going to sneak in a little example of using the StackPanel, which I use a lot in my programs. I'm not going to walk you through the typing because I want to walk you through the example instead. So here is the XAML and C# code behind.

Spoiler



Spoiler


Before we even talk about, F5 this and run it. Fill in your first name and notice how it is appearing in real-time as you type, on the line just above. Now type your last name and again see how it is appearing in a nicely formatted "Last, First" display. Now click the Yogi Bear button. Simple, yet cool. Its also an example of what your users are coming to expect in even the simplest of displays.

Now lets study it a bit. The C# first since you're more familiar with that coming from WinForms.
  • Notice that it is really simple logic to follow. There are properties for FirstName, LastName and FullName. None of them do anything more than take care of themselves and announce when they get changed. Clean.
  • Line 30 Starts the handler for a property change. That's right, we are going to listen for and react to our own property changes. All this handler does is update the FullName property. It takes FirstName and LastName and puts them together with a comma and space between the two. The method leaves anything else up to some other responsible mechinism. It follows good practice of doing only one thing with no side effects.
  • In the constructor we subscribe the method to the event.
Like I said, its really simple and easy to follow because nothing is doing anything more than taking care of itself.

Now the XAML. That's a bit less familiar to the WinForms coder but I hope its looking less scary already.
First we got rid of the grid. It just wasn't the right choice here. We put in a StackPanel with its orientation set to verticle (line 9). That does what the name suggests: Its a panel that stacks all of the controls within it. In this case it will stack them vertically.
Inside that vertical stack panel we created a textblock to hold our FullName.
Then we made another StackPanel, this one oriented horizontally.
You can see how the textblocks, textboxes and our Yogi Bear button are all lined up on the same row. They are stacked horizontally.
The TextBoxes have their Text attribute binded to the corresponding property we made in the C# code behind. But there are a couple more optional bits in the binding: Mode and UpdateTriggerSource. There are a few modes for binding and they are covered in the MSDN. For now we will just say they do what they claim. TwoWay binds in the two directions we want: To and from the GUI and the property so a change in one is reflected in the other.

I'm not going to go on an on about each and every little attribute. You see them there in the Properties pallet when you click on a control such as the TextBlock. Take some time to play with them. See how each number in the Margin makes a change to the spacing on one side of a control.

What if we want to make all those name controls look like they belong together? Well, a Border around them would do the job nicely. With WinForms you would have had to make a group box, over size it, drag these controls into it, resize it just right, then drag it to the right place on the form. The GroupBox drew a border for you and you didn't have much control over its look.
With XAML all we have to do is add an opening tag above all the controls and a closing tag below them.
  • Line 11 of our XAML should be the start of the horizontal stack panel <StackPanel Orientation="Horizontal">. Add a return to the BEGINNING giving us a new blank line 11. On our new 11 type <Border>
  • Do the same at 32 for the closing tag. <\Border>

This gives us a GroupBox surrounding our horizontal StackPanel that holds the naming controls. Oh, but we don't see anything. That's because we haven't defined the color or thickness of the lines. Update the opening Border line on line 11 to this and look at the difference. <Border CornerRadius="5" BorderBrush="BlueViolet" BorderThickness="2" Padding="2" Margin="0,4,0,0">
Attached Image

What if I told you that you could have even finer control of the border? Try this for a new BorderThickness BorderThickness="2,2,5,10"

Hopefully you're starting to a glimpse into the possibilities of WPF, and see just how fine-grained the control of your presentation can be.

In Conclusion

Master Spliter to the Turtles said:

Possess the right thinking.

This tutorial has gotten long enough, and we've only covered the first topic about transitioning from WinForms to WPF. But it is a very important topic that I think most people making this move would run in to as they start playing with WPF on their own, so I wanted to cover it right off the bat so you can start on the right foot and not bring old techniques with you. In future installments we will be using this technique to do even cooler binding.

Please enjoy the next in this series (click the link)

This post has been edited by tlhIn`toq: 01 May 2013 - 10:00 AM


Is This A Good Question/Topic? 11
  • +

Replies To: Making the transition from WinForms to WPF - part 1

#2 Michael26  Icon User is offline

  • DIC-head, major DIC-head
  • member icon

Reputation: 355
  • View blog
  • Posts: 1,524
  • Joined: 08-April 09

Posted 30 April 2013 - 02:28 PM

Do you know any good book about Xaml or WPF of your choosing, i know this site has a lot of book recommendation
Was This Post Helpful? 0
  • +
  • -

#3 Rs_Chief  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 10-February 09

Posted 01 May 2013 - 09:37 AM

View PostMichael26, on 30 April 2013 - 02:28 PM, said:

Do you know any good book about Xaml or WPF of your choosing, i know this site has a lot of book recommendation


'Pro WPF in C#2010 by Matthew MacDonald (Apress)'. It will give you a solid foundation.

Ray
Was This Post Helpful? 0
  • +
  • -

#4 jimzcoder  Icon User is offline

  • D.I.C Regular

Reputation: 54
  • View blog
  • Posts: 331
  • Joined: 14-November 12

Posted 12 June 2014 - 08:52 PM

this tut is really nice and can be easily understood. thumbs up :)
Was This Post Helpful? 0
  • +
  • -

#5 AustinTX  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 17-June 14

Posted 17 June 2014 - 07:19 AM

Is it just me or is the Spoiler show functionality not working?

Tried loading the page in IE, Chrome, and FF.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1