I'm making a level select screen and I need the text field to display different level numbers for each level. I don't really see what I'm doing wrong here, but I'll go over what I did and post the relevant code.
I have a button class (linked) and inside the symbol I have a dynamic text field. I have two classes of relevance, LevelSelectScreen and LevelSelectButtons (pretty self-explanatory what they are). I thought it would be really easy to change the text if I did it inside the LevelSelectButtons class, by simply doing levelText.text = "Wanted Text", where levelText is the given instance name for my button (just a text field on top of my graphic for the button). Unfortunately, this gives the oh so common and annoying error: TypeError: Error #1009: Cannot access a property or method of a null object reference.
I tried doing virtually the same thing in my LevelSelectScreen class during my loop, but I got the same error. Help on how to get this levelText to work is greatly appreciated! Here is the relevant code.
LevelSelectScreen
public class LevelSelectButtons extends SimpleButton {
public var levelNumber:int;
public var levelSelectScreen:LevelSelectScreen;
public function LevelSelectButtons(i) {
x = 200;
y = 100 + 50*i;
addEventListener(MouseEvent.CLICK,LevelSelectClicked,false,0,true)
levelNumber = i;
levelText.text = "Level" + i;
}
}
LevelSelectScreen
public class LevelSelectScreen extends MovieClip {
public var levelSelectButtons:LevelSelectButtons;
public var mainMenuButton:MainMenuButton;
public function LevelSelectScreen() {
for (var i:int = 1; i<=2; i++)
{
levelSelectButtons = new LevelSelectButtons(i);
addChild(levelSelectButtons);
}
}
}
You can't have a dynamic text field in a SimpleButton.
Annoying, I know.
Simple fix would be to have LevelSelectButton wrap a SimpleButton instead of extend it. Then your text field would be inside LevelSelectButton on top of a text-less SimpleButton. (Be sure to set mouseEnabled to false on the text field so it doesn't interfere with mouse events on the SimpleButton.
A more complex option would be to write your own custom button class.
It's not actually that difficult, but might be overkill for what you're trying to do here.
its because you haven't declared levelText variable and you are trying to access it, therefor Cannot access a property or method of a null object reference.
I am trying to build a very simple widget with two input fields Name and Email address. Right now I am following http://www.deepcode.co.uk/search?q=how+to+build+a+widget+without+a+model+in+Orchard.
Migration.cs
public int UpdateFrom22()
{
ContentDefinitionManager.AlterTypeDefinition("QuickLinkWidget", builder => builder
.WithPart("QuickLinkWidgetPart")
.WithPart("CommonPart")
.WithPart("WidgetPart")
.WithSetting("Stereotype", "Widget"));
return 23;
}
ContentPartRecord (Models/QuickLinkWidgetRecord.cs)
public class QuickLinkWidgetRecord : ContentPartRecord
{
//We shall not define any property for this class.
//We shall not create any HANDLER for this widget because we do not need to store any data to our database.
}
ContentPart: (Models/QuickLinkWidgetPart.cs)
public class QuickLinkWidgetPart : ContentPart<QuickLinkWidgetRecord>
{
//Again we shall not definte any Record property here......
//We shall not create any HANDLER for this widget because we do not need to store any data to our database.
}
Handler
Did not define any as I don't have deal with any repository.
Driver: ((Models/QuickLinkWidgetDriver.cs))
public class QuickLinkWidgetDriver : ContentPartDriver<QuickLinkWidgetPart>
{
override DriverResult Display(QuickLinkWidgetPart part, string displayType, dynamic shapeHelper)
{
//We do not have any property defined in Model class.
//So we don't have to assign any Default values to anything!
//We shall just return a SHAPE of the widget
}
}
Placement.info:
<Place Widgets_QuickLinkWidget="AsideFirst:1" />
And finally the View: Views/Widgets/QuickLinkWidget.cshtml
#using Emfluence.Intrust.Models;
#model Contact
<h2>Our Side link widget</h2>
Nothing extra I added in my View
Please help me understanding what I am doing wrong. Why I am not able to see the widget rendered at all? I am stuck with it for the last 3 days!, just want to see it rendering first!
Thanks in advance fro your help
I had similar issue and i got it working by changing some setting on my placement.info file...in your case try this
<Place Widgets_QuickLinkWidget="Content" />
My question is :
I want to know when does a xLayout (or ViewGroup in general) add a child view from XML ? And by "when" I mean at what point of code, in what "pass" of the "traversal" of the UI toolkit ?
Which method of xLayout or ViewGroup should I override ?
I have done my homework : I have watched the "Writing Custom Views For Android" presented (by Adam Powell and Romain Guy) in the last Google I/O and I have read Adam Powell comments on this Google+ post.
Looking for the exact point in Android's source code where children are added.
We can look at what setContentView(R.layout.some_id) is doing under the hood.
setContentView(int) calls PhoneWindow#setContentView(int) - PhoneWindowLink is a concrete inplementation of Window:
#Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
The method LayoutInflater#inflate(layoutResID, mContentParent) eventually calls ViewGroup#addView(View, LayoutParams) on mContentParent. In between, child views
I want to know what happens exactly after I set content view to an XML file that contains a custom view. Afer the constructor there has to be a part in the code where the custom view "parse/read/inflate/convert" XML-declared child views to actual views ! (comment by JohnTube)
Ambiquity: From JohnTube's comment, it seems he is more interested in knowing how a custom view is inflated. To know this, we will have to look at the workings of LayoutInflaterLink.
So, the answer to Which method of xLayout or ViewGroup should I override ? is ViewGroup#addView(View, LayoutParams). Note that, at this point, the inflation of all regular/custom Views has already taken place.
Inflation of custom views:
The following method in LayoutInflater is where the addView(View, LayoutParams) is called on the parent/root:
Note: The call mLayoutInflater.inflate(layoutResID, mContentParent); in PhoneWindow#setContentView(int) chains to this. Here mContentParent is the DecorView: the view that's accessible through getWindow().getDecorView().
// Inflate a new view hierarchy from the specified XML node.
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot)
// Recursive method used to descend down the xml hierarchy and instantiate views,
// instantiate their children, and then call onFinishInflate().
void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs,
boolean finishInflate) throws XmlPullParserException, IOException
The call of interest in this method(and in the recursive rInflate(XmlPullParser, View, AttributeSet, boolean)) is:
temp = createViewFromTag(root, name, attrs);
Let's see what createViewFromTag(...) is doing:
View createViewFromTag(View parent, String name, AttributeSet attrs) {
....
....
if (view == null) {
if (-1 == name.indexOf('.')) {
view = onCreateView(parent, name, attrs);
} else {
view = createView(name, null, attrs);
}
}
....
}
The period(.) decides whether onCreateView(...) or createView(...) is called.
Why this check? Because a View defined in android.view, android.widget or android.webkit package is accessed through its class name. For example:
android.widget: Button, TextView etc.
android.view: ViewStub. SurfaceView, TextureView etc.
android.webkit: WebView
When these views are encountered, onCreateView(parent, name, attrs) is called. This method actually chains to createView(...):
protected View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
return createView(name, "android.view.", attrs);
}
This would deal with SurfaceView, TextureView and other views defined in android.view package. If you are interested in knowing how TextView, Button etc. are dealt with, look at PhoneLayoutInflaterLink - it extends LayoutInflater and overrides onCreateView(...) to check if android.widget and android.webkit are the intended package names. In fact, the call getLayoutInflater() gets you an instance of PhoneLayoutInflater. This is why if you were to subclass LayoutInflater, you couldn't even inflate the simplest of layouts - because LayoutInflater can only deal with views from android.view package.
Anyway, I digress. This extra bit happens for regular Views - which don't have a period(.) in their definition. Custom views do have a period in their names - com.my.package.CustomView. This is how the LayoutInflater distinguishes between the two.
So, in case of a regular view(say, Button), a prefix such as android.widget will be passed as the second argument - for custom views, this will be null. The prefix is then used along with the name to obtain the constructor for that particular view's class. Custom views don't need this because their name is already fully qualified. I guess this has been done for convenience. Else, you would have been defining your layouts in this way:
<android.widget.LinearLayout
...
... />
(Its legal though...)
Also, this is why views coming from a support library (eg. <android.support.v4.widget.DrawerLayout.../>) have to use fully qualified names.
By the way, if you did want to write your layouts as:
<MyCustomView ../>
all you have to do is to extend LayoutInflater and add your package name com.my.package. to the list of strings that are checked during inflation. Check PhoneLayoutInflater for help with this.
Let's see what happens in the final stage for both custom and regular views - createView(...):
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
// Try looking for the constructor in cache
Constructor<? extends View> constructor = sConstructorMap.get(name);
Class<? extends View> clazz = null;
try {
if (constructor == null) {
// Class not found in the cache, see if it's real, and try to add it
clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name).asSubclass(View.class);
....
// Get constructor
constructor = clazz.getConstructor(mConstructorSignature);
sConstructorMap.put(name, constructor);
} else {
....
}
Object[] args = mConstructorArgs;
args[1] = attrs;
// Obtain an instance
final View view = constructor.newInstance(args);
....
// We finally have a view!
return view;
}
// A bunch of catch blocks:
- if the only constructor defined is `CustomView(Context)` - NoSuchMethodException
- if `com.my.package.CustomView` doesn't extend View - ClassCastException
- if `com.my.package.CustomView` is not found - ClassNotFoundException
// All these catch blocks throw the often seen `InflateException`.
}
... a View is born.
If you're talking about a ViewGroup defined in XML, it's children are added when the view is inflated. This can be when you inflate explicitly with a LayoutInflater or when you set the content view of an activity. (There are probably a few other times as well, particularly if you are using stub views.)
If you want to add the children yourself to a ViewGroup that is not inflated, you can do that in the view's constructor.
EDIT: If you want to see how the children are added when a view is inflated, this occurs in the call to LayoutInflater.inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot). The source for android.view.LayoutInflater is included in the Android SDK distributions; on-line versions can be found in many places (here at GrepCode, for instance). This method ends up being called when, for instance, you call setContentView(int) for an Activity or when you explicitly inflate a layout resource.
The children are actually added in the call to rInflate(parser, root, attrs, false); ("recursive inflate"), which might be called from a couple of different places in the inflate() method, depending on what the inflater found as the root tag. You can trace through the code logic yourself. An interesting point is that a child is not added to its parent until its own children have been recursively inflated and added to it.
The other interesting method, used by both inflate and rInflate, is createViewFromTag. This might rely on an installable LayoutInflater.Factory (or .Factory2 object) to create the view, or may end up calling createView. There you can see how the call to the view's two-argument constructor ((Context context, AttributeSet attrs)) is made.
Is it possible to convert ExpandoObject to anonymously typed object?
Currently I have HtmlHelper extension that can take HTML attributes as a parameter. The problem is that my extension also needs to add some HTML attributes so I've use ExpandoObject to merge my attributes and attributes that user passes to the function using htmlAttributes parameter. Now I need to pass merged HTML attributes to original HtmlHelper function, and when I send ExpandoObject, nothing happens. So I guess that I need to convert ExpandoObject to anonymously typed object or something similar - any suggestions are welcome.
I don't think that you need to deal with expandos to achieve your goal:
public static class HtmlExtensions
{
public static IHtmlString MyHelper(this HtmlHelper htmlHelper, object htmlAttributes)
{
var builder = new TagBuilder("div");
// define the custom attributes. Of course this dictionary
// could be dynamically built at runtime instead of statically
// initialized as in my example:
builder.MergeAttribute("data-myattribute1", "value1");
builder.MergeAttribute("data-myattribute2", "value2");
// now merge them with the user attributes
// (pass "true" if you want to overwrite existing attributes):
builder.MergeAttributes(new RouteValueDictionary(htmlAttributes), false);
builder.SetInnerText("hello world");
return new HtmlString(builder.ToString());
}
}
and if you wanted to call some of the existing helpers, then a simple foreach loop could do the job:
public static class HtmlExtensions
{
public static IHtmlString MyHelper(this HtmlHelper htmlHelper, object htmlAttributes)
{
// define the custom attributes. Of course this dictionary
// could be dynamically built at runtime instead of statically
// initialized as in my example:
var myAttributes = new Dictionary<string, object>
{
{ "data-myattribute1", "value1" },
{ "data-myattribute2", "value2" }
};
var attributes = new RouteValueDictionary(htmlAttributes);
// now merge them with the user attributes
foreach (var item in attributes)
{
// remove this test if you want to overwrite existing keys
if (!myAttributes.ContainsKey(item.Key))
{
myAttributes[item.Key] = item.Value;
}
}
return htmlHelper.ActionLink("click me", "someaction", null, myAttributes);
}
}
Is it possible to convert ExpandoObject to anonymously typed object?
Only if you generate the anonymous type yourself at execution time.
Anonymous types are normally created by the compiler, at compile-time, and baked into your assembly like any other type. They're not dynamic in any sense. So, you'd have to use CodeDOM or something similar to generate the same kind of code that's used for anonymous type... that's not going to be fun.
I think it's rather more likely that someone else will have created some MVC helper classes which know about ExpandoObject (or can just work with IDictionary<string, object>).
I am using the basic instructions (here) for creating a property driven by a custom ToolPart.
All is good, except for the part where, in order to access the webpart property within the ApplyChanges method I must cast the "this.ParentToolPane.SelectedWebPart" back to a concrete "SimpleWebPart" class.
public override void ApplyChanges()
{
SimpleWebPart wp1 = (SimpleWebPart)this.ParentToolPane.SelectedWebPart;
// Send the custom text to the Web Part.
wp1.Text = Page.Request.Form[inputname];
}
Doing this means that I must pair each toolpart with a specific webpart. Is there a better way?
I cannot create an interface as there is no way of specifying a property in one.
I ineptly tried an passing an event/eventhandler during toolpart creation, but that did not update the webpart property when called.
I could create a base class for all the webparts that have a public "Text" property, but that is fugly.
I could also get desperate and crack open the this.ParentToolPane.SelectedWebPart reference with Reflection and call any properties named "Text" that way.
Either way, I am staring down the barrel of a fair bit of faffing around only to find out each option is a dead end.
Has anyone done this and can recommend the correct method for creating a reusable toolpart?
I have used an interface instead of a specific instance of a webpart.
private class IMyProperty
{
void SetMyProperty(string value);
}
public override void ApplyChanges()
{
IMyProperty wp1 = (IMyProperty)this.ParentToolPane.SelectedWebPart;
// Send the custom text to the Web Part.
wp1.SetMyProperty(Page.Request.Form[inputname]);
}
But this does not give a compile time warning that the toolpart requires the parent webpart to implement the IMyProperty interface.
The simple solution to that is to add a property of the IMyProperty interface in the toolpart constructor and call this reference instead of the this.ParentToolPane.SelectedWebPart property.
public ToolPart1(IContentUrl webPart)
{
// Set default properties
this.Init += new EventHandler(ToolPart1_Init);
parentWebPart = webPart;
}
public override void ApplyChanges()
{
// Send the custom text to the Web Part.
parentWebPart.SetMyProperty(Page.Request.Form[inputname]);
}
public override ToolPart[] GetToolParts()
{
// This is the custom ToolPart.
toolparts[2] = new ToolPart1(this);
return toolparts;
}
This works fine, but I cannot get over the feeling that there is something nasty in the underlying SharePoint code that may trip me up later.