C#/WPF Building Custom Controls

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] [Music] okay testing testing we are live excellent there we go hey welcome back everyone uh we are going to and once again i left my stream running in the background and so i am hearing myself gotta shut myself down okay uh so i got some requests since last week um for some various stream ideas so i was gonna go through and um attack one of them which was all around building out custom wpf controls from scratch you code too late yes yes i do um oh that's dan hi dan you are up very late i know what time zone you're in uh so i was gonna look at building out a custom wpf control from scratch just to uh kind of walk through that process uh and see where that goes i figured i'd pick on something kind of like a rating control like a star rating you know one through five um but unlike most rating controls we'll let it go to zero because sometimes you really do want to down vote and give something zero stars so i figured we'd go through and build that up um as always if people have uh questions comments you're right dan you still can't spell um topics you'd like to see or feel free to either uh hit me up on twitter or if you want to leave a comment on one of the videos that'd be great so with that said let's get going um the code for this i'm planning on just using my existing project for the material design samples it it's labeled as a material design sample project but uh with a bunch of examples but in general i i've sort of branched out a little bit and it just contains um a bunch of wpfe related stuff mostly things that are kind of roughly related to this because it's kind of hard to figure out where you where do you draw a line so it's gonna this is gonna be a wpf related thing so step one file new project because that is where we are going to start uh let's see wpf app we'll do net core doesn't make a difference in this case we will do a custom control and i'm going to bury this underneath uh styling and custom controls yeah go that'll work so it's worth noting within wpf you've sort of got multiple tiers of where you can go through and do uh sort of custom ui so the the the top level option for you is typically just building out a user control and those are probably by far the simplest so if you're unsure what to use start there because that will probably put you in the right place the other ones tend to be around data templates so for example this is usually what you end up using if you've got a list of items or a collection of things and you just want to change the way a particular object looks and then finally you get all the way down sort of the base level stuff with custom controls and so sort of the the overarching concept that's um worth understanding is that wpf controls are lookless that is they have the view of how the controls look and the behavior of the controls are two separate things uh way back in ye olden days in windows forms or win forms uh controls and how they looked were very tightly coupled together you had a button it clicked it looked this particular way and heaven forbid you ever want to put an icon inside of that button because now you've got to do custom drawing in order to make it work right with wpf they're like we're seeing people do a whole bunch of custom rendering stuff just because they want to change the look of the button not because they want to change the functionality of the button and so that's when they separated apart the idea of how a control looks from how a control works so in general whenever you're going to look at building out a custom control the first thing you should ask yourself is is there an existing control that behaves the way i want right so if it's something that i'm going to click on or punch that's a button right if it's something i'm going to type in um that's a text box those types of things am i going to show some sort of progress that would be a progress bar um so for our ratings control we could start with a progress bar and and work from there because we would then have things like current value minimum maximum that kind of thing right so we could we could start there i'm going to start from a little bit more base level control just so that we can see building up on top of everything but in general if there's a control that already has the behavior you want and you just want it to look differently that's where you should start okay so let's start with this idea so we are going to go add new thing uh let's see new item reading uh let's just call it a star rating yeah it seems good enough naming's hard as is always the case uh so we will go wow i am just realizing my font is teeny weeny that hopefully is a little bit better uh okay so public class and i think what we're gonna do is we're gonna pick on something fairly fundamental maybe framework element we'll we'll start here we may end up changing this if i realize we need we need more but um in sort of the object hierarchy of wpf land framework element is pretty base level of a control um so we're just going to go there you get kind of basic things like um with height very very basic little stuff we might go up to like control or something higher if we need it okay so done i've made a control but it's pretty boring uh let's go let's go and actually use it now so we're just gonna drop it in here so in general you would probably separate your control into its own project uh but in the interest of simplicity let me just change my startup project before i hit f5 and get confused uh let's see star rating right and we're just gonna slap that guy in the middle uh vertical alignment center horizontal alignment center all right so we'll just slap that guy in the middle and i believe this guy's going to want namespaced so compile and hopefully the thing stops whining at me it should be hopefully happy and look at that we've built a custom control just doesn't do anything or look anywhere close to what we want okay uh there we go it compiles first unit test passed okay so let's take uh let's take a look so first thing we might probably want is some sort of rating um and this is sort of where we might make a first choice one could argue that we might want to do like an enum to limit it to values one through five i think in this case i'm just going to use an it and whack the user upside the head if they pick a bad value right so let's see attached come on what is it oh i'm sorry d come on come on where is the oh don't tell me you got rid of my where's my where's my did you get rid of my snippets show only keywords where is the only show only snippets i want well that's dumb that's dumb it apparently has dropped some snippets that i used to have so uh rather than thinking too hard man all my stuff is tiny i'm just gonna go steal from another project real quick because i don't want to think too hard on this syntax because that requires like thinking and typing so we're going to drop this in and then we'll clean it up so uh for controls uh you don't create standard properties or like wpf auto properties like what you normally would uh to do to support the data binding framework um properties need to have a lot more information associated with them so in this case the standard way to add a property to a custom control is just to uh create a static field by convention your last name is property on this so we're going to just call it value because reasons uh and this guy will become value to match but your static field is suffixed with the word property and then you create a standard c-sharp property whose getter and setter reference this guy so we are going to do a couple quick changes first of all we're going to change the type of this guy and then we're going to change the property that's being referenced we'll just do that we'll do this so this is what your c sharp property ends up looking like so just a standard property the getters uh call get value and set value these are base level uh methods that come on dependency object which is the foundational level that um all ui elements build on top of right this is i think the only thing below dependency object is probably object it's either one off object or very close to it but these methods actually reach into the the system that gets used for the bindings to be able to do this so for example if something needs to set one of these properties and then unset it there has to be something keeping track of that information to know what unset it means right and so we will then change this guy so your dependency property is effectively registering up this property as something that can be used right so uh several parameters the name of your property here the type that it is the owner type so this is who's actually where i guess the cleanest way to say this is this is uh the class that is declaring the property this is also for uh if you do attach properties where that comes into play as well so and i guess we could do default event that's fine and then finally the property metadata so in general you just need property metadata there is also a derived class from this called framework property metadata so if you have a property that you want to use to say trigger a layout pass or trigger out a new rendering pass that's when you would switch and use framework property metadata because its constructor has an overload that lets you set those flags we may get to that we probably won't need it uh so we're just gonna do this so here let's come down here and let's do let's make this let's make this a little more interesting so we can interact with this guy since we currently don't have any interactions uh let's see button content uh plus and how about button content i guess this should be backwards right minus on the left plus on the front i think what we're going to do is just stack panel orientation horizontal so what we're going to do here is just give ourselves a couple buttons that we can use to increment and decrement the value on our star rating control that way there's a simple uh simple way of uh interacting with it let's see button click that sounds great button click one so we might we might consider better options but apparently it's not going to let me rename that but now now that i save the file only namespace prefixes can be renamed well doesn't that just bite how about increment decrement right and then we got to give this guy a name so we can reference him in our code behind so name uh rating control i um i very intentionally decided to pick a name that didn't match the type name just so that it's clear what is the the name of the control versus the local field reference so radian control dot value plus plus [Music] rating control dot value minus minus okay okay so all that work and we still have a control that when we look at this i think it'll let me do this renders as absolutely nothing which is not really helpful so proof is in the pudding let's just start our value at three right so regardless we've got a value we can set it um but we can't really do anything with it because there is no uh ui associated with it so again the behavior that we have is a simple control with a value property on it but we don't have essentially any interaction or any gui associated with it so the next thing that we need to do is we actually need to create some sort of template and look in style for this so we're going to come up here and add a new resource dictionary uh let's see reading control let's dial okay and so let's go through and we are going to create a new stop it don't help me visual studio okay so we are going to create a new style and the target type of this style will be the let's see x type uh local uh rating oh no star rating gotta keep gotta keep my stuff right and i think we'll just go ahead and give this guy a key uh star i don't know text star rating alright so we'll give this guy a key here and then the most important thing for us to go through instead on here uh so let's see uh setter property is going to be the template property because without this we don't have any ui to go with it and you'll note that i went through and i explicitly used element syntax for these because you can't realistically inline this so and then we will do a control template and this will be a target type of local star rating okay if this level of nesting starts to get to be a pain actually we might just preemptively do it we're just going to preemptively pull it out and move this guy into its own own little thing so that i don't have to keep doing this so how about uh text star rating template right and then we will just inline this guy right here value static resource text star rating template boom okay so let's go through and uh next we need to actually reference that um that resource dictionary so right now this resource dictionary is just kind of hanging out he's not doing a whole lot he's got he's got some information in him um and actually let's let's put something in it just so just so we can see it right text gets template goes here right that should be as about a large and ugly of a visual to do as we can possibly get to are you unhappy about something template does not recognized or not accessible mike oh do i need to go up a level in my hierarchy we'll see this this mayor we'll see if this guy squawks at me uh if so we'll just we'll just bump up our base class oh we've got all kinds of errors because one of us increment decided to rename the methods decrement see there's there's uh recompile before we get too carried away value is not recognized they're not accessible i'm sorry what you compiled a moment ago i'm guessing that is property template what did i do wrong here uh let's just go and look we might need to check and see i might need to bump up the base class that's on this do you not have a template let's go let's just work our way up the food chain real quick uh you are less troll oh hush now hush now there we go we'll just go up to a control there now you should be happier i assume there you go so if there are properties that you need in the visual or in the ui hierarchy tree go up and down accordingly okay so we've got the style we've got the content template they're being referenced we're good uh but we've got a resource dictionary that is not being included anywhere so on our window i'll back you up just a little bit because it'd be nice to see something we will add it in window resources let's see resource dictionary so this is certainly one of the areas that gets very long and verbose and it can be a bit of a pain every time you want to go through and merge a dictionary effectively add a reference to another resource dictionary so because you end up having to spell out everything uh resource dictionary source gonna be happy uh let's see rating control style.xml okay and now what we can do is we can come down here and we can add in our style so static resource uh text already and if we rebuild this guy i assume that little red square should get all hip and cool and not work at runtime that just doesn't sound fair uh what is your problem tech star rating template oh order of operations matters now if we come back over here it is now resolving our template and applying it now there's a few things that we probably want to do here before we get too carried away with building out the template so step one in general i usually try to make sure that all of my things or all of my styles have keys on them however you don't want to have to go through and specify that this is the default style on every single one of those controls because if we omit this style right the entire template goes away which is not useful so what you want to do is you want to have a default template that that ends up getting referenced right so what you do is we will create a default template so style uh actually here's what i'm gonna do i'm just gonna grab this line right here boom right so to make it so to make a default one you simply omit the x key so the key for a style will either be the explicitly specified key that you've entered or it will be an implicit style and it will be based around the target type so when in doubt if it can't find uh or if it sees a control of type star rating it will end up using this uh style so if we omit this our template goes away and the reason is because these two styles there's no relationship so we could go through and we could we could add this setter in to our new default style but a better solution simply to grab the based on and go static resource text star rating right hold that guy through and now we're back to resolving it and you'll note i no longer have to specify my style down here so the implicit styles are a great way to go through and actually apply your base styles to everything it's also the way uh when we're doing custom control libraries how most of these get split out so a practical example so inside of the material design library you'll note that as part of the getting started inside of the main demo on the app.xaml one of the things that you have to reference so one of them is the bundled theme this is just around getting the right colors but the other very important line to add is this guy right here so this is referencing a resource dictionary called dot materialdesignthemes.defaults.xaml if we go look at that resource dictionary come down here themes defaults this resource dictionary here uh one has a reference to a whole bunch of other dictionaries but then its sole purpose is to go through and declare a bunch of implicit styles on all of the various wpf types so button calendar checkbox combo box anything that this library has a style for it will go through and use sorry i am wanting to turn off my ac unit so it doesn't annoy me all night so that is how uh when you reference the material design library if you aren't seeing your styles come through or hey i i added your nuget where are my styles why doesn't my app change its theme usually the first question i have is well did you reference this library because as soon as you bring this in it now has a bunch of uh implicit styles that it can use when it sees your controls to know what is the appropriate style it's also why if you go through and set your own style it's important that you also set the based on property on that style so that it can properly grab uh the right defaults for everything right because if you don't set a based on you're effectively wiping out that style and just straight up replacing it with your own you're not merging the two together okay so now we've got that we've got template goes here that's not helpful so i'm gonna see if i can get this to play nice i don't know if i'll get this perfect but i wonder if i can just put you right about like that okay so let's look at this template so for starters this is a horrible template mostly because template goes here is not useful and really what we want is we want something that reflects the value that we've set so let's go through and do something so if we do template binding and we can do value you will note that this now is going to show us the value from our control it's showing three right now because that is what we defaulted our value property to as well so let's actually launch this because it's cool kind of cool let's see except for i think i have completely stripped out where the control is located so let's let's go back i gotta fix okay this side-by-side docking is not gonna work for me that's not gonna work we gotta we gotta fix this grid up real quick so how about uh grid road definitions road definition and the default of a row definition is one star so we'll get one one share of the total height and then we'll do this uh we'll do this uh actually just gonna do this and that'll put both of those there oh that's not actually going to work okay with 100 it's not the ugliest ui i've ever built but it's pretty close okay so pile that guy and let's figure out a little confused why i'm not seeing my when my template went away oh because you are not at the right location so how about grid row one so it claims to be right there where where did my beautiful template go i'm gonna launch it because i don't trust my designer hello and this is where we debug and proof that my demos are not faked so three four okay so it's there where's my ui where's my ui okay i'm gonna check real quick because i don't know what happened we're gonna grab snoop and just take a quick peek and see what is going on inside of this window so inside of my main window and dive down until i run into my grid there's my stack and here is so here's the control as part of my visual tree now inside of it uh let's see i have a text block that has a width of zero and its text property is blank which is not really that helpful oh is it because the tight mismatch because we know it's there i am very very confused it clearly registered the value changed it's a template binding so it should go to the control template to look it up it's clearly there uh template binding let me just make sure i'm not landing on something weird come on template binding value not crazy right you can resolve you can what am i what am i missing what am i missing it should just display and show it the only thing i can come up with at the moment is that it's for some reason unhappy about um the value of it let's let's just hypothetically try something so background blue alright and that should uh let's see horizontal alignment i kind of wanted to just stretch so i don't see that let's go here wrap this guy in a grid move you up oh i don't have karnak running again fix that there we go now we see keyboard shortcuts okay so let's template blinding background sorry just testing to try and figure out where i went wrong so there it is there if i drop background it doesn't do anything right so it's worth noting the edit and continue can oftentimes get very confused when you are yeah and it's gone so i'm gonna go with this guy must be very unhappy about the type that's got to be what it is we'll circle back to this in a moment okay i'm a little surprised at that i'm trying to remember template bindings there are special rules around them and i don't always remember them exactly so let's just do well trying to decide do i spend time debugging this or not we're gonna go value here and i there's there's a few other concepts that i want to get to so um real quick just to to note something so we've got it we've got a displaying value here right and if we look at this guy in snoop because i like snoop right if i look at him here is my radiance control and then inside of this because this is the visual tree we see the grid and then the text block these two elements are coming from my controls template because a controls template defines how it renders inside the visual tree so as wpf comes along it gets down to star rating and goes okay i've got a star rating element great what do i do with that what does it actually look like and because we have a template it knows oh okay i put a grid in and then i apply the text block you'll note then if i go through and set the background color to blue that promptly gets ignored however if i look up here right i can see that my background color has clearly been set to blue the problem is is blue on the radians control by itself doesn't mean anything right the problem is is because inside of the template itself no one's using that property everyone's just ignoring the background property and it so it ends up getting dropped so in like the material design library this will often crop up uh in bug reports where people say hey i applied this property and nothing happens it's like well the most likely explanation is that nothing inside of the template is using it probably an omission in most cases and so the fix then is to go through on the template and go okay where should background be applied right should the background be applied let's see template binding background right should the template be applied to the parent grid perhaps it should only be applied to the individual text block which unfortunately it's gonna leave both of those there and you gotta restart changing templates edit continue not all not always hip to the changes it tries it really oh and the text block itself is apparently filling this so we could go through here and say something like uh vertical alignment center all right so now if we look at it even though the control itself is filling the entire row the text block is only uh filling up the um the center area so the question then with all of these is where should all of these properties go so the key thing here to note is going through and building a control template can actually be quite involved and most of the time when people go to build a control template my recommendation is don't if there's any way you can get around not building your own control template do that right because even though you have complete control there's no halfway street you either take complete control of the template and at that point you are clear to do whatever the heck you want right you can make it look and act however we want or if you can find properties and things just to tweak on it that are used within the template that's by far better because now you don't have to redo everything if you do have to redo a template it's by far better to start with an existing template for the control start with its default template and then tweak it accordingly right that'll usually get you close enough and more importantly it'll save you the headache of trying to figure out where should all of my default properties get applied at right unfortunately for us we don't really have a great explanation for how this is going to go so we're going to build this out here right so i think what we're going to do is let's make this radiance control trying to think if i want to do this with or without the material design library in here we might just start without it i'm thinking i'm thinking what we'll do is we'll just rev it and um build it up and have the thing go across i think maybe yeah sure let's go for it okay so let's do a maybe a border control right and i think what we're going to do is we're just going to have the uh because i think my intent is going to have the value b to go up to like five right that's kind of normal um and i think what we'll do is we'll just have the border um slowly fill up the space going across right uh this is no longer really accurate for text but whatever uh and have this go across actually before we do that i kind of want to i kind of want to debug this just a hair further because i want i want to conf no no that button i want to confirm that the because the template binding on value should be working right um and i have a hunch the issue is the type and i think i want to just do a quick value converter yeah i think i i'm a little surprised it doesn't it's not handling this but something in the back of my mind is telling me that without the the converter it's not going to to do it so let's just really quickly slap this guy together um public class string converter value converter and we're just going to put you in your own file auto code do victory implement the interface write all the code return value to string just in the event i know we're not going to hit a null but we're just going to make absolutely certain so do to do x key uh and you don't have to have the uh your other resources live at the root of your resource dictionary if you kind of want to hide them away the control template also has its own resources you can leverage let's go here static resource string converter just want to confirm that the type is the problem here because it really should be displaying that value hey look at that that's better break points out of the way and then if i remember to do zoom it uh yeah thanks and look at that there's a four click click click click click okay it was the type mismatch that was the problem yay okay so we have a control we have some stuff in here there's some there's some issues right there's a background there's the placement of this so for example right here right we did vertical alignment center but we probably don't want that right we probably want this to be a template binding of the horizontal content alignment right actually if you do the horizontal one in the vertical property that will probably confuse me later future kevin will not be happy uh so let's go with horizontal right and then this becomes horizontal right and if we look back at our main window now to to achieve the what we want nope nope don't do that we can now do horizontal content alignment center right and then vertical content alignment gets center and now our text block will end up in the very center of our control so by going through and figuring out which properties we care about and then appropriately applying them to each element inside of your template this is basically the beginnings of building out custom controls and templating and then from here the sky's the limit you now are in control of the visual tree the same way you would up here but more importantly you've now encapsulated all of the logic inside of here so let's go through and trying to think what would be what would be more interesting i'm thinking let's do maybe some visual state stuff yeah me so i think what i might do is leave it as an uh exercise to the viewer to figure out making this template more interesting yeah i think we're going to leave it as an exercise to the viewer to make this template more interesting and instead we'll look at some states and templating options i think that's probably more interesting so i'm going to go back to the material design library and i'm realizing i keep referencing this for anyone who is unfamiliar with this i'll just drop a link to the chat for anyone who cares that's another library i work on a lot um well let's take a quick look because there's a bunch of custom controls in here um what would be a good one to to look at for let's do dialogue host i think yeah yeah this guy here so when building out custom controls you'll probably come across various things like this so specifically these attributes along the top um called templated part right and template visual state so because we have this separation between how a control looks and how control works there's still um i guess some healthy tension there because how a control looks quite often when you interact with the control your code in for your control wants to do something to the ui right i want to manipulate something so for example let's go uh i don't know we're going to pick on this right so let's let's pretend inside of our star rating control that maybe on mouse down mouse double click we'll do mouse double click right so we'll we'll pretend that on mouse double doubleclick we want to do something right and we don't necessarily want to we don't want to just manipulate the value property right because we could do this from outside the control we want when this happens we want to ultimately change something right something's happened in our control and i want to manipulate one of the ui elements the problem being is right here i have no way of actually knowing what my template is we know that there's only one template but this control here there could be infinite number of templates somebody could have rolled their own custom template so the question now is how can i get access to the items in the template and be reasonably assured of what's there so i'm going to drop that guy in so what we're going to do is we are going to create a couple of constants here so public const string and let's go with um i can what are we gonna call this guy um content wrapper sure and by convention inside of wpf templates when something's going to be a required element of the template it's usually prefixed with part underscore uh content right so when you see one of these guys what this means is the control needs to have this thing exist so what we're going to say is this control absolutely must have an element in its template called part content and it has to be of type grid right because down here i don't know we're going to do something with it i guess because reasons reasons are good right uh so uh let's just do uh private bool is big not bug big bug is something different right and then you know it is big gets not is big and then we'll do we'll do some more stuff so change i don't know uh something something important top tier naming thank you kenny i'm glad somebody noticed my hello world cup for those who care if you're gonna drink mountain dew you should do it from a c sharp goblet as is appropriate okay so the thing is we need to get access to this so if in the base level control there is a method called apply template so or on applied template this guy here and so inside of or after this method has been called um the template has been appropriately applied to our control and we can now query items back out right so what you end up doing is uh find element by name all right and then we grab and this is why we declare a constant right because we need the same name up here um as in here right var grid uh and let's we'll just cash this guy in a private uh grid content rapper right just hold on to that guy content wrapper uh that ain't gonna work that's a duplicate name you should not reuse the same name for two different things something about top tier naming and then this guy here we will cast to a grid okay so now that we have access to the grid we will do content wrapper i don't know content wrapper grid trying to think what would be fun uh private uh how about scale transform scale all right so we're just gonna we're gonna actually just do this uh let's see content wrapper grid uh render render transform gets scale or yeah scale gets new scale transform and does your constructor have uh i assume default isn't going to default to zero but i don't know that for a fact just confirm that this is going to default to something sane like one and one that doesn't leave a lot of confidence so we're just gonna we're gonna just make sure it scales everything one and one so uh if is big uh let's see scale uh scale x gets two scale y gets two else right and we'll just we'll just when you double click on the thing it'll let will let you make the rating control big right so you'll know this isn't just a this is this is something that's actually going to change and stay fixed right we're not going to um we're not going to be changing based upon states of the control we'll get to that in a moment okay so we got this we got this and now what we need to do is we actually so let's let's fire this up and i want to grab it here i guess let's grab it right so we're going to go hunting for this guy we haven't declared one so this is null so this goes boom right so what needs to happen then and we could throw an exception but in general this isn't uh a huge deal in general what we're saying is this guy here needs to have an x name of part content so when you see this all caps part underscore what that really means is this is this is an element to the template uh and the template is expecting to find it right and again the the all caps part underscore is um convention you can technically use any string you want but use this trust me um if you aren't going to use it as part of the controls behavior itself don't use part underscore because people will see this and expect it okay so let's head on down and oh now it should work all right so let's uh did the object reference except for when i said it now will work what i mean is it clearly now will not work did i just double check this i didn't do anything stupid did i beyond the obvious oh get templated child i did i called the wrong method you should call the right method and then see how it works i do wish you would stop doing it over here okay so we've got this right double click and then clearly it works because it went away completely i'm wondering if it's oh the render transform is on the grid so the grid is twice the size so it's going to move off the bottom oh that was not a good idea that was not a good idea so the grid is changing size not the text um let's make it obvious but not big enough that it's going to push off the bottom let's try this again there we go so the grid again the the render transform is being applied to the grid so the grid's width and height are now being multiplied and increased by 20 percent um and so it's effectively pulling and scaling down so but we now have we now have a custom user control with a templated part that we can then go and manipulate so anything that is important for your control's behavior right not its look its behavior should be declared as template parts okay now what about doing things for um visual states right so in most controls um and i'll pick on button again right there are there are various states that the control can be in you can think of for a button there's mouse over not mouseover there's enabled there's a stable there is pressed and unpressed right and so your control might move through several states um and we don't want to have to manage and manipulate state like this in our code behind especially since the how a state looks should be decoupled from the logic here but ultimately the the code here determines when you move into a particular state right so um you want this code to control what state you're moving into but you want your controls template to define how that state looks so how does that play out so that comes into play with um the visual state manager so we're gonna i'm once again gonna steal from uh the material design project because huzzah so there are uh in this case this guy is defining a couple visual states for the pop-up for the pop-up open pop-up closed right so let's go and do something similar for r so i think what we're going to do is define some uh visual states [Music] and we will once again declare our own constant so public con string what will be our state um scary red scary red i guess right uh happy green right and actually i guess what we can do is we can we can abuse this right just to make sure that these things stay in sync name of happy green uh points for spelling not sure if you're around dan but see i can't spell either uh let's see uh turns out spelling's hard so let's go with pop-up states rating state sounds good uh and we will do uh scary red red state name right rename that so i actually get refactoring right um i can't use name of if i do that don't think name of is worth it so we're just gonna we'll abandon the name of idea it was nice it was a thought we will see where these get used here in a moment happy green okay so rating states and we've got scary red state name and then we will do [Music] happy green okay and it's worth noting that within a state group states are exclusive right so if two states are met so in our case we're saying that our rating control cannot be in the scary red and the happy green state at the same time um if you want two states to be able to exist simultaneously they need to be in separate groups okay so within a group they're mutually exclusive if you want them to uh not be mutually exclusive put them in their own groups okay so now what we need to do is we need to define uh when we move into this state uh let's see let's say hypothetically that if the value i don't know exceeds uh if the value exceeds like four will be scary red and less than two will be happy green so on our property metadata so you can you can effectively do this anywhere but in our case what we're saying is we want to do it when the property changes right so with the dependency property uh yeah change that for me please and change that for me uh with the dependency properties uh it's very tempting to do property state changes right inside of this setter resist temptation to do that this setter will not get called in all cases that this property gets changed under the hood because this is registered up effectively in like a dictionary inside of the base class various things will manipulate this property and never go through these getter and setters right these two methods could be invoked on your object from outside without ever hitting your property so in general don't rely on this property being util it's getter and setters being utilized for this property to change so if you want to know when the property changes inside of the property metadata there is a callback oh that is awesome so let's see uh obj args i believe is the appropriate lambda expression all right did i mess it up what is the uh wow typing's hard there we go something a little better there and am i missing a close print for the register okay so inside of here uh var rating control will get uh actually uh grombo grumbo grumbo grumbo grumble gobble gobble we should do this right uh private static void on value changed uh and i'm trying to remember the exact signature property changed callback all right so what are you uh dependency object and dependency property change event arcs uh object let's see property changed then args d e because your type name was already long enough uh and then we will just get rid of u and do on value change there that that's cleaner a little less nasty so var rating control because we know that the only way that this guy's gonna be triggered is because this guy only lives on the radiant control so we will just uh straight up convert except for by rating control i clearly mean star rating so in an effort to not confuse myself i'm going to do that and do that and there we go okay so we'll do uh if e dot new value so on this uh event args object there are a few things there is the new value the old value and you do technically get the dependency property object itself so if you wanted to share callback handlers for some reason because they were very similar but you needed to tweak one slightly for one property you can you can get away with that um the key part is this api is a little old there are no generics i think this a i don't know if this api predates generics regardless uh new value and old value are of type objects so you do need to cast so it's int new value uh so let's see uh if new value is greater than or equal to four right we want visual state manager right go to state right and now we need to tell it what object we care about so in this case it's control right and then we need to tell it what state we want to go to so this will be scary red state right and then use transitions and i think what we're going to do in this case is say we're just going to say true always so it is worth noting that use transition that last value is a request not a command right you cannot necessarily guarantee that transitions will always be used um for example if the user has disabled animations inside of windows you often will not get your transitions it will just jump to the final visual state okay so now we've got the logic inside of our control for controlling what state we're going to go to right so we've declared that if our value exceeds 4 we're going to be in scary red state and if our value is less than 2 we'll be in happy green state now i realize as i have directly put colors in the names of these but the the look of how uh scary red and happy green look in the ui don't have to be red and green in fact if somebody wanted to be really weird they could make them be whatever they wanted okay so now what we need to do is we need to actually implement uh the visual states inside of our template so let's let's take a look at that so once again i am i'm just going to straight up start stealing stuff because so much easier so much easier so dialog host something about this uh that's a lot of stuff that's a lot of stuff that's a lot of stuff yeah we'll just take it all sure great okay so inside the root element for your template which is where this is typically located we will add in the visual state manager so you'll note this is just an added thing that goes in and now the first thing we need is our group name so in this case rating states right so we need to tell it what group we're talking about and then i think for now what i'm going to do is i'm i think i'm just going to comment out all the transitions we're going to do this without transitions first and then we'll come back and add it yeah i think we'll do that to do okay and so we need scary red and happy green as our two states so visual state and is just gonna get rid of all that get rid all that make it we're gonna make it nice and pretty oop right okay so we've got our state names uh and now the question is what should scary red do that's a question one to which i do not necessarily know the answer to uh so uh let's think for a moment what would be fun to do if we go into the scary red state how about we manipulate this guy a little bit i think yeah let's do this one uh let's do x name text content right i just need a name so that i have a way of referencing it and then inside of each of these visual states we get storyboards storyboards that we can then use to go and target stuff right so let's do say a double animation uh yeah we'll just do this right so we haven't uh manipulated this guy too much yet so let's grab him boom so we'll grab that target property let's pick one size i guess right and so what we will say is at key times zero uh uh let's just do double animation all right let's let's not think too hard uh because i think double animation has some simple two properties uh we will animate to i guess font size 24 because yes right and then down here we will do the same thing but if we go into the happy green state we're gonna get small right we're gonna go to like a six that's it let's see it work or based upon how my day is going let's see it fail miserably and then we can all laugh at kevin um real quick i am going to go and change that blue is making things hard to see let's pick something lighter alice that is like white how about light blue there that's a little more readable yeah see that's pretty okay so if we go increment oh did you see that animate you'll note we didn't change states yet right because nothing has told us to change from that state even though we just went down nothing has actually told it leave the state of scary red so it is still in scary red state and now it's in happy green state because we got to the 2 and it's going to stay there until we hit the 4. done ok so but we do have this sort of sort of weird issue right of right now it's just doing the double animations and you can see it's using transitions so sometimes what you want is you want to just jump to the state uh rather than animating to it and you want your control to be able to communicate that so let's let's get transitions in here too right so uh let's see duration zero all right i believe that'll kick it off duration zero all right so i believe that yeah so now it's just going to jump there right but we want we want to go in and add some transitions so let's let's make this thing animate so let's do some transitions uh that's a lot of stuff okay more importantly i don't really care about like all of this no delete thank you [Music] boom okay and then if we go up here let's grab up here to do like that right okay so first thing is we need to declare i wonder if we should give ourselves a normal state yeah let's give ourselves a normal state just so that we have a uh so scary red happy green uh rating state um so somewhere be somewhere in the middle here no i can type some days not often uh let's see so rather than scary red how about normal normal yeah that sounds normal right cannot be any more normal than a normal state name uh so else we need to actually communicate that's where we want to go visual state manager uh go to state control normal state name allow transitions right okay a common time where you might set this to false is if you wanted to immediately jump to a given state in our case uh we're going to basically default to where we want to be but we can do we can do this right we can let's see and just force ourselves force ourselves right into there um just in case we don't get a value change that comes through right away so all of that stuff uh and then i'm just going to add in a normal state right so normal and this guy will be at a 12 i guess let's just verify that this works you'll note that the visual state manager i don't believe is that unhappy so if you typo one of your names you'll note it doesn't care it's never gonna match but it doesn't care so there there so two three four back to three up to four okay cool we got a normal state so let's say that when we go from normal to scary red we want to do some stuff right like we might we might want to actually do like um i don't know uh let's do a uh color animation right uh let's see storyboard all right so let's hypothetically say that we want storyboard to go through and uh not font size how about foreground right uh and we want the color to be uh let's see two red right we're gonna actually make going to scary red scary so let's fire this up and boop uh oh and that be a problem that be a problem so the the issue being that this guy is wanting a brush and that was not a brush that i set um because the foreground property is of type brush it is not of type color so let's foreground dot color and if we come down here and go foreground uh i don't know black right you'd probably use a theme brush instead of a hard-coded one but in lieu of that we're just gonna go here and you can see if you so if you watch carefully very carefully it gets big and then you see the color animate to red and then disappear so what's happening here is on the state transition this guy fires right along with this guy right but this one is going to hold its position at the end of it the transition is just for transitioning right so it depends on what you're looking to have happen but this can be useful so let's let's take this and actually what we're going to say is anytime we go to scary red whether it's from normal or not right right now we can't get from green to red right we can't go from happy green to scary red because our buttons only let us increment our decrement but that's just a an artifact of the way we implemented this there's no reason we couldn't have a button that says always go immediately to scary red state like set it to 10 everything's on fire notify everyone right so we're going to set this visual transition to say anytime we go to scary red i don't care where we transferred from we always want to be there right and then how about any time we go to uh was it happy green this is the problem when you pick bad names when you pick bad names it comes back to bite you also one comment i was going to make i kind of did it quickly the fact that this foreground is now a solid color brush so inside of the xaml there are type converters uh for solid color brush so if you type in a hex value or a well-known color name there is a type converter that will then set this property to a solid color brush with that as its color so just be aware i'm abusing the fact that i know that solid color brush has a color property on it um and this was my cheater way of setting a um a solid color brush be aware if somebody set say a linear gradient brush or radio brush this is going to fall apart right but i set it inside of my template i'm in control of it no one else should be able to touch it okay here we go so plus to the red down down and i just realized it's basically impossible to see that green with how how bloody tiny we made that so let's just fix that real quick so this guy become happy green is now really important to us so now we can see it animate and actually rather than green let's do lime lime is brighter it's prettier so there we go fuchsia thank you kenny okay how does one spell fuchsia fuchsia i can spell there we go there that's right i know all the horrific colors that's right fuchsia and lime what could be better what could be better right um let's see here so i think what i might so let's do this so we're gonna explicitly set a duration on this right so zero zero zero we're gonna say it's going to take half a second right so i'm i'm kind of doing this intentionally and then this guy here um well i guess it's it's not going to make a difference it won't make a difference okay so let's see have i covered everything i was going to i think i have visual state transition managers control templates styles both implicit and explicit explicit meaning has a key creating these guys here we could go to the effort of trying to get fancier on this template bindings and then visual state manager and handling it inside of this yeah so next step let's commit this guy let's see do to do uh adding an example of a custom control and push and now for all interested parties that will exist in underneath styling underneath custom control for all interested people there it is cool um anyone have questions i think that's probably all i was uh planning on covering on this um there's a couple other things that have been floated for additional topics as well um i might do a coding performance stream on doing performance analysis and fixing the john from the system command line library was looking to do that and thought it might make for something interesting if people are interested in uh learning that um i also had another request for using expression trees um for basically doing like dynamic event registration so how to build up expression trees to go through and do some of that stuff and i might couple that a little bit with um fun uses of dynamic can we cover all the n squared search algorithms oh kenny kenny kenny kenny kenny uh yes we can cover them in as far as you shouldn't do it because performance sucks um yeah no problem so yeah like i said if anybody has ideas of stuff that you're interested in seeing i'm happy to do that my go-to answers on what i may end up working with when i'm out of ideas are i keep going back to my testing library as well as the material design stuff and the material design stuff is probably going to get kicked live shortly i've been very focused on trying to make sure it's it has better testing and that has significantly slowed down my review of issues and getter chat and pull requests and so for people following for that uh i vote for expression trees one of these days i heard kenny did you just volunteer to pair on expression trees is that is that was that the implication there yeah i can see the expression trees being being rather fun they're they're one of those areas that you use it and you usually write it once and it's really cool and then you just wrap it up in a nice little helper method or similar and store it for later so i've got a couple fun ones that i've done for stuff like that um that can be kind of cool to do so i may uh sit quietly and absorb everything as quickly as possible no no no not sit quietly slow me down ask questions don't let me just talk to myself because sometimes i go fast and having somebody to ask questions and slow me down is usually a good thing so i think that's that's probably where i'm going to leave things for tonight it's been a long week actually before i go just in the off chance anyone has ideas i will show you the problem that i have been struggling with for the last couple days and it is it is frustrating me frustrating me frustrating me frustrating me okay so uh i think last week's stream i did my xaml test library so this if people haven't seen it this is a library that is being purpose built um to effectively be able to write tests so all of those styles and templates like what we just showed there are um great except for there is no reasonable way to write unit tests or ui tests against them right that is kind of a pain in the neck so i have built this library in an effort to address that because i want to write quote unquote unit tests against my templates because i want to be able to make some assertions on how they work and how they behave specifically for the material design library because most of that library's code is inside of the xaml and so i don't want it to go untested so i don't think this is a general purpose ui testing library um but it is purpose-built for people who are building custom controls and templates so that they can write tests against those so that's the intent um and to do that this library needs to have the ability to effectively do a lot of stuff like what appium does or selenium for people who are familiar with the website of things and so i've i've built it out so these are my tests right i've got they are very simple they are not hard they create a window and they shove some xaml content into that window right so if people are interested in how this library works go watch my old streams tagged with xaml test um i go through kind of building it out and i've been working on this but i've got these two tests right so this test creates a text box sets the keyboard focus sends some text right so it does little typey typey typey simulation right and then asserts that after it sent this input that the text of my text box is correct right that's that's it this test passes test two this guy creates a text box now there is one teeny weeny little thing about it it has this accepts return property set to true which means it's a multi-line text box that you can hit enter to go to the next line in that's it right i did not realize how hard this was going to be to test right it's got some positioning stuff and some sizing stuff just because there's a bug with my screenshot code this test fails right fails and it fails not due to this not due to this it fails on this line here which is remarkably similar to this line here but it only fails on my pipeline it does not fail locally so proof is in the pudding right run test so fire these up boom boom so you'll note how it's actually running the test is it's actually firing up a wpf window and evaluating this so it gets the full rendering stack but it does it rather fast these two tests both pass okay insert frustration here uh let me just go back and make sure i'm on the latest you'll note i've got a string of failed builds this is exciting ton of warnings um so as part of this thing's pipeline um there's a there's a clever trick happening with this little recorder class if anything here throws an exception such as the assert call before hitting the success it'll automatically save a screenshot at the end of the test otherwise you can manually call safe screenshots so this safe screenshot is here just to ensure that i always get one for this test this one's failing so i'm so i'm guaranteed to get one so on my pipeline i'm going to close that because i keep opening the wrong window so on my pipeline one of the steps after the test fail is it always uploads any screenshots that it took so just so that i can always get them so we download them as you can see test images we're getting to be a large number and then it automatically does some fancy naming and whatnot so the key part here is it does tell me which test each of these go to so we will note on send text input text is changed right this guy here you will note the screenshot that it took shows the text box with the text entered okay that's key so the test passed as one would expect this one here shows the text box with no text entered and i haven't the foggiest idea why because again it passes locally and works just fine something is different and i have no idea what it is and it's frustrating me so if anyone solves it or wants to play with it you are welcome to do so prs will automatically run the test for you on my pipeline the link to the repo is there and the branch that i'm playing on is called key up because initially it was fighting with the fact that the enter key apparently only sends key up and not key down which confused me for a very long time so with all of that said this has been the the bug that i've been banging my head against and i am i'm at the point where i'm just gonna start trying random stuff until it works um or at least until i get a hint as to what is causing the difference the one other somewhat interesting bit i'll show this too because i thought this was i felt rather clever with myself uh that's not the right folder let's try this again uh let's just go back if i go to like say test images one to do that doesn't show it go back one of these will ha will happily show it yeah so one of my first failure cases i did notice that when i screenshotted the window inside of the build agent apparently this is what's running on it which that was fun that was exciting i was like oh um that's a bit of a problem if there's a window over the top of my window that's sitting always on top i kind of need to be able to clicky clicky on my stuff and uh that that's gonna be an issue so there was this this was problem one to address uh and the the hacky solution that i that i came up with is i execute a little bit of power shell to minimize all windows before running my tests i thought this was clever it's probably not but i thought it was fun it seemed like a clever trick i there there is a couple weird issues that i'm that i'm going to that i know i'm going to run into uh most notably the resolution on those build agents is tiny and i am not entirely sure how to set the resolution on a build agent that is run that is running github actions that's not a self-hosted agent so the and i'm not sure if it's possible or if i'm supposed to be able to do it so i think i probably can but i might need a little magic utility to do it but as i've recently discovered today changing screen resolution is kind of hard so i might need to do that the other thing is the default window size that i'm using is actually smaller or i'm sure i'm sorry the resolution on my build agent is smaller than the default window size being used by most of my tests so you'll note that many of my tests the windows off off screen or you can see the bottom here is hiding behind the taskbar so that will be that problem will rear its head if i ever try to do like a clickable element that is within or that is outside of the effective bounds where i can act so okay that was a long-winded way of saying i think i'm tired i think i might be done for the night so i think that's where i'm going to leave it unless people have questions again if uh people have ideas for stream ideas or topics you uh would like to watch me ramble on about for a little bit um like i said either drop me a comment um on one of the videos or feel free to hit me up on twitter and shoot me messages um and let me know what you think but i think for now i'm gonna say happy coding good night kenny i'll see you in the morning thanks for coming everyone you
Info
Channel: Kevin Bost
Views: 5,605
Rating: undefined out of 5
Keywords: programming, material desgin, C#, WPF, XAML
Id: T4U9ce8J93Q
Channel Id: undefined
Length: 92min 20sec (5540 seconds)
Published: Fri Aug 28 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.