In WPF, we can easily define a style in a global scope and apply it to all objects of this type. This is the so-called implicit style, for example:
<Window X: class = "wpfimplicitstyle. window1"
Xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Xmlns: x = "http://schemas.microsoft.com/winfx/2006/xaml"
Title = "window1" Height = "300" width = "300">
<Grid>
<Grid. Resources>
<! -- Set a global style for one type -->
<Style targettype = "button">
<Setter property = "background" value = "aliceblue"/>
</Style>
</Grid. Resources>
<Stackpanel>
<Button> button a </button>
<Button> button B </button>
</Stackpanel>
</GRID>
</WINDOW>
In this way, the two buttons become a light blue background.
However, there is no way to do this in Silverlight. We must manually add statements such as style = "{staticresource somestyle}" to each control that requires style settings, which is very troublesome.
Fortunately, the Silverlight toolkit provides a similar implementation called implicitstylemanager (implicit style manager, or ISM ).
This class is used to set an additional attribute (Attached Property) on a root element. Then, the visual tree of the element conforms to the style of a specific type of child element, the implicit style can be automatically applied.
Example:
<Usercontrol X: class = "implicitstyletest. Page"
Xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Xmlns: x = "http://schemas.microsoft.com/winfx/2006/xaml"
Width = "400" Height = "300"
Xmlns: theming = "CLR-namespace: Microsoft. Windows. Controls. theming; Assembly = Microsoft. Windows. Controls. theming">
<Grid X: Name = "layoutroot" background = "white">
<Grid. Resources>
<Style targettype = "button">
<Setter property = "background" value = "aliceblue"/>
</Style>
</Grid. Resources>
<! -- Set it once on the root element -->
<Stackpanel theming: implicitstylemanager. applymode = "Auto">
<Button content = "button a"> </button>
<Button content = "button B"> </button>
</Stackpanel>
</GRID>
</Usercontrol>
After running the example, you will find that the styles of both buttons are set, so that the implicit style behavior in WPF is achieved.
In this example, we can see that the applymode attribute is set to auto. In fact, it has three optional values, representing the following meanings:
1. Auto
During layout updated, ISM applies the implicit style again. In this mode, if elements are dynamically added to the visual tree in the future, they will be applied with an implicit style.
Note that layoutupdated events occur frequently, not only when you add elements. However, there is no event similar to itemaddedtotree. If the visual tree is large, ISM will spend a lot of time traversing it, which may have a certain impact on performance. But for convenience, we have to make some trade-offs and sacrifice some performance. If the visual tree is large, you can consider switching to the onetime mode.
2. Onetime
It only works when it is loaded for the first time and does not work for the elements dynamically added to the visual tree.
Sometimes, your visual tree is very big, so you don't consider using auto mode. At this time, you can use the onetime mode to apply the style at one time. At the same time, after adding a new nodeCodeYou can manually call the apply method of ISM to apply the style again. This method can avoid performance loss in auto mode.
3. None
The effect is the same as if applymode is not set.
After learning how to use ism, let's see how it is implemented.
We know that the style in the Silverlight element can only be set once at runtime. Otherwise, an error will occur, and ISM is no exception. This constraint also applies.
The implementation principle of ISM is roughly as follows:
1. Define an additional property called applymode (Attached Property) for the "root" element that needs to set the style.
We know that additional attributes can be set in XAML, as shown in the preceding example. At the same time, it has the biggest benefit, it is the callback function that can be triggered when the property is changed (defined in propertymetadata during registration ). In this way, after applymode is set in the code, ISM can trigger this callback function for processing.
2. In this callback function, register the layoutupdated event processing function of the element, so that the element will be notified when it is updated for whatever reason.
Here, the most clever thing is: Delegate the processing of the element layoutupdated event to the attributes of the element in the form of dependencyproperty, this saves the trouble of managing event handler on your own... Dependency properties are really good!
Code:
/// <Summary>
/// Applymodeproperty property changed handler.
/// </Summary>
/// <Param name = "dependencyobject"> frameworkelement that changed its
/// Applymode. </param>
/// <Param name = "eventargs"> event arguments. </param>
Private Static void onapplymodepropertychanged (dependencyobject, dependencypropertychangedeventargs eventargs)
{
Frameworkelement element = dependencyobject as frameworkelement;
If (element = NULL)
{
Throw new argumentnullexception ("dependencyobject ");
}
Implicitstylesapplymode oldmode = (implicitstylesapplymode) eventargs. oldvalue;
Implicitstylesapplymode newmode = (implicitstylesapplymode) eventargs. newvalue;
Implicitstylemanager. sethasbeenstyled (element, false );
Eventhandler = implicitstylemanager. getlayoutupdatedhandler (element );
// If element is automatically styled (once or always) Attach event
// Handler.
If (newmode = implicitstylesapplymode. Auto | newmode = implicitstylesapplymode. Onetime)
& Amp; oldmode = implicitstylesapplymode. None)
{
If (eventhandler = NULL)
{
Eventhandler =
(Sender, argS) =>
{
Implicitstylemanager. propagatestyles (element, false );
};
Implicitstylemanager. setlayoutupdatedhandler (element, eventhandler );
Element. layoutupdated + = eventhandler;
}
}
Else if (oldmode = implicitstylesapplymode. Auto | oldmode = implicitstylesapplymode. Onetime)
& Newmode = implicitstylesapplymode. None)
{
If (eventhandler! = NULL)
{
Element. layoutupdated-= eventhandler;
Implicitstylemanager. setlayoutupdatedhandler (element, null );
}
}
}
3. In the preceding layoutupdated event processing function, traverse the control's visual tree and set the style for the elements that meet the conditions (or only once ).
It is worth mentioning that it is a code technique for Traversing trees. To avoid overhead caused by recursion or similar methods, a clever stack is actually used to access Tree nodes. In addition, yield return is used as much as possible in all areas to be traversed, and the actual operations on nodes are delayed in a functional programming method.
The specific code is not explained in detail, here the MS code is posted for appreciation only functional programming, interested friends can study on their own:
/// <Summary>
/// This method propagates the styles in the resources associated
/// A framework element to its descendents. This results in
/// Style inheritance that mimics WPF's behavior.
/// </Summary>
/// <Param name = "element"> the element that will have its styles
/// Propagated to its children. </param>
/// <Param name = "recurse"> whether to recurse over styled elements that
/// Are set to onetime and have already been styled. </param>
Private Static void propagatestyles (frameworkelement element, bool recurse)
{
Basemergedstyledictionary initialdictionary = getmergedstyledictionary (element );
// Create stream of elements and their base merged Style
// Dictionaries by traversing the logical tree.
Ienumerable <tuple <frameworkelement, basemergedstyledictionary> elementstostyleanddictionaries =
Functionalprogramming. Traverse (
New tuple <frameworkelement, basemergedstyledictionary> (element, initialdictionary ),
(Elementanddictionary) =>
Elementanddictionary
. First
. Getlogicalchildrendepthfirst ()
. Select (childelement =>
New tuple <frameworkelement, basemergedstyledictionary> (
Childelement,
New mergedstyleresourcedictionary (
Implicitstylemanager. getexternalresourcedictionary (childelement )?? Childelement. Resources,
Elementanddictionary. Second ))),
(Elementanddictionary) => recurse |
(Implicitstylemanager. getapplymode (elementanddictionary. First )! = Implicitstylesapplymode. Onetime |
! Implicitstylemanager. gethasbeenstyled (elementanddictionary. First )));
Foreach (tuple <frameworkelement, basemergedstyledictionary> elementtostyleanddictionary in elementstostyleanddictionaries)
{
Frameworkelement elementtostyle = elementtostyleanddictionary. first;
Basemergedstyledictionary styledictionary = elementtostyleanddictionary. Second;
Bool styleapplied = false;
If (elementtostyle. Style = NULL)
{< br> Style = styledictionary [getstylekey (elementtostyle)];
If (style! = NULL)
{< br> elementtostyle. Style = style;
styleapplied = true;
}< BR >}
If (implicitstylemanager. getapplymode (elementtostyle) = implicitstylesapplymode. Onetime & (visualtreehelper. getchildrencount (elementtostyle)> 0 | styleapplied ))
{
Implicitstylemanager. sethasbeenstyled (elementtostyle, true );
}
}
}
Refer:
Http://www.beacosta.com/blog? P = 54
Http://www.beacosta.com/blog? P = 55
(It seems like a pretty girl from the MS Silverlight team who recommends subscribing to or following her blog)
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/inelm/archive/2008/12/16/4612817.aspx