I have the following objects in c# :
Object A :
[ObservableObject]
public partial class MyClassA
{
[ObservableProperty]
bool isSelected;
public MyClassA(bool IsSelected)
{
this.isSelected = IsSelected;
}
}
Object B
Object B references object A in a ObservableCollection :
[ObservableObject]
public partial class MyClassB
{
[ObservableProperty]
ObservableCollection<MyClassA> myClassAList;
public MyClassB(ObservableCollection<MyClassA> MyClassAList)
{
this.myClassAList = MyClassAList;
}
public override string ToString()
{
int c = 0;
for (i=0; i<= myClassAList.count-1; i++) {
if (myClassAList(i).IsSelected) {
c = c + 1;
}
}
return "Items selected " + c;
}
}
What I need is whenever something changes in any object in the list myClassAList, MyClassB.ToString also should notify the change.
So far when binding a single MyClassA object to a XAML page, changes are reflected on the use interface (this is ok).
However when binding a MyClassB object, changes in the list of objects MyClassA, are not reflected (this is not ok)
Does anyone know how to solve that please ?
Thanks. Cheers
Related
Something that has been bothering me for a long time is why do the following lines of code have the same results.
Code 1:
Transform[] childs = gameObject.GetComponentsInChildren<Transform>();
foreach(Transform child in childs) { Debug.Log(child.name); }
Code 2:
foreach(Transform child in gameObject.transform) { Debug.Log(child.name); }
This is actuallly a pseudo-code, I didn't really test it but should be enough to explain.
My question is, what's happening on Code 2 ? Is gameObject.transform actually an array of Transform ? Why doesn't Code 2 print the name of the parent gameObject ?
Maybe this is something very simple and obvious I'm just overlooking but I can't make it out right now.
Transform implements the IEnumerable interface. This interface is what allows the use of the foreach keyword.
public partial class Transform : Component, IEnumerable
The IEnumerable interface requires implementation of the GetEnumerator() method. The enumerator is responsible for keeping track of the position in the underlying collection and indicating if there are more items to be iterated over.
This is implemented in Transform below
public IEnumerator GetEnumerator()
{
return new Transform.Enumerator(this);
}
private class Enumerator : IEnumerator
{
Transform outer;
int currentIndex = -1;
internal Enumerator(Transform outer)
{
this.outer = outer;
}
//*undocumented*
public object Current
{
get { return outer.GetChild(currentIndex); }
}
//*undocumented*
public bool MoveNext()
{
int childCount = outer.childCount;
return ++currentIndex < childCount;
}
//*undocumented*
public void Reset() { currentIndex = -1; }
}
I have a class called DisplayInventory
Dictionary<InventoryObject, GameObject> itemsDisplayed = new
Dictionary<InventoryObject, GameObject>();
itemsDisplayed.Add(inventory.Container[i], obj);
the code breaks at this line (inventory.Container[i]) because it
cannot convert (field) List InventoryObject.Container.
this is my InventoryObject class
public class InventoryObject : ScriptableObject {
public List Container = new List();
public void AddItem(ItemObjectData _item, int _amount)
{
bool hasItem = false;
for (int i = 0; i < Container.Count; i++)
{
if(Container[i].item == _item)
{
Container[i].AddAmount(_amount);
hasItem = true;
break;
}
}
if(!hasItem)
{
Container.Add(new InventorySlot(_item, _amount));
}
}
}
[System.Serializable] public class InventorySlot {
public ItemObjectData item;
public int amount;
public InventorySlot(ItemObjectData _item, int _amount)
{
item = _item;
amount = _amount;
}
public void AddAmount(int value)
{
amount += value;
} }
The part where you declare the inventory variable or specifically inventory.container is missing.
Or is this the container from the InventoryObject class?
You need the specific type InventoryObject for the Dictionary.
What you are giving into it is simply just a List object that you put inside the container.
If you are refering to the container from the InventoryObject class, it really is just a List() that you hold there.
In this case the Dictionary would only need inventory as input.
Or you could change from
public List Container = new List();
to this
public List<InventoryObject> Container = new List<InventoryObject>();
This would still make more sense if this container is outside of the InventoryObject and in an Inventory class or something.
I've a Collection class which aims to store different kind of objects, however I'd like to call specific method name which these classes share (e.g. ToString()).
Here is my attempt:
class Collection {
public:
void *data[];
void Collection() {}
void ~Collection() {
for (int i = 0; i < ArraySize(data); i++) {
if (CheckPointer(data[i]) == POINTER_DYNAMIC) delete data[i];
}
}
void *Add(void *_object) {
uint _size = ArraySize(data);
ArrayResize(data, _size + 1, 100);
data[_size] = _object;
return _object;
}
string ToString(string _dlm = ",") {
string _out = "";
for (int i = 0; i < ArraySize(data); i++) {
if (CheckPointer(data[i]) == POINTER_DYNAMIC) {
_out += ((void *) data[i]).ToString(); // #fixme: Syntax error.
}
}
return _out;
}
};
However using ((void *) data[i]).ToString() syntax fails with:
'ToString' - member function not defined Collection.mqh
How can I call a ToString() method for each stored object?
It seems to me that it should be something like collection.toString() where collection is an object of your class Collection. Then each object that you add to your collection should implement this function... Maybe it is easier to mention some superclass that supports toString() (or interface with this method) and make sure that you add only correct objects? This also makes your code free of unexpected errors in runtime.
Also CArrayObj is at your disposal with most functions, if you need toString() or any other function then you can simply extend basic class. Maybe the only disadvantage of the default collection is that it stores CObject-inherited objects.
This can be achieved by creating an abstract class where all classes can share the same virtual method. For example:
class Object {
public:
virtual string ToString() = NULL;
};
class Foo : public Object {
public:
virtual string ToString() {
return "Foo";
};
};
class Bar : public Object {
public:
virtual string ToString() {
return "Bar";
};
};
Then in the Collection class the following method:
virtual string ToString(string _dlm = ",") {
string _out = "";
for (int i = 0; i < ArraySize(data); i++) {
if (CheckPointer(data[i]) == POINTER_DYNAMIC) {
_out += ((Object *) data[i]).ToString() + _dlm;
}
}
return _out;
}
Sample usage:
Collection *objects;
objects = new Collection;
Foo *foo = new Foo();
Bar *bar = new Bar();
objects.Add(foo);
objects.Add(bar);
Print(objects.ToString());
delete objects;
Is there a way of mimicking MonoBehaviour copy semantics in ScriptableObjects?
Say I have a MonoBehaviour like so:
public class DummyClassBehaviour : MonoBehaviour {
public DummyClass DummyClassTest; //ScriptableObject
public DummyClassBehaviour DummyBehaviourTest; //Another DummyClassBehaviour
}
And a ScriptableObject:
public class DummyClass : ScriptableObject {
public string Text = "";
}
When I duplicate(CTRL+D) a GameObject w/ DummyClassBehaviour attached, 'DummyBehaviourTest' copies as you would expect: If it references a MonoBehaviour in the GameObject I'm copying, the copy mechanism updates the reference to the same MonoBehaviour type in the new GameObject. If it references a MonoBehaviour in another GameObject, that reference remains unchanged.
The ScriptableObject, on the other hand, always references the original. So I end up with N GameObject's all sharing the same ScriptableObject (DummyClass) from the original GameObject. I'm using ScriptableObjects to allow serialization of non-Monobehaviour data classes.
As far as I can tell, and please someone correct me if I'm wrong, you cannot modify the serialization behavior of a ScriptableObject to match that of a MonoBehaviour. Namely that it should update references if a duplicate is made.
Instead I opted for a less than optimal solution, but it works. My class is assigned a unique identifier that gets serialized like everything else. I use this ID in DummyBehaviour.Awake() to create a lookup table that I can then use to reassign my DummyClass.
I'm not going to accept my own answer because I don't feel it answers my original question fully, but it's related:
[System.Serializable]
public class DummyClass {
// Unique id is assigned by DummyBehaviour and is unique to the game object
// that DummyBehaviour is attached to.
public int UniqueID = -1;
public string Text = "";
// Override GetHashCode so Dictionary lookups
public override int GetHashCode(){
int hash = 17;
hash = hash * 31 + UniqueID;
return hash;
}
// override equality function, allows dictionary to do comparisons.
public override bool Equals(object obj)
{
if (object.ReferenceEquals(obj, null))return false;
DummyClass item = obj as DummyClass;
return item.UniqueID == this.UniqueID;
}
// Allow checks of the form 'if(dummyClass)'
public static implicit operator bool(DummyClass a)
{
if (object.ReferenceEquals(a, null)) return false;
return (a.UniqueID==-1)?false:true;
}
public static bool operator ==(DummyClass a, DummyClass b)
{
if (object.ReferenceEquals(a, null))
{
return object.ReferenceEquals(b, null);
}
return a.Equals(b);
}
public static bool operator !=(DummyClass a, DummyClass b)
{
if (object.ReferenceEquals(a, null))
{
return object.ReferenceEquals(b, null);
}
return !a.Equals(b);
}
}
And my MonoBehaviour:
[ExecuteInEditMode]
public class DummyBehaviour : MonoBehaviour {
public List<DummyClass> DummyClasses = new List<DummyClass>();
// reassign references based on uniqueid.
void Awake(){
Dictionary<DummyClass,DummyClass> dmap = new Dictionary<DummyClass,DummyClass>();
// iterate over all dummyclasses, reassign references.
for(int i = 0; i < DummyClasses.Count; i++){
DummyClass2 d = DummyClasses[i];
if(dmap.ContainsKey(d)){
DummyClasses[i] = dmap[d];
} else {
dmap[d] = d;
}
}
DummyClasses[0].Text = "All items same";
}
// helper function, for inspector contextmenu, to add more classes from Editor
[ContextMenu ("AddDummy")]
void AddDummy(){
if(DummyClasses.Count==0)DummyClasses.Add(new DummyClass{UniqueID = 1});
else {
// Every item after 0 points to zero, serialization will remove refs during deep copy.
DummyClasses.Add(DummyClasses[0]);
}
UnityEditor.EditorUtility.SetDirty(this);
}
}
I am new to MvvmCross and I have a question.
I noticed that the following binding code works in one way only:
{ this, "{'CurrentIndex':{'Path':'CurrentIndex','Mode':'TwoWay'}}" }
CurrentIndex is an Int Property in the View
CurrentIndex is also an Int Property in the ViewModel
This way works!
ViewModel => View
But not this way!
View => ViewModel
I have a collection of ViewControllers and my goal was to call a DeleteCommand for the CurrentIndex in the viewModel.
However,
"Android and Touch 2 way bindings are incomplete"
Reference: MvvmCross experiences, hindsight, limitations?
My guess is the TwoWay mode only works for Controls (UILabel, UITextfield, ...) but not for Properties.
So, is there a good way to make it works in both ways? Or Are there any alternatives to my problem?
Patrick
In order for a binding to transfer any value between a View to a ViewModel, then it needs to hook into some event when the value changes.
In the ViewModel, this event is always the event in the INotifyProperty interface.
In the View/Activity, there is one single pattern employed - so each binding has to hook into a separate event. For example, the Text on EditText is hooked up using the TextChanged event (see MvxEditTextTextTargetBinding.cs) while the value in a SeekBar is hooked up using a Listener object rather than an event (see MvxSeekBarProgressTargetBinging.cs).
So if you wanted to implement this two-way binding for your activity, then you could do this by:
declaring an event - CurrentIndexChanged - in your activity (MyActivity) which is fired whenever CurrentIndex changes
declare a custom binding for your MyActivity which programmatically links CurrentIndex and CurrentIndexChanged
adding the custom binding to the binding registry during Setup
For example, your activity might include:
public event EventHandler CurrentIndexChanged;
private int _currentIndex;
public int CurrentIndex
{
get { return _currentIndex; }
set { _currentIndex = value; if (CurrentIndexChanged != null) CurrentIndexChanged(this, EventArgs.Empty); }
}
And you might then declare a binding class like:
public class MyBinding : MvxPropertyInfoTargetBinding<MyActivity>
{
public MyBinding (object target, PropertyInfo targetPropertyInfo)
: base(target, targetPropertyInfo)
{
View.CurrentIndexChanged += OnCurrentIndexChanged;
}
public override MvxBindingMode DefaultMode
{
get
{
return MvxBindingMode.TwoWay;
}
}
private void OnCurrentIndexChanged(object sender, EventArgs ignored)
{
FireValueChanged(View.CurrentIndex);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (isDisposing)
{
View.CurrentIndexChanged -= OnCurrentIndexChanged;
}
}
}
And you'd need to tell the binding system about this binding in setup like:
registry.RegisterFactory(new MvxSimplePropertyInfoTargetBindingFactory(typeof(MyBinding), typeof(MyActivity), "CurrentIndex"));
However... at a practical level, if you are operating in C# rather than in XML, then you might be better off in this case using C# to simply update the ViewModel rather than using declarative binding in this case.
To be clear... in this case, I would most probably just write the Activity property as:
public int CurrentIndex
{
get { return _currentIndex; }
set { _currentIndex = value; ViewModel.CurrentIndex = value; }
}
Or... I'd consider not having this property in the Activity at all.
If it helps, there's some more information on custom bindings in:
MonoTouch MVVMCross binding to instance variables
In MvvmCross how do I do custom bind properties
Hope this helps! IMHO the bindings are there to help you when you're working in XML - you don't have to use them...
Stuart
UPDATE If you are going to do lots of these and follow the same name pattern - using property named X with changed EventHandler event named XChanged then something like this might work - it uses reflection to find the event automagically:
public class MyBinding<T> : MvxPropertyInfoTargetBinding<T>
where T : class
{
private readonly PropertyInfo _propertyInfo;
private readonly EventInfo _eventInfo;
public MyBinding(object target, PropertyInfo targetPropertyInfo)
: base(target, targetPropertyInfo)
{
_propertyInfo = targetPropertyInfo;
var eventName = _propertyInfo.Name + "Changed";
_eventInfo = View.GetType().GetEvent(eventName);
if (_eventInfo == null)
{
throw new MvxException("Event missing " + eventName);
}
if (_eventInfo.EventHandlerType != typeof(EventHandler))
{
throw new MvxException("Event type mismatch for " + eventName);
}
var addMethod = _eventInfo.GetAddMethod();
addMethod.Invoke(View, new object[] { new EventHandler(OnChanged) });
}
public override MvxBindingMode DefaultMode
{
get
{
return MvxBindingMode.TwoWay;
}
}
private void OnChanged(object sender, EventArgs ignored)
{
var value = _propertyInfo.GetValue(View, null);
FireValueChanged(value);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (isDisposing)
{
var removeMethod = _eventInfo.GetRemoveMethod();
removeMethod.Invoke(View, new object[] { new EventHandler(OnChanged) });
}
}
}