MvvmCross noob here. Does anyone know why I can't bind a property in a UILabel derived class?
var set = this.CreateBindingSet<LoginView, LoginViewModel>();
set.Bind(_serverValue).For(p => p.Text).To(vm => vm.ServerListSelectedItem);
set.Bind(_serverValue).For(p => p.Visible).To(vm => vm.IsServerListAvailable);
set.Apply();
private class ServerValue : UILabel
{
public bool Visible
{
get { return !Hidden; }
set { Hidden = !value; LoginView.LayoutControls(); }
}
}
The text gets updated but the Visible property never does. Should I even expect that this should work?
Thanks,
Jon
MvvmCross uses Reflection - and Reflection is subject to .Net security rules.
Try making your control public rather than private to see if that helps -
public class ServerValue : UILabel ...`
Beyond that, there is also a registered custom binding for Visible in MvvmCross by default - see https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Binding.Touch/MvxTouchBindingBuilder.cs#L45
registry.RegisterCustomBindingFactory<UIView>("Visible",
view =>
new MvxUIViewVisibleTargetBinding(view));
You may find that this conflicts with (and hides) your Visible property - custom bindings are checked before Reflection - so you may need to choose a different property name.
For more on binding to custom properties and more on custom bindings see N=19 and N=28 in http://mvvmcross.blogspot.com
Related
I have a custom editor in my EditorTemplates folder for a IList<PersonRelations>. The Editor has this model:
#model IList<PersonRelation>
and in my entity is as this:
public IList<PersonRelation> Relations { get; set; }
this is how I called it in my view:
<div class="editor-field">
#Html.EditorFor(model => model.Relations)
</div>
and it's rendering the model if Relations is null.
But.. I want to declare my property in this way
private IList<PersonRelation> _relations;
public IList<PersonRelation> Relations
{
get { return _relations ?? (_relations = new List<PersonRelation>()); }
set { _relations = value; }
}
To avoid null references exceptions.
The thing is when the List is not null and has no elements, the editor is not being displayed at all.
In my editor I iterate through the elements but also I render another controls outside the loop, and I can't see any elements.
I'm missing something?
Solved.
When I changed the property, I forgot to decorate it with [UIHint("PersonRelations")]
which was in the original form of the property (my custom editor's file name is "PersonRelations.cshtml")
This is needed due it seems that the engine is not able to infer the editor for a collection, even when you have one, so you explicitly have to tell which one you want to use.
I have a seemingly simple use case. There is a ICsvReader component. Let's name it simply Reader here. We load a known set of CSV files and some of them have headers and some don't. Currently there are multiple readers: Reader_Skips1Row, Reader_Skips2Rows etc.
Is there a way to register only one component and have Windsor look at the component key, strip the "_Skips..." part and resolve the required component with relevant properties set?
I have tried subresolver and facility with no luck.
EDIT
Yes there is only one implementation but it is used as a dependency and configured to be resolved by name. The reader is configured in code
Component.For<ICsvReader>()
.ImplementedBy<CommaSeparetedCsvReader>()
.DependsOn(new { SkipHeader = true, HeaderRowsToSkip = 2 } )
.Named("CommaSeparetedCsvReader_Skips2Rows")
.Lifestyle.Transient
Component.For<ICsvReader>()
.ImplementedBy<CommaSeparetedCsvReader>()
.DependsOn(new { SkipHeader = true, HeaderRowsToSkip = 1 } )
.Named("CommaSeparetedCsvReader_Skips1Row")
.Lifestyle.Transient
Component.For<ICsvReader>()
.ImplementedBy<CommaSeparetedCsvReader>()
.Named("CommaSeparetedCsvReader")
.Lifestyle.Transient
These are used as dependency in a processor class. It is configured in XML, so that in can be manipulated at runtime
<component id="Processor
type="Processor">
<parameters>
<reader>CommaSeparetedCsvReader_Skips2Rows</reader>
</parameters>
</component>
Ideally I would like to register only the CommaSeparetedCsvReader component but when an attempt is made to resolve CommaSeparetedCsvReader_Skips2Rows it should strip the suffix, parse it and change the properties accordingly.
Is it possible to somehow modify the Resolve() behavior?
Thanks,
Tom
If you are resolving your components using the TypedFactoryFacility, creating a custom ITypedFactoryComponentSelectors might help you. I would need more detail on how you create the Readers to give you more info.
Kind regards,
Marwijn.
Edit =====================================
Let's add an example:
public interface IFoo
{
}
public class Foo1 : IFoo
{
}
public class Foo2 : IFoo
{
}
public interface IFooFactory
{
IFoo CreateFoo(string which);
}
public class FooFactoryComponentSelector : DefaultTypedFactoryComponentSelector
{
protected override string GetComponentName(MethodInfo method, object[] arguments)
{
return (string)arguments[0];
}
}
--- registration
container.AddFacility<TypedFactoryFacility>();
Component.For<IFoo>().Named("Foo1Name").ImplementedBy<Foo1>(),
Component.For<IFoo>().Named("Foo2Name").ImplementedBy<Foo2>(),
Component.For<IFooFactory>().AsFactory(f => f.SelectedWith(new FooFactoryComponentSelector())),
--- usage
var factory = _container.Resolve<IFooFactory>(); // in general this would just be a dependency in the constructor.
var foo = factory.CreateFoo("Foo2Name");
Just adapt the component selector to your needs. If necessary you can also pass additional arguments to CreateFoo, if the constructor requires arguments not provided by the container.
More info: http://docs.castleproject.org/Windsor.Typed-Factory-Facility-interface-based-factories.ashx
Using this abstract class:
#JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
#JsonSubTypes({ #JsonSubTypes.Type(value = PostingTaskInstanceDto.class, name = "TI") })
public abstract class BasePostingDto {}
and this inherited class:
public class PostingTaskInstanceDto extends BasePostingDto {}
I get correct serialization for a single object. This works, using Spring-MVC:
#RequestMapping("/{id}")
#ResponseBody
public BasePostingDto findById(#PathVariable("id") Long id) {
return createDto(postingService.findById(id));
}
But if I retrieve a List of BasePostingDto from the remote controller, the type property is missing:
#RequestMapping("/by-user/all")
#ResponseBody
public List<BasePostingDto> findByUser() {
return createDtoList(postingService.findByUser(AuthUtils.getUser()));
}
Why is this and how can I force the type property?
Update: the type property is also included if I change List<BasePostingDto> to BasePostingDto[], however I would prefer to go with the List.
It sounds like the framework you are using (and which uses Jackson under the hood) is not passing full generics-aware type information.
I don't know how that can be fixed (it is problem with integration by framework, and not something Jackson can address), but the usual work around is for you to use sub-class of List:
public class PostingDtoList extends List<BasePostingDto> { }
and use that in signature, instead of generic type. This solves the issue because then the generic type signature is retained (since it is stored in super type declaration, and accessible via type-erased PostingDtoList class!).
In generally I think it is best to avoid using generic List and Map types as root type (and instead use POJO); partly because of problems issued (there are bigger problems when using XML for example). But it can be made to work if need be.
I'm building a mobile AIR app using Flash Builder 4.5. The initial view in my views package is TestHomeView.mxml. I want to refer to it in one of my .as classes elsewhere in the app, and I'm not sure how to do that.
Theoretically I should be able to add an "id" attribute to TestHomeView.mxml, but FB gives me an error: "id is not allowed on the root tag of a component". The root tag is s:view.
The reason I need to do this is that within another class I make various calculations and then need to pass an array of values to a component in my view class. So in SomeOtherActionScriptClass.as I first assemble the array, myArray, and then in that class I want to do this:
myViewComponent.viewArray = myArray;
If I'm going to do that, I also need to import the view class into the .as class, which strikes me as weird. So is there a simple way to do what I want, or do I have to dispatch a custom event which contains the array, and listen for it in the view class?
EDIT - Based on the below MVC suggestion I did the following in model:
[Bindable]
public class Model
{
private static var myModel:Model;//doesn't let me name it 'model' because
//I have a package named 'model'
public var myArray:Array; //its value set later in model code
public function Model()
{
if ( Model.myModel != null ){
throw new Error( "Only one Model instance should be instantiated" );
}
}
// singleton: always returns the one existing static instance to itself
public static function getInstance() : Model {
if ( myModel == null ){
myModel = new Model();
}
return myModel;
}
Then in the view code I have:
[Bindable] //do I actually need this?
private var myModel:Model = Model.getInstance();
var viewArray = new Array();
viewArray = myModel.myArray;
But it is coming back null. It isn't null when I put a breakpoint in the Model class, but when I try to access it from the view class, it's null. The model itself isn't null, but that variable is.
What am I doing wrong?
Thanks.
First, if you are trying to make a singleton in AS3 you should first create a class, within the same class file as Model, that is used to ensure you can only create the class once.
Add this class at the bottom of the Model class file (outside of the Model class):
internal class SingletonEnforcer{}
Then create the Model constructor like this:
public function Model(enforcer:SingletonEnforcer){ Init(); } // if init code is needed
public static function get Instance():Model
{
if (!myModel){
myModel = new Model(new SingletonEnforcer());
}
return myModel;
}
Now you don't have to throw an exception for creating a second instance because it isn't possible.
I'm not sure about your first question of referencing your app's main mxml, but if you were asking how to call the app that is running (like WindowedApplication in AIR) then you would call it like this:
// my WindowedApplication file = MyApp.mxml
MyApp(this.parentApplication)
That will return the app's instance.
Once you've set up the Singleton like I have it above you should be able to access your array like:
Model.Instance.myArray;
I hope this helps!
Follow the MVC pattern.
Create Model class (make it Bindable) with a property viewArray. Bind to this property from your View. And in any other class just change viewArray property of the model. The binding event will be fired and this property will also be changed in your View. To make your Model "visible" from any point, you can make it a Singleton.
I have a swing app with a text box bound to a property on my model (this is a READ_WRITE AutoBinding). The model also has an isDirty property that I want to bind to a button's enabled property.
How do I properly notify the binding when I change the state of isDirty.
Here is my binding code:
BeanProperty<PaChannelConfig, Boolean> paChannelConfigBeanProperty_1 =
BeanProperty.create("dirty");
BeanProperty<JButton, Boolean> jButtonBeanProperty =
BeanProperty.create("enabled");
AutoBinding<PaChannelConfig, Boolean, JButton, Boolean> autoBinding_2 =
Bindings.createAutoBinding(
UpdateStrategy.READ,
model,
paChannelConfigBeanProperty_1,
btnApply, jButtonBeanProperty);
autoBinding_2.bind();
What is the proper way to add this notification?
Basically, the model should have the methods
addPropertyChangeListener(PropertyChangeListener)
removePropertyChangeListener(PropertyChangeListener)
firePropertyChange(PropertyChangeEvent)
look at the class PropertyChangeSupport, that class have implementations of the methods above.
In the model, the method setDirty(boolean) should be implemented like this:
public void setDirty(boolean dirty) {
boolean old = this.dirty;
this.dirty = dirty;
firePropertyChange(new PropertyChangeEvent("dirty", old, dirty));
}
hope that helps
You can use PropertyChangeSupport to easily implement support for property change notification. The documentation at the provided link has an example of how to set it up and use it.