Windows Runtime dependency properties that depend on each other - windows-runtime

I got a Windows Runtime component authored in C++/CX that contains four dependency properties. Three of those properties set the color channels red, green and blue in the underlying renderer. The C++/C code for one such property looks as follows:
uint8_t DemoControl::Red::get()
{
return static_cast<uint8_t>(GetValue(RedProperty));
}
void DemoControl::Red::set(uint8_t r)
{
SetValue(RedProperty, r);
}
DependencyProperty^ DemoControl::_redProperty =
DependencyProperty::Register("Red",
uint_t::typeid,
DemoControl::typeid,
ref new PropertyMetadata(127, ref new PropertyChangedCallback(&DemoControl::OnRedChanged)));
void DemoControl::OnRedChanged(DependencyObject^ d, DependencyPropertyChangedEventArgs^ e)
{
DemoControl^ DemoControl = static_cast<DemoControl^>(d);
DemoControl->renderer->SetRed(static_cast<uint8_t>(e->NewValue));
}
The fourth property returns the entire color, i.e. it is a combination of the values of the three other properties.
The question is, how would I update that color property if either the red, green or blue property changes without triggering the code attached to the color property via data binding?
A similar question has been asked here but for WPF. The answer suggests to use value coercion but this seems to be a feature unavailable to Windows Runtime components. The PropertyMetadata object used when registering the dependency property does not support CoerceValueCallback from what I can see.

I'm not a C++ expert, but at least for your scenario, I think I can help.
You can register the same callback for multiple Dependency Properties. So in your specific case, you could just have a single OnColorComponentChanged callback:
void DemoControl::OnColorComponentChanged(DependencyObject^ d, DependencyPropertyChangedEventArgs^ e)
{
DemoControl^ DemoControl = static_cast<DemoControl^>(d);
if (e->Property == DemoControl::RedProperty) // Hand Wave Syntax Here
{
DemoControl->renderer->SetRed(static_cast<uint8_t>(e->NewValue));
}
else if (e->Property == DemoControl::GreenProperty)
{
DemoControl->renderer->SetGreen(static_cast<uint8_t>(e->NewValue));
}
else if (e->Property == DemoControl::BlueProperty)
{
DemoControl->renderer->SetBlue(static_cast<uint8_t>(e->NewValue));
}
// Hand Wave Here
DemoControl->Color = MakeColor(DemoControl->Red, DemoControl->Green, DemoControl->Blue);
}

Related

AS3 Objects Issue with Switches

I am having some issues with AS3 Object Arrays. I am trying to make an inventory system, which the user can navigate left and right (which is working). When the user presses ENTER the item should then equip.
I was going to use switch and case to equip the items, as there will only be around 8 items to the game. I get the result [object purpleSword] when using trace, but my switch isn't getting any results or firing anything. I need the equipItem function to find the purpleSword that's ben found in the arrayItems. Items are added to the arrayItems when picked up off the floor.
Does anyone have any tips for using Objects for an RPG inventory system? Many thanks in advance.
public var arrayItems: Array = new Array();
if (keyEvent.keyCode == Keyboard.ENTER) {
if (currentScreen == "inventory") {
if(inventoryCurrent >= 0) {
var actualCurrentItem = inventoryCurrent - 1;
equipItem(arrayItems[actualCurrentItem]);
}
}
}
public function equipItem(itemNumber) {
switch(itemNumber) {
case "purpleSword":
trace("equip purple sword");
break;
}
}
AS3 has a type system, you should use it to help you understand your own errors, and help others understand your code (like us).
Given the fact that you say your output gives you [object purpleSword] I can assume you have a class purpleSword. My guess is that this is an exported symbol, not a .as class file, but it could be either.
But those are all guesses because you haven't provided any type information. For example:
arrayItems:Array could contain anything, and you haven't told us what. You could use a items:Vector.<Object> to store objects, or Vector.<Sprite> to store symbols exported from your Flash library, or better yet create an InventoryItem class and use a Vector.<InventoryItem> to store them.
var actualCurrentItem should be var actualCurrentItem:int
equipItem(itemNumber) should be equipItem(itemNumber:int):void?
If you do this, you will realize (either through your own observation or the compiler telling you) that your equipItem() function is wrong: it expects an itemNumber but it will receive an object.
If my prior assumptions were correct, you could do this:
public var items:Vector.<Object> = new <Object>[];
if (keyEvent.keyCode == Keyboard.ENTER) {
if (currentScreen == "inventory") {
if(inventoryCurrent >= 0) {
var actualCurrentItem:int = inventoryCurrent - 1;
equipItem(items[actualCurrentItem]);
}
}
}
public function equipItem(item:Object):void {
switch(item.constructor) {
case purpleSword:
trace("equip purple sword");
break;
}
}
This works because Object/constructor is a reference to the class of any object, ie purpleSword class, etc. However, you really should use something more concrete than Object, which could be any kind of object and tells you nothing about what kind of properties it might have.

LibGDX assigning a specific shader to a ModelInstance

I have recently been learning an implementing my own shaders in libgdx.
So far I did this with a custom shader provider, which chooses between a few shaders based on the userdata value of the object;
public class MyShaderProvider extends DefaultShaderProvider {
public final DefaultShader.Config config;
final static String logstag = "ME.MyShaderProvider";
//known shaders
static public enum shadertypes {
prettynoise,
invert,
standardlibgdx,
noise,
distancefield,
conceptbeam
}
public MyShaderProvider (final DefaultShader.Config config) {
this.config = (config == null) ? new DefaultShader.Config() : config;
}
public MyShaderProvider (final String vertexShader, final String fragmentShader) {
this(new DefaultShader.Config(vertexShader, fragmentShader));
}
public MyShaderProvider (final FileHandle vertexShader, final FileHandle fragmentShader) {
this(vertexShader.readString(), fragmentShader.readString());
}
public MyShaderProvider () {
this(null);
}
public void testListShader(Renderable instance){
for (Shader shader : shaders) {
Gdx.app.log(logstag, "shader="+shader.getClass().getName());
Gdx.app.log(logstag, "can render="+shader.canRender(instance));
}
}
#Override
protected Shader createShader (final Renderable renderable) {
//pick shader based on renderables userdata?
shadertypes shaderenum = (shadertypes) renderable.userData;
if (shaderenum==null){
return super.createShader(renderable);
}
Gdx.app.log(logstag, "shaderenum="+shaderenum.toString());
switch (shaderenum) {
case prettynoise:
{
return new PrettyNoiseShader();
}
case invert:
{
String vert = Gdx.files.internal("shaders/invert.vertex.glsl").readString();
String frag = Gdx.files.internal("shaders/invert.fragment.glsl").readString();
return new DefaultShader(renderable, new DefaultShader.Config(vert, frag));
}
case noise:
{
return new NoiseShader();
}
case conceptbeam:
{
Gdx.app.log(logstag, "creating concept gun beam ");
return new ConceptBeamShader();
}
case distancefield:
{
return new DistanceFieldShader();
}
default:
return super.createShader(renderable);
}
//return new DefaultShader(renderable, new DefaultShader.Config());
}
}
This seemed to work.
I have an object with a noise shader applied, animated fine.
I have an object with a inverted textured shader, again looking fine.
I have a whole bunch of other objects being rendered with the normal default shader.
It seems the provider as I have set it up is correctly rendering different objects with different shaders based on userData.
However,I recently found a new object I created with a new shader type (ConceptBeamShader) is only being rendered with the Default shader.
The objects user data is set the same as the others;
newlazer.userData = MyShaderProvider.shadertypes.conceptbeam;
However, at no point does the conceptbeamshader get created or used.
In fact createShader() doesn't seem to run for it at all...implying that an existing shader in the shaders array is good enough.
Using the testListShader() function above I see "DefaultShader" is in the "shader" list, which canRender anything, and thus it never gets to creating that new shader I want that object to use :-/
I assume the other shaders only got picked before because those objects were created before DefaultShader got added to that internal shader list.
Surely as soon as a DefaultShader is used, it gets stored in that provider list and will "gobble up" any other shaders. The getShader function in the class MyShaderProvider extends is;
public Shader getShader (Renderable renderable) {
Shader suggestedShader = renderable.shader;
if (suggestedShader != null && suggestedShader.canRender(renderable)) return suggestedShader;
for (Shader shader : shaders) {
if (shader.canRender(renderable)) return shader;
}
final Shader shader = createShader(renderable);
shader.init();
shaders.add(shader);
return shader;
}
As you can see the shaders are looped over and the first one which returns true for "canRender" is used.
So...umm...how exactly are you supposed to say "render this ModelInstance with this shader" ?
None of the tutorials I have read online seemed to cover this - in fact the one on the official site seems to recommend exactly what I am doing so theres clearly something I am missing.
Thanks,
edit
The place it was instanced was asked for. Not sure how this helps but here;
public static MyShaderProvider myshaderprovider = new MyShaderProvider();
Its then assigned to the modelbatch at the games setup
modelBatch = new ModelBatch(myshaderprovider);
As mentioned, my other shaders are working and visible on the objects I assigned the matching userdata too, so I am 99.9% sure the provider is being called and is, at least in some cases, picking the right shader for the right object.
My hunch where its going wrong is as soon as "DefaultShader" gets added to the internal shader list.
There are several ways to specify the Shader to use for a ModelInstance. One of which is to specify the Shader when calling the render method on the ModelBatch:
modelBatch.render(modelInstance, shader);
This will hint the ModelBatch to use this shader, which it will almost always do, unless the specified Shader isn't suitable to render. Whether a Shader is suitable (and should be used) to render the ModelInstance is determined by the call to Shader#canRender(Renderable).
Note the difference between the Renderable and ModelInstance. This is because a single ModelInstance can consist of multiple parts (nodes), each which might need another Shader. For example when you have car model, then it might consist of the opaque chassis and transparent windows. This will require a different shader for the windows and the chassis.
Therefore specifying a Shader for an entire ModelInstance isn't always very useful. Instead you might need to have more control over which Shader is used for each specific part of the model (each render call). For this you can implement the ShaderProvider interface. Which allows you to use whichever Shader you like for each Renderable. Ofcourse you should make sure that the Shader#canRender(Renderable) method of the Shader you use returns true for the specified Renderable.
It can be useful to extend the DefaultShaderProvider so you can fall back on the DefaultShader when you don't need a custom shader. In that case you must make sure that there's an unambiguous and consistent distinction between when the default shader should be used and when a custom shader should be used. That is, the DefaultShader#canRender method should not return true when a custom shader should be used and your customshader#canRender method should not return true when the DefaultShader should be used. (on itself this isn't specific to custom or default shader, you always need to know which shader to use)
You are trying to use ModelInstance#userData to distinct between a custom and default shader. There are two issues with this:
The userData is the same for every Renderable of the ModelInstance. So practically you over complicating your design at no gain. You might as well use modelBatch.render(modelInstance, shader).
The DefaultShader is and can't be aware of any user specific data. It simply looks at the information it is aware of (the material, mesh, environment, etc.) and return true in canRender if it should be used to render based on that info.
To solve the second point, the libGDX 3D API comes with attributes (used for both environment and material). By design these allow you to compare a Shader and Renderable with just two numbers, which are bitwise masks of the attributes. Therefore the preferred, easiest and fastest method is to use a custom attribute. This not only let's you unambiguously identify which shader to use, but also let you specify the required information to use the shader (there's a reason you want to use a different shader).
An example of how to do that can be found here and here.

How to do CreateBindingSet() on Windows Phone?

In the N+1 video #34 (Progress), there was an example of using CreateBindingSet() for the Android version, which is not typical. But the narrator also mentioned briefly that the same can be done on the Windows platform.
As much as I tried, however, I am unable to get a View's property to be bound to its ModelView on the Windows Phone. I always get a NullReferenceException.
The closest I came was the code below, including suggestions from ReSharper. Here's my FirstView.xaml.cs:
using Cirrious.MvvmCross.Binding.BindingContext;
using Whatever.ViewModels;
namespace Whatever {
// inheriting from IMvxBindingContextOwner was suggested by ReSharper also
public partial class FirstView : BaseView, IMvxBindingContextOwner {
public class MyBindableMediaElement
{
private string _theMediaSource = "whatever";
public string TheMediaSource
{
get
{
return _theMediaSource;
}
set
{
_theMediaSource = value;
}
}
}
public FirstView()
{
InitializeComponent();
_mediaElement = new MyBindableMediaElement(this.theMediaElement);
var set = this.CreateBindingSet<FirstView, FirstViewModel>();
// the corresponding view model has a .SongToPlay property with get/set defined
set.Bind(_mediaElement).For(v => v.TheMediaSource).To(vm => vm.SongToPlay);
set.Apply();
}
public IMvxBindingContext BindingContext { get; set; } // this was suggested by ReSharper
}
I get a NullReferenceException in MvxBaseFluentBindingDescription.cs as soon as the view is created. The exact location is below:
protected static string TargetPropertyName(Expression<Func<TTarget, object>> targetPropertyPath)
{
var parser = MvxBindingSingletonCache.Instance.PropertyExpressionParser; // <----- exception here**
var targetPropertyName = parser.Parse(targetPropertyPath).Print();
return targetPropertyName;
}
I have not seen a working example of creating a binding set on a Windows Phone emulator. Has anyone gotten this to work? Thanks.
I can confirm that the narrator said that remark a little too flippantly without actually thinking about how he might do it...
However, with a little effort, you definitely can get the CreateBindingSet to work in Windows if you want to.
Before you start, do consider some alternatives - in particular, I suspect most people will use either Windows DependencyProperty binding or some hand-crafted code-behind with a PropertyChanged event subscription.
If you do want to add CreateBindingSet code to a Windows project then:
Add the Binding and BindingEx assemblies to your Ui project - the easiest way to do this is using nuget to add the BindingEx package.
In your Setup class, override InitializeLastChance and use this opportunity to create a MvxWindowsBindingBuilder instance and to call DoRegistration on that builder. Both these first two steps are covered in the n=35 Tibet binding video - and it's this second step that will initialise the binding framework and help you get past your current 'NullReferenceException' (for the code, see BindMe.Store/Setup.cs)
In your view, you'll need to implement the IMvxBindingContextOwner interface and you'll need to ensure the binding context gets created. You should be able to do this as simply as BindingContext = new MvxBindingContext();
In your view, you'll need to make sure the binding context is given the same DataContext (view model) as the windows DataContext. For a Phone Page, the easiest way to do this is probably just to add BindingContext.DataContext = this.ViewModel; to the end of your phone page's OnNavigatedTo method. Both steps 3 and 4 could go in your BaseView if you intend to use Mvx Binding in other classes too.
With this done, you should be able to use the CreateBindingSet code - although do make sure that all binding is done after the new MvxBindingContext() has been created.
I've not got a windows machine with me right now so I'm afraid this answer code comes untested - please do post again if it does or doesn't work.
I can confirm it works almost perfectly; the only problem is, there are no defaults register, so one has to do the full binding like:
set.Bind(PageText).For(c => c.Text).To(vm => vm.Contents.PageText).OneTime();
to fix this, instead of registering MvxWindowsBindingBuilder, I am registering the following class. Note: I have just created this class, and needs testing.
public class UpdatedMvxWindowsBindingBuilder : MvxWindowsBindingBuilder
{
protected override void FillDefaultBindingNames(IMvxBindingNameRegistry registry)
{
base.FillDefaultBindingNames(registry);
registry.AddOrOverwrite(typeof(Button), "Command");
registry.AddOrOverwrite(typeof(HyperlinkButton), "Command");
//registry.AddOrOverwrite(typeof(UIBarButtonItem), "Clicked");
//registry.AddOrOverwrite(typeof(UISearchBar), "Text");
//registry.AddOrOverwrite(typeof(UITextField), "Text");
registry.AddOrOverwrite(typeof(TextBlock), "Text");
//registry.AddOrOverwrite(typeof(UILabel), "Text");
//registry.AddOrOverwrite(typeof(MvxCollectionViewSource), "ItemsSource");
//registry.AddOrOverwrite(typeof(MvxTableViewSource), "ItemsSource");
//registry.AddOrOverwrite(typeof(MvxImageView), "ImageUrl");
//registry.AddOrOverwrite(typeof(UIImageView), "Image");
//registry.AddOrOverwrite(typeof(UIDatePicker), "Date");
//registry.AddOrOverwrite(typeof(UISlider), "Value");
//registry.AddOrOverwrite(typeof(UISwitch), "On");
//registry.AddOrOverwrite(typeof(UIProgressView), "Progress");
//registry.AddOrOverwrite(typeof(IMvxImageHelper<UIImage>), "ImageUrl");
//registry.AddOrOverwrite(typeof(MvxImageViewLoader), "ImageUrl");
//if (_fillBindingNamesAction != null)
// _fillBindingNamesAction(registry);
}
}
This is a skeleton from Touch binding, and so far I have only updated three controls to test out (Button, HyperButton and TextBlock)

Enumerating class properties

I'm trying to iterate through the properties of a custom class, however, the methods provided by Adobe appear to not work. I'm not receiving compile or run-time errors.
Class
package {
public dynamic class enum {
public var foo:Number = 123;
public function enum() {
this.setPropertyIsEnumerable("foo", true);
if (this.propertyIsEnumerable("foo") == false) {
trace("foo:" + foo + " is not enumerable.")
}
}
}
}
// outputs "foo:bar is not enumerable."
Implementaiton
var test:enum = new enum();
for (var property:String in test) {
trace(property);
}
// outputs nothing
I try to keep my code fast and flexible, so it's really frustrating when you must change the class to Dynamic just to be able to use for ... in on the properties. Jackson Dunstan's testing confirms that this can be 400x slower than static class properties, but those have to be explicitly referenced (impractical for property agnostic methods), or use reflection of the class (computationally expensive) to be accessible.
The only way I've found to sidestep the whole issue is to use dynamically declared variables... which is pointless since at that point using setPropertyIsEnumerable(prop, true) is superfluous; all dynamically created properties already are enumerable. Additionally, dynamic variables cannot be strongly datatyped, and performance goes out the window.
For example...
Class
package {
public dynamic class enum {
public var foo:String = "apple";
public function enum(){
this.dynamicVar = "orange";
this.dynamicProp = "banana";
this.setPropertyIsEnumerable("foo", true);
this.setPropertyIsEnumerable("dynamicProp", false);
}
}
}
Implementation
var test:enum = new enum();
for (var key:String in test) {
trace(key + ": " + test[key]); // dynamicVar: 1
}
// outputs "dynamicVar: orange"
Now that the class is dynamic, we see that only one of our 3 test properties are being iterated. There should be 2.
It almost feels like Adobe wants us to adopt bad programming habits. Words fail me...
Non-dynamic classes do not provide enumerable properties or methods.
As stated in the description of the link you provided.
Sets the availability of a dynamic property for loop operations.
I think you might want to refactor your code on this approach.
I have never had to loop over a classes properties like you are doing here.
If you want to track items dynamically you should use an associative array and track them that way not at the class level like you are doing.
And if you want strong data typing then use a vector.

MvvmCross ViewModel lifecycle during rotation

I have been using Mvvmcross to develop Android application. I am dealing with the issue of ViewModel lifecycle during a rotation. It seems that generally ViewModel is preserved during a rotation. However this is not the case when I present ViewModels in MvxTabActivity. When the rotation happens it always calls a ViewModel constructor.
I have used similar code structure as in N+1 tutorial https://github.com/slodge/NPlus1DaysOfMvvmCross/tree/master/N-25-Tabbed.
Is there a way to modify this tutorial to keep ViewModels in memory during rotation when using MvxTabActivity?
The default ViewModel caching which attempts to workaround the Android rotation behaviour is based around IMvxSingleViewModelCache - so it's not too surprising it can't cope with multiple Activities and multiple ViewModels.
For where this interface is declared and used, see https://github.com/slodge/MvvmCross/search?q=IMvxSingleViewModelCache&ref=cmdform
If this behaviour is troubling you, then you should be able to work around it by one of :
1. Use fragment based tabs rather than Activity based ones
Android handles Fragment lifecycle differently to Activity ones.
2. Or continue using activity based tabs, but implement your own IMvxSingleViewModelCache
It should be simple, for example, to identify your child view models with by their 'Child' naming convention.
With this done you can then implement something like:
public class MyCustomViewModelCache
: IMvxSingleViewModelCache
{
private const string BundleCacheKey = "__mvxVMCacheKey";
private int _counter;
private IMvxViewModel _currentViewModel;
public void Cache(IMvxViewModel toCache, Bundle bundle)
{
if (toCache != null
&& toCache.GetType().Name.StartsWith("Child"))
{
// don't worry about caching child view models
return;
}
_currentViewModel = toCache;
_counter++;
if (_currentViewModel == null)
{
return;
}
bundle.PutInt(BundleCacheKey, _counter);
}
public IMvxViewModel GetAndClear(Bundle bundle)
{
var storedViewModel = _currentViewModel;
_currentViewModel = null;
if (bundle == null)
return null;
var key = bundle.GetInt(BundleCacheKey);
var toReturn = (key == _counter) ? storedViewModel : null;
return toReturn;
}
}
This class based on MvxSingleViewModelCache.cs with just one small addition.
You can register an instance of this class as the IMvxSingleViewModelCache singleton during the InitializeLastChance of your Setup.
Mvx.RegisterSingleton<IMvxSingleViewModelCache>(new MyCustomViewModelCache());
With this done, the home/tab activity should (I think) continue to work - and it'll pass the viewmodels down to the tab children after rotation.
(Other possibilities for IMvxSingleViewModelCache are possible - e.g. it could cache multiple view models - but please don't let it cache too many view models for too long or you may run into 'out of memory' conditions)
3. Or switch the Android rotation handling off
If you add the android:configChanges="orientation" flag (or it's monodroid equivalent Attribute) then you can just handle the rotation yourself.