Custom Controls in WPF (3) CustomControl (bottom)
Zhou yinhui
1. coupling between the UI part of the control and the logic part.
This is a problem that is easy to ignore but very important. The reason why we use CustomControl instead of UserControl is that we want our controls to be the same as those built in WPF, its UI can be easily customized by other users or changed in the future. that is to say, the visual tree cannot be entangled with the background logic, because the elements in the visual tree may be completely changed by your control users. for example, if your control has a Button in the visual tree and you have processed some controls in the Click Event of the Button, it is very likely that your control creation has failed, this Button may be deleted when you redefine the control Template.
Before discussing the solution, we should note that the logic of the control and the distinction between the role of the Style and Template should be noted. if the background logic is not in the background, the background logic is not required. If the background logic is not in the background, the interface cannot be managed, or it is very troublesome. A simple example is: for example, if you want to move the mouse over your control, the control is slightly larger. When the mouse leaves the control, the control size is restored (or other brilliant effects ), the MouseEnter and MouseLeave events added to the background logic of the control are processed to achieve this effect. at this time, your background logic is too wide, because this effect is a Style thing, you can put it in the default Style of the control (in Generic. in xaml, you can refer to custom controls (3) CustomControl (on) in WPF to provide control users, rather than adding them to the background logic. this not only adds coupling, but it also seems to the user that it is somewhat "rape public opinion", because he cannot overwrite the control effect that you think is pretty through a custom Style.
Although WPF has well isolated the UI from the background logic so that the UI Designer can better communicate with us and assist in division of labor, this does not mean that, WPF can completely isolate the UI from the background without affecting each other. in fact, when writing background logic, we often need to use some elements in the control UI tree to complete it, for example, when writing a ProgressBar, we need to know the length or height of an element in the visual tree that represents "Total, to determine the length or height of another element in the visual tree that represents "current amount" based on the current Value of ProgressBar. another case is that we have written a good logic in the background, but we need a UI element in the visual tree to explicitly call it. For example, we have written LineDown () in the ScrollBar control () method, but this method needs to be called when you click an element in the control visual tree that indicates "scroll down a row" (such as a downward arrow.
WPF provides two solutions: TemplatePartAttribute and Command.
1.1 TemplatePartAttribute
TemplatePart is applicable to the first case mentioned above. It is used to notify users, currently, the control must have elements of the specified type and name in the control's visual tree, otherwise, the function may be lost or the user needs to manually delete the element in the visual tree. if we are a user of a control and it specifies this property, when modifying the Template of a control, make sure that the control contains elements of the specified type and name, unless you know that you do not need the related functions or you have already handled them.
In WPF built-in controls, there are many such controls, such as ComboBox, PasswordBox, and ProgressBar.
Let's take a look at ComboxBox:
[TemplatePartAttribute (Name = "PART_EditableTextBox", Type = typeof (TextBox)]
[TemplatePartAttribute (Name = "PART_Popup", Type = typeof (Popup)]
[LocalizabilityAttribute (LocalizationCategory. ComboBox)]
[StyleTypedPropertyAttribute (Property = "ItemContainerStyle", StyleTargetType = typeof (ComboBoxItem)]
Public class ComboBox: Selector
We found that there are two TemplatePart attributes: "PART_EditableTextBox" of the TextBox type, and "PART_Popup" of the Popup type. The former is used to edit text, and the latter is used to pop up list items, if a user lacks these two elements when customizing the Template of the control, the corresponding functions will be lost.
Our controls can also define necessary components like ComboBox, and Override some OnApplyTemplate () methods to obtain the corresponding element: public override void OnApplyTemplate ()
{
Base. OnApplyTemplate ();
Button mybtn = base. GetTemplateChild ("PART_BTN ");
If (mybtn! = Null)
{
Mybtn. Click + = new RoutedEventHandler (mybtn_Click );
}
}
1.2 Command
This is suitable for the second case mentioned above, that is, we have written a good logic in the background, but a UI element in the visual tree needs to be explicitly called. for example, the two arrows at the top and bottom of the ScrollBar are used to flip rows up and down. We obviously cannot call the LineDown method in the mouse-Click Event of the two arrows. the correct method is to wrap the LineDown and LineUp methods in the background logic into LineDownCommand and LineUpCommand, and then bind the Command attribute of the elements in the visual tree to the corresponding Command. in this way, even if you change the up and down arrows in the visual tree to other types of elements, You can bind them with the corresponding functions through commands. for example, The XAML code of the Down Arrow of the built-in ScroolBar control of WPF is written as follows:
<RepeatButton IsEnabled = "{TemplateBinding IsMouseOver}" Style = "{StaticResource ScrollBarButton}" Grid. row = "2" Command = "{x: Static ScrollBar. lineDownCommand} "Microsoft_Windows_Themes: ScrollChrome. scrollGlyph = "DownArrow"/>
For more information about commands, see bind commands and commands in WPF (1) bind commands and commands in WPF (2)
2. It's not always a good thing
If one day the art cell burst and a very beautiful control was created, this would naturally be a good thing, but I am worried that this would be incompatible with most of the user's current operating system interfaces, after all, many people still use the "Windows classic" topic in Vista rather than "Aero ". to create a control UI that is compatible with the current topic of the user's operating system, you may need to provide several styles for the control, for example, a relatively gorgeous one for Aero themes, the other is relatively simple for Windows classical. to achieve a dynamic change with the subject of the user's operating system, you have at least two methods: (1) Listen to the system message WM_THEMECHANGE, and then switch to the control interface. (2) Place the Style corresponding to the system topic in the themes folder of the control solution, for example, put the Style corresponding to Vista Aero to themes \ Aero. normalColor. xaml, which corresponds to the blue Windows XP themes \ Luna topic. normalColor. xaml, which corresponds to the Classic themes \ Classic topic of Window. xmal: themes \ topic name. color name. in xaml, the classic topic has no color name. in this way, when the user switches the topic, our control will switch to the corresponding Style. If we do not provide the Style corresponding to the user's current topic, call themes \ Generic. xaml (that is why I say "Generic. this name is not accidental)
3. storage location of control resources
In general, in order not to undermine the encapsulation of controls, we do not place the control resources outside the control. For example, some resources are frequently used in our applications, we share these resources. We may move these resources to the APP's resource dictionary, but the resources in our controls are also removed, which will damage the encapsulation, this is not conducive to the reuse of controls to other apps. however, we often face the following problems: If I put the control resources in the resource Dictionary of the control, but this control is used in many places in our applications, this causes frequent resource replication. A typical example is that we made a poker game, and our artist made a set of beautiful poker pictures for me, 54 pictures in total. Then I created a poker control, the control instance selects and presents an image based on its current number of points and color. Finally, 54 poker control instances are generated to form a complete set of cards. if I place 54 images provided by the artist in the resource Dictionary of the control, in fact, only one of the images is used for a poker control instance, and the other 53 images are completely redundant. when 54 poker control instances are generated, 54*54 = 2916 images provided by the artist are saved. the solution is to transfer the resource to the themes \ Generic of the control. in xaml, no encapsulation is destroyed and resources are shared.
Finally, I would like to thank you for your attention to my blog. It is wonderful to share your work and learning experience here. in addition, I am sorry: "Custom Controls in WPF" this series has been dragging for too long, and it takes almost a month and a half to complete, because during this time, I did have too many things to complete. thank you very much.