I'm trying to set the axisStroke style on AxisRenderer via CSS. It's of type IStroke. How do I create a Stroke in CSS?
Why not try my (admittedly, somewhat crazy) CSS post-processing trick: http://bitrotincarnate.wordpress.com/2009/12/16/strokes-and-fills-in-flex-css-through-actionscript-injection/
The solution I finally used was to create a custom extension of AxisRenderer that exposed the styles I needed:
package
{
import mx.charts.AxisRenderer;
import mx.graphics.Stroke;
[Style(name="axisStrokeColor", type="uint", format="Color", inherit="yes")]
[Style(name="axisStrokeWeight", type="Number", format="Length", inherit="yes")]
[Style(name="minorTickStrokeColor", type="uint", format="Color", inherit="yes")]
[Style(name="minorTickStrokeWeight", type="Number", format="Length", inherit="yes")]
[Style(name="tickStrokeColor", type="uint", format="Color", inherit="yes")]
[Style(name="tickStrokeWeight", type="Number", format="Length", inherit="yes")]
public class StyledAxisRenderer extends AxisRenderer
{
override public function styleChanged(styleProp:String):void
{
super.styleChanged(styleProp);
var stylesUpdated:Boolean = false;
if ((styleProp != "axisStroke") && (styleProp != "minorTickStroke") && (styleProp != "tickStroke"))
{
if ((getStyle("axisStrokeColor") != null) && (getStyle("axisStrokeWeight") != null))
{
var axisStroke:Stroke = new Stroke(getStyle("axisStrokeColor"), getStyle("axisStrokeWeight"));
setStyle("axisStroke", axisStroke);
stylesUpdated = true;
}
if ((getStyle("minorTickStrokeColor") != null) && (getStyle("minorTickStrokeWeight") != null))
{
var minorTickStroke:Stroke = new Stroke(getStyle("minorTickStrokeColor"), getStyle("minorTickStrokeWeight"));
setStyle("minorTickStroke", minorTickStroke);
stylesUpdated = true;
}
if ((getStyle("tickStrokeColor") != null) && (getStyle("tickStrokeWeight") != null))
{
var tickStroke:Stroke = new Stroke(getStyle("tickStrokeColor"), getStyle("tickStrokeWeight"));
setStyle("tickStroke", tickStroke);
stylesUpdated = true;
}
if (stylesUpdated)
{
invalidateDisplayList();
}
}
}
}
}
Not sure I understand the guts of your code logic above, although I do understand its purpose. ;-)
However, relying on styleChanged() alone did not work for me. Particularly if I switched CSS styles during run-time and and the new style declaration didn't have "axisStroke" in it -- which of course it can't! The fuller solution would be to include code for styleChanged() and also stylesInitialized(). In the source, I also include a reference to the stroke object _axisStroke. This cuts down countless calls to getStyle("axisStroke") as well as being necessary for the guaranteed instantiation logic below.
Like I said, my problem was I would get run-time errors if I declared a style entry in CSS for an axis renderer component but did not set its "axisStroke" style property programatically. Although the work-around for this is usually to instantiate a default CSSStyleDeclaration in a classConstructed() static method (see adobe's developer guide and styles for this common practice), this will not work if the style CSS is changed dynamically during run-time. The classConstructed() only gets called initially and usually checks to make sure there is no style declaration already for the component before registering the default. Then later, when you change styles, your component's "axisStroke" style gets cleared. THEN, you can get null exceptions. Furthermore, just using CSS, you can't insert a new "axisStroke" style. But, since stylesInitialized() is called anytime a component's style selector setting changes -- eg: StyleManager.setStyleDeclaration("MyComponent", myStyleDeclaration) -- you can insure all the styles are set and in place, and anything that might throw a null exception, such as a missing "axisStroke" style, can be accounted for.
private var _axisStroke:IStroke;
override public function stylesInitialized():void {
super.stylesInitialized();
_axisStroke = getStyle("axisStroke");
if (!_axisStroke) {
//usually a default stroke style is set in a default CSSStyleDeclaration
//created in a classConstructed() method
_axisStroke = new Stroke(0xCCDDEE, 8, 1, false, LineScaleMode.NORMAL, CapsStyle.NONE);
//note: I do NOT register _axisStroke to the style "axisStroke".
//Since "axisStroke" is still technically not set, I leave it null.
//You may need to set it, however, if you are extending AxisRenderer.
//setStyle("axisStroke", _axisStroke);
}
var axisColor:Number = getStyle("axisColor");
if (!isNaN(axisColor)) {
Object(_axisStroke).color = axisColor;
}
...
}
Anyway, if just using styleChanged() works for you, great. But if you still get run-time errors from styles not being initialized or in the wrong order, look into using stylesInitialized(). This is particularly useful when making your own IAxisRenderer classes.
Someone :) suggested I should post a link to my InsideRIA about how to "tweak" a CSSStyleDeclaration to add the stroke information so that you don't have to override AxisRenderer (or have an explicit AxisRenderer at all) http://insideria.com/2010/08/usinc-css-for-strokes-on-flex.html,
Additionally, I'd like to add that I've started dealing with custom styles in a way that's a little different than how the examples show. It looks something like this:
protected var _myStyleValue:SomeType = someValueThatWouldNeverOccurInNature; protected var _myThingTheStyleIsAttachedTo:IStyleClient; //set this flag whenever you want to refresh the object protected var _myThingTheStyleIsAttachedToChanged:Boolean;
override protected function commitProperties():void { if (_myThingTheStyleIsAttachedToChanged) { var myStyleValue:SomeType = getMyStyleValue(); _myThingTheStyleIsAttachedTo.setStyle('myStyle', myStyleValue); _myThingTheStyleIsAttachedToChanged = false; } }
override public function styleChanged(styleProp:String):void { super.styleChanged(styleProp); if (styleProp=='myStyle') { _myThingTheStyleIsAttachedToChanged = true; _myStyleValue = someValueThatWouldNeverOccurInNature; } }
protected function getMyStyleValue():SomeType { if (_myStyleValue==someValueThatWouldNeverOccurInNature) { var theValue:*=getStyle('myStyle'); if (theValue==undefined) { _myStyleValue=theDefaultValue; } else { _myStyleValue=theValue as SomeType; } return _myStyleValue; } }
It's a little more verbose than what you usually see, especially if there are multiple properties you need to look at. However, it sidesteps issues about styles applied in MXML vs. CSS style declaration, as well as what happens when a style changes. It's also "lazy", and so may not get called if it is not needed.
@James:
Looks like a little bug here:
if ((getStyle("axisStrokeColor") != null) && (getStyle("axisStrokeColor") != null))
{
var axisStroke:Stroke = new Stroke(getStyle("axisStrokeColor"), getStyle("axisStrokeColor"));
Repeating the axisStrokeColor twice. Think you meant to refer to strokeWeight as well:
if ((getStyle("axisStrokeWeight") != null) && (getStyle("axisStrokeColor") != null))
{
var axisStroke:Stroke = new Stroke(getStyle("axisStrokeColor"), getStyle("axisStrokeWeight"));