Join Scott Peterson for an in-depth discussion in this video Data binding in action, part of Developing UWP Apps: 5 Bindings, Commands, and Converters.
- [Instructor] Okay, we're over here in our UWP app, tagur, that we've been working on this series to take a look at the concepts of binding, mostly data binding but in general, binding just to demonstrate some concepts. Let's go ahead and open up our pages folder, and go to our pages two. And you'll recall that all we have is just this junky little text block serving as a label in here. Let's just get rid of that, and let's stick a stack panel in there, and let's take a look at that content. So basically we just have text block, that's a label there.
We've got that text box that has nothing in it. We have a save button, and we have a change display label. So you can see that this is pretty clean in the XML here, I'll just go to view code, and create that there. Go to definition, and go to definition. And so this really isn't doing much here. So we just load that up, and we'll take a look at that page. And so the idea here is that if I type in a display name of Scott, for example, we'd want to save that.
And we don't want to have to refer to the text property of that text box, which is the way that you would've done it sort of in the olden days before we had this concept of data binding. That we'd have to refer to the property of that actual control, which is a pretty lousy development method. So let's go back here into the code, and let's just for kicks just create a new class file. Let's pretend that we have a user info class. And we'll just say that it's going to have three string properties, display name label, display name, and save label.
Let's go back over to our XML. And so for the text of display name, we would just say binding DisplayNameLabel. And for the text we'll say that we want that to be this binding DisplayName. And for the content of that button, that button there, we'll just say binding, and what did we call that, SaveLabel. If we go back over that code, we can just create a public property.
It doesn't actually have to be public, but I'll just create a public property of type UserInfo, and I'll call this CurrentUser. We'll just do a quick get and set there. And let's instantiate that on a loading event. We'll just say this.loaded. This isn't really the way we would always do it, but just to demonstrate. This loading event will say this.CurrentUser = new UserInfo, and we'll say that the DisplayName is Scott.
And the DisplayNameLabel is Display Name. This is what's going to show up in that text block. And we'll say the SaveLabel is Save. Okay, great. So we have this little class file. It's this object, it is just of a type UserInfo. Now I want to bind this page to this object. So we would just say this.DataContext = this.CurrentUser.
So we're essentially saying all of the data that's going to be used on this page is going to be bound to this CurrentUser object. So now if we load this up, we'll get rid of that comma there, so now if we load this up, remember, DisplayName, DisplayNameLabel, and SaveLabel are going to bind to DisplayNameLabel, DisplayName, and SaveLabel. And we're just saying bind it to that. We don't have to set a data context or anything here because we're setting the data context in code behind. So let's load that up now, and go to that page.
And notice how we've got that Display Name, Scott is populated, and save on the save button. So let's just take a look at something else here. Let's say we were going to want to change the label from display name to something else on a label click. So now we could say this.CurrentUser.DisplayNameLabel = First Name, so it started out as Display Name, now it's going to go to First Name. Let's load that up and take a look at how it's going to happen when it doesn't work.
So now, we'll put a break point back here just to see that we are running that line of code. But now if we say change display label, the display label says Display Name, and it changes the line of code to First Name. But notice how our UI did not change, it still says Display Name. This is because we're not using an observable pattern here, and this is really important. So back in olden days we would have to do like an INotifyPropertyChanged, and we're still essentially doing that on the backend.
But the idea here is that there's really two types of objects that we need to implement in order to have our UI stay in sync when we're binding. We need observable properties, or we need if it's a collection, an observable collection. The observable properties use the INotifyPropertyChanged on the backend. And an observable collection essentially does the same thing but for collections. So let's see how we would implement that here because what we have is just essentially static labels.
We're not notifying the UI whatsoever. So if we bring in this variation of this class, we're going to change that to ObservableBase. Let's take a look at what we're doing here. So now I've got a DisplayNameLabel, but I'm doing in the get and set, I'm doing this SetProperty. DisplayName this, get set, I'm setting the SetProperty. And I'm passing it by ref. The key here is that I'm inheriting from this ObservableBase.
If we go to that ObservableBase, notice how this is just kind of old school INotifyPropertyChanged stuff in here and we're just creating this nice method that is this SetProperty. So we could actually stick all of this code, this OnPropertyChanged code in our class, but it's much easier if we just inherit from that and create this ObservableBase or BindableBase base class. So now what's going to happen is that the UI is going to be notified of updates.
So we haven't had to change any code in our model. All we're doing is changing the way that it's getting notified by going from a static public string DisplayName to this ObservableBase displayName. So now if we go ahead and run it, you can go to that page. Now I'll say change display label, it's Display Name, now it's First Name. And now notice in our UI how it notified the user interface and the First Name has been changed.
Well, realistically this ought to be able to happen with anything, right? So I've got the name Scott in here. I should be able to change that to John and have the same thing happen. So if I go back here and put a break point there. Now if I say save, and I look at this object, this.CurrentUser, notice how the DisplayName still says Scott. So our SaveLabel is still Save, the DisplayNameLabel is First Name so that's fine, but the DisplayName is still Scott.
It doesn't register that change at all. So one more thing that we would want to do here, we would want to say that that binding now is a mode of TwoWay. This is going to say bind it when the data comes in, and bind all the changes as they go out. So now if we load that up, we go over there and we change Scott to John, say save and take a look at that CurrentUser, notice how it updated to John.
Now this is really helpful because as users are filling out forms, or users are changing settings or whatever we have to do, we don't want to ever have to check any update, or to track characters, or look for a text change. We don't want to have to follow any of that. All we want to do is take a look at the data on the backend, or the model on the backend. Look at the properties, and then evaluate data against the properties disconnected from any control. So you notice that here I'm not saying TextBox1 equals or anything like that.
In fact, the controls don't even have a name. And this is important because now if I want to put in a new label, I can say TextBlock binding DisplayName here. We'll say FontWeight = Bold, we'll make that bold there. Now any changes that we make are going to apply everywhere in the UI.
So I could say John, and notice how it changes to John. So this is really important because we may have elements all over our user interface that need to bind to that display name. And this can be true for anything, any value. Dark theme, light theme, color, you know, a list of customers, a list of products, a product count, whatever it is, it doesn't matter. The cool thing here is that when it gets updated one place in the model it's going to reflect in the UI everywhere without us having to write any code. But one thing I want you to notice here is that as I was typing, notice how it was not changing.
It's only changing when that control loses focus. Well, we have some control over that in the Universal Windows Platform too. So here I could say Mode TwoWay, but I could also say UpdateSourceTrigger is either default, which for a text box is when it loses focus. Or I could actually say update the source when the property changes. Now let's take a look at that behavior.
So now when I go here and I say J, notice how it's changing real-time. So you may not want to do this sometimes for performance issues, but honestly I don't see any drawbacks in a pretty clean uncluttered UI from doing this. So I could just say John Boehnem, and save it, and it's going to be notified. Then the UI is going to notify our model that that change has taken place. So one other thing I'm going to do here is I'm just going to add this OnSaveClick.
And now you'll recall from our previous session how we can just save this to temporary storage now. So we're just changing this DisplayName in this last run that we're going to use a bit later in this session when we save that. So we can load that up. And I'll change my display name to Jimmy Page. We'll say save, and it's good. Okay, so that's pretty cool. That's showing us kind of two or three examples of just binding in this simple pattern where I've just created a class right in the page.
But most of the time you don't want to do this because in this example I was binding to the DisplayName, but that DisplayName is really only scoped. We don't use that word too much anymore, but it's really only scoped at this page level, so I can't really access it. I mean I could, but it's not really reasonable to access this app-wide. One thing I can do here in the page, though, is I could also add a reference to a value. For example, I could just say public SolidColorBrush BackgroundBrush.
And I could have it load up a value. So I could change this to anything. I could change it to light gray. We could change it to whatever we want. If we go back over to our page, notice how we have values like this, ThemeResource, and this isn't technically binding. Again, remember I'm referring to these as bindings. I consider these bindings. Technically, they're handled a little bit differently, but I'm still considering these bindings. So it's binding to the ThemeResource ApplicationPageBackgroundThemeBrush.
Well, I could just use the x:Bind, and I could bind to BackgroundBrush. Notice how that appears for me. Remember we had this public BackgroundBrush. And I'm just saying x:Bind BackgroundBrush. What this is going to do is just check to see if a BackgroundBrush exists in the immediate scope. So let's go ahead and load that up, we'll go over to that page. And notice how the BackgroundBrush has now been changed based on that binding.
And again, that's something that's just going to load up when we get to the page. So if I were to put a break point here and load that page, you see how we stopped right here on this BackgroundBrush. So that's pretty cool as well. Let's take a look at how we're implementing this real-time, though, in the application. In a real world application, you're going to want to have one or more view models. Now, again, this is going back to standard MVVM.
But we have models, for example, we have this image info model we've been working with through this entire series that we're using to show a collection of images on the page. So that has a title, it has tags, it has a url FileBytes, Videourl whether it's a video or not. We've got this model, but we've also got a view model that we're working with globally throughout the app. And notice that this view model actually inherits from that ObservableBase as well. So I've got a number of properties on here.
One that we've been working with heavily is this CurrentImages. So just so we have the context here, if we go over to the main page and take a look at our grid view, notice how the ItemSource is CurrentImages. That matched to CurrentImages here. I've got some other things here, SelectedImage, TitleContents, Suggestions that we used when we were looking in our advanced controls. I've got a number of properties that were set. You'll recall that we implemented this OnNetworkStatusChanged, but now I've changed the InitializeImages to pull up images that are from temporary storage when we were talking about networking and storage in our previous session I went in and changed this to update this model.
So the idea here is that CurrentImages is going to be populated in this StartViewModel, and every element of our application is going to connect to our StartViewModel. So if we were to pull up our StartPage which is really just the page that houses our frame that we created in our first session with layout navigation, notice in the StartPage I'm just setting the DataContext of that page to that view model. So if we go over to that view model, it's this StartViewModel.
And the important thing here is that any controls that are within this page, or children of this page, they get loaded up in this frame of this page will all be aware of that data context at this point. So remember, MainPage is getting loaded up in a frame. If I were just to come to the MainPage and put a break point, notice how I'm not setting a data context on this page anywhere.
But if I say this.DataContext, notice how I've got a DataContext. Okay, this is because I set the DataContext on the parent page, so it's going to inherit that. And the only way that this would change is if I were to overwrite it. If I were to say in this specific page this.DataContext equals something that would work, I can also do this with any controls. I could name a control, for example, I could name this grid something. I could say x:Name="myGrid".
And then I could say this.myGrid.DataContext. Every object in the Universal Windows Platform that is a UI element, or inherits from a UI element is going to have a DataContext. And this DataContext is going to be critical for all data binding. Okay, so let's go back over and take a look at that view model. In fact, I'll get rid of that so we don't junk that up there. So if you go back over that view model, remember I've got this CurrentImages property.
And this CurrentImages property essentially is just a list. The problem is when we use a list, it's not observable so it's not going to notify the user interface that changes have been made to this collection. So if we use an ObservableCollection every time images get added or removed it's going to notify the UI. So now when we load the app, instead of initializing static images, or having this browse for images, I'm just pulling them up from the temporary folder.
And for every item that's found in the temporary folder I'm adding it to the CurrentImages collection. When I do this, and add it to an ObservableColleciton it's going to notify the UI. So it'll be fast, but you'll be able to see these images come in one by one. But another thing that we can do here that's important is as this count changes we can have it update the UI separately. So if we really wanted to see how this would function, I could just simply put a little delay in here since it happened so fast, of like half a second, what's going to happen is it's going to grab those images, and it's going to wait a half a second before it adds them to the collection.
And then we can actually watch that update live. But the other thing that I've done is over here in the StartPage, I've added this TextBlock that is bound to the CurrentImages. But it's also bound to the Count, and this is really cool in UWP that we can just bind it to a property, so CurrentImages.Count. Then I've got another part of a label that just says images. So this will update real-time as those images are added, just like a ticker.
So let's take a look at how that might look. So there we go, every half a second load that in. So you can imagine this is cool for real-time updates in any scenario. I mean, you can even have a fast moving Twitter timeline, or anything that comes in, anything that could happen the UI is just automatically being updated. Okay, so that's pretty slick. Let's go and look at a couple of more things, though, before we finish this section on binding. I want to just draw your attention to some changes that I've made.
We have this idea of StaticResources and ThemeResources, and you'll notice that all throughout the application we're referring to these ThemeResources. So these are by default, we've got this ThemeResource ApplicationPageBackgroundThemeBrush. We've got this border, and that's referring to this background, and we've got this static color that's in there. These things should really be maintained in a ResourceDictionary, and have us bind a property to that value in the ResourceDictionary.
So I've created a ResourceDictionary called AppStyles, and I've created a couple of solid color brushes here, one is AppMainAccentBrush. The other thing that I've done here is I've created a couple of resources that are for fonts. So we've got the Segoe UI Symbol and Segoe MDL 2 Assets symbol, which is the newer symbol font for UWP. And so that now I can just always refer to these static resources here. So if I were to go back over the StartPage I could say StaticResource AppMainAccentBrush, and I could do that everywhere.
Then if I change the accent brush in the resource it'll change it here, and we do the same thing for fonts. So if I go down here where we have FontFamily, I could say StaticResource SegoeModernSymbolFont. So I can do this everywhere and bind to those resources. And the way that I reference that AppStyles is in my App.xml, I just say, hey, load up this ResourceDictionary. And then everything that's in here will apply, so this includes style.
I've got this AppCleanButtonStyle that I've applied, and really we can just fill this up with whatever we want. So in general, these are going to be StaticResources, these will be ThemeResources. In fact, now that I say that I want to go back and make sure that I change that to ThemeResources. So it isn't going to affect things technically, but for maintainability, and just best practice and correctness I'm going to change that to ThemeResource. So it's going to go ahead and use that instead. So I am using these types of bindings all over the place.
So really what you'd want to do is just say, am I using that MDL font anywhere else? And I can just say ThemeResource, notice how it knows it's a font. It's only pulling up those font resources, and I can replace that everywhere. I could just do a global find and replace if I wanted to, but we'll just do that to demonstrate that here. And we've got the same thing there. So that way using my example of the weather font, we can just change out where this font points to and it's going to apply everywhere.
So that's pretty cool with binding. But two other elements of binding that are critical to put this whole thing into a nice clean picture are the idea of commands and converters. So let's go back over to the slide deck and see what we can take a look at related to those.
Note: This course was created by Wintellect. We are pleased to host this training in our library.