how to bind an image src to resource drawable image with Mvvmcross? - xamarin.android

I'm trying to bind an image's src.
I have tried using MvxHttpImageView like this
<Mvx.MvxHttpImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/iconeView"
local:MvxBind="{'ImageUrl':{'Path':'ImgSrc'}}" />
with
public string ImgSrc
{
get {return "#res/drawable/icon.png"; }
}
I have tried several other ImgSrc and still don't have any result.
icon.png is in my Resources/Drawable directory and is an AndroidResource
any help will be great !
Thanks

Newer versions of MvvmCross support binding to a drawable resources. Using the DrawableName binding you can bind an ImageView to a property on your viewmodel that contains a drawable name.
using System;
using Cirrious.MvvmCross.ViewModels;
namespace MyApp.ViewModels
{
public class UserProfileViewModel : MvxViewModel
{
// set this to the name of one of the files
// in your Resources/drawable/drawable-xxxx folders
private MyDrawable _myDrawable;
public string MyDrawable {
get { return _myDrawable; }
set {
_myDrawable = value;
RaisePropertyChanged (() => MyDrawable);
}
}
}
}
And in your layout
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
local:MvxBind="DrawableName MyDrawable"/>
Alternatively, you could use the Drawable binding if your VM property is an int

The mvxhttpimageview knows how to load images from http and from 'disk'
Sadly, it doesn't know how to load from resources
However, there are ways to get an image to load static content.
You can write you own custom binding
You can use the standard imageview, images stored in android assets and the 'AssetImagePath' binding built into mvx.
To try the first, take a look at the conference sample - at how the favorite button background is bound to IsFavorite
To do the second:
include the icons in the asset folder - e.g. /assets/icon1.png
make sure the build action is set to AndroidAsset
in the XML use a standard ImageView and binding text like {'AssetImagePath':{'Path':'WhichAsset'}}
In real use, I generally also use a converter - something that maps a viewmodel property like State with a value of LoadingState.Loading to an asset image path like '/loadingimages/loading.png'
You can see the code for the asset binding in https://github.com/slodge/MvvmCross/blob/master/Cirrious/Cirrious.MvvmCross.Binding/Android/Target/MvxImageViewDrawableTargetBinding.cs
Sorry answer doesn't include more code - answering on mobile

I built a converter, which converts a drawable name to a Bitmap and bound the Bitmap to the ImageView.
The converter:
public class ImageNameToBitmapConverter : MvxValueConverter<string, Bitmap>
{
protected override Bitmap Convert(string value, Type targetType, object parameter, CultureInfo culture)
{
var topActivity = Mvx.Resolve<IMvxAndroidCurrentTopActivity>();
var bm = BitmapFactory.DecodeResource(topActivity.Activity.Resources, GetResourceId(value, "drawable", topActivity));
return bm;
}
private static int GetResourceId(string variableName, string resourceName, IMvxAndroidCurrentTopActivity topActivity)
{
try
{
return topActivity.Activity.Resources.GetIdentifier(variableName, resourceName, topActivity.Activity.PackageName);
}
catch (Exception)
{
return -1;
}
}
}
The XML view:
<ImageView
local:MvxBind="Bitmap Icon, Converter=ImageNameToBitmap"
android:layout_width="100dp"
android:layout_height="100dp" />
The Icon property I'm binding is a string like: "icon_name" where I put the image icon_name.png in the Android project's Resources/drawable

For latest MvvmCross next solution is actual:
In markup:
<ImageView
android:id="#+id/iconImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_centerVertical="true"
local:MvxBind="DrawableName Type, Converter=TypeToImageStringConverter" />
In converter:
public class TypeToImageStringConverter : MvxValueConverter<VacationType, int>
{
protected override int Convert(VacationType value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
switch (value)
{
case VacationType.RegularVacation:
return Resource.Drawable.Icon_Request_Green;
case VacationType.SickLeave:
return Resource.Drawable.Icon_Request_Blue;
case VacationType.LeaveWithoutPay:
return Resource.Drawable.Icon_Request_Dark;
case VacationType.OvertimeVacation:
return Resource.Drawable.Icon_Request_Gray;
case VacationType.ExceptionalLeave:
return Resource.Drawable.Icon_Request_Plum;
default:
return Resource.Drawable.Icon_Request_Gray;
}
}
}
All sense is that you just must give out a drawable resource id.

Related

Binding in a Style Setter in WinUI 3

Does WinUI 3 support binding in a Style Setter? I've defined a Style for a NavigationView and the third line is:
<Setter Property="CompactPaneLength" Value="{Binding CurrentCompactPaneLength}" />
This produces a Specified cast is not valid. exception at run time. The DataContext for the page containing the NavigationView is the ViewModel for the page. Both NavigationView.CompactPaneLength and CurrentCompactPaneLength are double and public and CurrentCompactPaneLength is an ObservableObject (from CommunityToolkit.Mvvm.ComponentModel).
The source code for the WinUI 3 (SDK 1.1.2) includes various Setters, such as
<Setter Target="PaneContentGrid.Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=CompactPaneLength}" />
Doing the bindings in code works, if that's what's necessary. But shouldn't the XAML work, too?
Apparently, general bindings in Setters are not supported yet in WinUI 3, although it is a much-requested feature. A workaround is to create a helper class with a DependencyProperty in it that calls a change handler whenever the property is changed/set. The change handler can then create the required binding in code. Kudos to clemens who suggested something like this ages ago for UWP. Here is an example helper class:
internal class BindingHelper
{
#region CompactPaneLengthBindingPath
public static readonly DependencyProperty CompactPaneLengthBindingPathProperty = DependencyProperty.RegisterAttached(
"CompactPaneLengthBindingPath", typeof(string), typeof(BindingHelper), new PropertyMetadata(null, BindingPathChanged));
public static string GetCompactPaneLengthBindingPath(DependencyObject obj)
{
return (string)obj.GetValue(CompactPaneLengthBindingPathProperty);
}
public static void SetCompactPaneLengthBindingPath(DependencyObject obj, string value)
{
obj.SetValue(CompactPaneLengthBindingPathProperty, value);
}
#endregion
#region HeightBindingPath
// another DP is defined here (all of them are strings)
#region ForegroundBindingPath
// and a third one, etc.
// ===================== Change Handler: Creates the actual binding
private static void BindingPathChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is string source) // source property (could add DataContext by setting Value="source#datacontext" for example)
{
DependencyProperty target; // which property is the target of the binding?
if (e.Property == CompactPaneLengthBindingPathProperty) target = NavigationView.CompactPaneLengthProperty;
else if (e.Property == HeightBindingPathProperty) target = FrameworkElement.HeightProperty;
else if (e.Property == ForegroundBindingPathProperty) target = Control.ForegroundProperty;
else throw new System.Exception($"BindingHelper: Unknown target ({nameof(e.Property)}"); // don't know this property
obj.ClearValue(target); // clear previous bindings (and value)
BindingOperations.SetBinding(obj, target, // set new binding (and value)
new Binding { Path = new PropertyPath(source), Mode = BindingMode.OneWay });
}
}
Note that all of the DependencyProperties are of type string and that the target type can be any ancestor type of the control you are working with. For example, the HeightBindingPathProperty binding can be used with any FrameworkElement.
Use the helper in a Style just as you would any Setter, like this:
<Style x:Key="MyNavigationView" TargetType="controls:NavigationView" >
<Setter Property="local:BindingHelper.CompactPaneLengthBindingPath" Value="CurrentCompactPaneLength" />
</Style>
I hope this helps.

Xamarin.Droid Binding Visibility Converter using MvxVisibility wont work

I have tree parts of layout in my file.axml
I wanna show/hide some of them based on a property in my viewmodel
My property is an enumerator, and Iam using an converter to return the visibility based on the enum.
Iam using the type "MvxVisibility" in the core, but for some reason, the android is not understand the return type (visibility)
Here is my android binding:
<LinearLayout
local:MvxBind="Visibility RedeemCodeState, Converter=RedeemStateToVisibility, ConverterParameter=0"
Here is my converter (core)
public class RedeemStateToVisibilityConverter :
MvxValueConverter<RedeemCodeState, MvxVisibility>
{
protected override MvxVisibility Convert(RedeemCodeState mark, Type targetType, object parameter, CultureInfo culture)
{
switch (mark)
{
case RedeemCodeState.RedeemCodeSubmit:
if (parameter.ToString() == "0")
return MvxVisibility.Visible;
return MvxVisibility.Collapsed;
case RedeemCodeState.AudiosSelection:
if (parameter.ToString() == "1")
return MvxVisibility.Visible;
return MvxVisibility.Collapsed;
case RedeemCodeState.Confirmation:
if (parameter.ToString() == "2")
return MvxVisibility.Visible;
return MvxVisibility.Collapsed;
default:
return MvxVisibility.Collapsed;
}
}
}
Here is my ViewModel (core):
public partial class RedeemCodeViewModel
{
private RedeemCodeState _redeemCodeState = 0;
public RedeemCodeState RedeemCodeState
{
get { return _redeemCodeState; }
set
{
_redeemCodeState = value;
RaisePropertyChanged(() => RedeemCodeState);
}
}
public RedeemCodeViewModel(string code)
{
RedeemCode = code;
}
}
public enum RedeemCodeState
{
RedeemCodeSubmit = 0,
AudiosSelection = 1,
Confirmation = 2
}
What Im doing wrong?
Value converters are meant to convert portable values to platform specific values. MvxVisibility is a portable type.
You need to add the Visibility plugins to both your Core and Droid projects. You can call multiple value converters in your binding.
local:MvxBind="Visibility Visibility(RedeemStateToVisibility(RedeemCodeState, 0))"
Note: I'm using Tibet binding syntax
This calls your value converter to convert your code state to a MvxVisibility value. We finally call Visibility value converter to convert that to the Android visibility value.

ActionScript: Correctly casting a bitmap to a class

I have tree instance calling an iconFunction "getIconFromItem".
<mx:Tree dataProvider="{collection}" iconFunction="getIconFromItem" />
The getIconFromItem function is returning null even though the bitmap is not null.
public function getIconFromItem(item:Object):Class {
var result:Class = item.icon as Class ;
return result ;
}
Setting a break point on the return result line reveals that item.icon is a bitmap and result is null.
Any ideas or pointers on how to successfully cast a bitmap as class so that the bitmap is returned as an icon?
Cheers
Keith
The problem here is that item.icon is of type Bitmap, which is not extended from the type Class, but from Object. Whenever you cast to a type that is not in the object's type hierarchy, null is returned.
You want to get the class of the icon, which will be instantiated by the tree control, not the icon itself, so you should change your function to
public function getIconFromItem(item:Object):Class {
return item.icon.constructor as Class;
}
The issue was iconFunction expects a class. Each embedded image has a subclass automatically generated by the compiler. Thanks to #weltraumpirat for pointing me in the right direction :-)
The goal was to dynamically display icons in a tree class. It is possible to modify the class created by Ben Stucki (http://blog.benstucki.net/?p=42) to work with the TreeItemRenderer.data object but this failed when I created a custom MXTreeItemRenderer.
Ended up creating a BitmapImage and a custom TreeItemRenderer which used a prefined iconBitmap attribute within a class to dynamically load icons.
package components
{
import flash.display.BitmapData;
import mx.core.mx_internal;
import spark.core.IContentLoader;
import spark.primitives.BitmapImage;
public class RuntimeBitmapImage extends BitmapImage
{
public function RuntimeBitmapImage()
{
super();
}
public function set bitmapData(bitmapData:BitmapData):void
{
super.setBitmapData(bitmapData, false);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<s:MXTreeItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" >
<fx:Script>
<![CDATA[
override public function set data(value:Object):void {
super.data = value;
if ( value != null ) {
if ( value.iconBitmap != null ) {
runtimeBitmapImage.bitmapData = value.iconBitmap.bitmapData ;
}
}
}
]]>
</fx:Script>
<s:states>
<s:State name="normal" />
<s:State name="hovered" />
<s:State name="selected" />
</s:states>
<s:HGroup left="0" right="0" top="0" bottom="0" paddingTop="2" verticalAlign="middle">
<s:Rect id="indentationSpacer" width="{treeListData.indent}" percentHeight="100" alpha="0">
<s:fill>
<s:SolidColor color="0xFFFFFF" />
</s:fill>
</s:Rect>
<potentiate:RuntimeBitmapImage id="runtimeBitmapImage" left="2" width="18" />
<s:Label id="labelField" text="{treeListData.label}" paddingTop="2"/>
</s:HGroup>
</s:MXTreeItemRenderer>

Localizing enum values in resource bundle

I have a problem with i18n enums in my JSF application. When I started, I had enums with the text defined inside. But now, I have keys tied to message bundles in the enum.
Example one of my enums:
public enum OrderStatus implements CustomEnum {
PENDING("enum.orderstatus.pending"),
CANCELED("enum.orderstatus.canceled");
/**
* key in message bundle
*/
private String name;
OrderStatus(String name) {
this.name = name;
}
#Override
public String getName() {
return name;
}
}
In the view layer, I use something like:
<!-- input -->
<h:selectOneMenu value="#{order.status}">
<f:selectItems value="#{flowUtils.orderStatuses}"/>
</h:selectOneMenu>
<!-- output -->
<h:outputText value="#{order.status}"/>
and in Java:
public class FlowUtils {
public List<SelectItem> getOrderStatuses() {
ArrayList<SelectItem> l = new ArrayList<SelectItem>();
for(OrderStatus c: OrderStatus.values()) {
// before i18n
// l.add(new SelectItem(c, c.getName()));
// after i18n
l.add(new SelectItem(c, FacesUtil.getMessageValue(c.getName())));
}
return l;
}
}
public class FacesUtil {
public static String getMessageValue(String name) {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().getResourceBundle(context, "m").getString(name);
}
}
It worked well, but when I needed to output #{order.status}, I needed to convert it.
So I implemented a converter, but got in trouble with conversion of String to Object in the getAsObject() method.
web.xml:
<converter>
<converter-for-class>model.helpers.OrderStatus</converter-for-class>
<converter-class>model.helpers.EnumTypeConverter</converter-class>
</converter>
Java:
public class EnumTypeConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent comp,
String value) throws ConverterException {
// value = localized value :(
Class enumType = comp.getValueBinding("value").getType(context);
return Enum.valueOf(enumType, value);
}
#Override
public String getAsString(FacesContext context, UIComponent component,
Object object) throws ConverterException {
if (object == null) {
return null;
}
CustomEnum type = (CustomEnum) object;
ResourceBundle messages = context.getApplication().getResourceBundle(context, "m");
String text = messages.getString(type.getName());
return text;
}
}
I'm entangled now with that. Anybody know how to internationalize multiple Enums efficiently?
The value which is passed through the converter is not the option label as you seem to expect, but the option value. The best practice is to not do this in the model side, but in the view side, because the model shouldn't need to be i18n aware.
As to the approach, you're basically unnecessarily overcomplicating things. Since JSF 1.2 there's a builtin EnumConverter which will kick in automatically and since JSF 2.0 you can iterate over a generic array or List in f:selectItems by the new var attribute without the need to duplicate the values over a List<SelectItem> in the model.
Here's how the bean can look like:
public class Bean {
private OrderStatus orderStatus;
private OrderStatus[] orderStatuses = OrderStatus.values();
// ...
}
And here's how the view can look like (assuming that msg refers to the <var> as you've definied in <resource-bundle> in faces-config.xml):
<h:selectOneMenu value="#{bean.orderStatus}">
<f:selectItems value="#{bean.orderStatuses}" var="orderStatus"
itemValue="#{orderStatus}" itemLabel="#{msg[orderStatus.name]}" />
</h:selectOneMenu>
That's all.
Unrelated to the problem, you've typos in the enum name and message keys, it should be:
PENDING("enum.orderstatus.pending"),
CANCELLED("enum.orderstatus.cancelled");
And, more clean would be to keep the bundle keys out the enum and use enum itself as part of bundle key. E.g.
PENDING,
CANCELLED;
<h:selectOneMenu value="#{bean.orderStatus}">
<f:selectItems value="#{bean.orderStatuses}" var="orderStatus"
itemValue="#{orderStatus}" itemLabel="#{msg['enum.orderstatus.' += orderStatus]}" />
</h:selectOneMenu>
enum.orderstatus.PENDING = Pending
enum.orderstatus.CANCELLED = Cancelled
I have posted my solution here: Internationalization of multiple enums (translation of enum values) - but still hoping for further enhancement.
EDIT: with the help of #Joop Eggen, we have come up with a really cool solution:
EDIT again: complete and ready-to-use solution:
Make a class
public final class EnumTranslator {
public static String getMessageKey(Enum<?> e) {
return e.getClass().getSimpleName() + '.' + e.name();
}
}
Make it a custom EL function
<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
version="2.0">
<namespace>http://example.com/enumi18n</namespace>
<function>
<function-name>xlate</function-name>
<function-class>your.package.EnumTranslator</function-class>
<function-signature>String getMessageKey(java.lang.Enum)</function-signature>
</function>
</facelet-taglib>
Add the taglib to your web.xml
<context-param>
<param-name>javax.faces.FACELETS_LIBRARIES</param-name>
<param-value>/WEB-INF/enumi18n.taglib.xml</param-value>
</context-param>
Have properties files enum_en.properties and enum_yourlanguage.properties like this
TransferStatus.NOT_TRANSFERRED = Not transferred
TransferStatus.TRANSFERRED = Transferred
Add the properties files as resource bundles to your faces-config.xml
<resource-bundle>
<base-name>kk.os.obj.jsf.i18n.enum</base-name>
<var>enum</var>
</resource-bundle>
Add the custom taglib to your xhtml files
<html ... xmlns:l="http://example.com/enumi18n">
And - voilĂ  - you can now access the translated enum values in jsf:
<h:outputText value="#{enum[l:xlate(order.transferStatus)]}" />
Well, enum is just another class. There is nothing stopping you from adding parsing and to-string conversion methods that will parse and output locale-sensitive messages.
Maybe it violates Single Responsible Principle (does it?), but I believe making enum responsible for parsing and returning locale-aware values is the right thing to do.
Just add two methods like this:
public String toString(FacesContext context) {
// need to modify the method
FacesUtil.getMessageValue(context, name);
}
public OrderStatus parse(FacesContext context, String theName) {
for (OrderStatus value : values()) {
if (value.toString(context).equals(theName) {
return value;
}
}
// think of something better
return null;
}
I hope I got the code right, as I am not checking it with IDE now... Is this what you were looking for?
I calculate the message key in the enum like as shown below; so no need to maintain the keys with additional attributes on the enum
public String getMessageKey() {
return String.format("enum_%s_%s", this.getClass().getSimpleName(),
this.name());
}
Then I use it like this
<p:selectOneMenu id="type"
value="#{xyzBean.type}" required="true">
<f:selectItems
value="#{xyzBean.possibleTypes}"
var="type" itemLabel="#{msg[type.messageKey]}">
</f:selectItems>
</p:selectOneMenu>
with having configured a org.springframework.context.support.ReloadableResourceBundleMessageSource in the app context
<bean id="msg"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/resources/locale/messages" />
<property name="useCodeAsDefaultMessage" value="true" />
<property name="cacheSeconds" value="1" />
</bean>
In case anyone is looking for a simple utility library to handle enum internationalization, please take a look at https://github.com/thiagowolff/litefaces-enum-i18n
The artifact is also available in Maven Central:
<dependency>
<groupId>br.com.litecode</groupId>
<artifactId>litefaces-enum-i18n</artifactId>
<version>1.0.1</version>
</dependency>
Basically, you just need to add the artifact to your project and define the enum respective keys following the described enum naming conventions. The translations (and also CSS class names) can be retrieved using the provided EL functions.

TextView Compound Drawable MvvmCross Binding

TextView has some DrawableXXX properties like DrawableStart, DrawableEnd, DrawableTop, etc. to show a Drawable next to the TextView text. Is it possible to bind this property using MvvmCross? I've tried using local:MvxBind="Drawablename 'check'" in my binding but it fails showing an MvxBind error:
Failed to create target binding for binding DrawableName for check
If this binding is not implemented in MvvmCross then how would I go about doing this on my own? Is the method used by N+28 still the recommended way to add a custom binding?
There is not currently any MvvmCross define target binding for adding compound drawables. However, you can easily add your own. If you are using MvvmCross 5+ you can take advantage of the new typed custom binding classes. Note, the example below has set the drawable to be placed on the left, however, you can choose to place at any direction (or multiple).
public class CompoundDrawablesDrawableNameBinding : MvxAndroidTargetBinding<TextView, string>
{
public const string BindingIdentifier = "CompoundDrawableName";
public CompoundDrawablesDrawableNameBinding(TextView target) : base(target)
{
}
public override MvxBindingMode DefaultMode => MvxBindingMode.OneWay;
protected override void SetValueImpl(TextView target, string value)
{
var resources = AndroidGlobals.ApplicationContext.Resources;
var id = resources.GetIdentifier(value, "drawable", AndroidGlobals.ApplicationContext.PackageName);
if (id == 0)
{
MvxBindingTrace.Trace(MvxTraceLevel.Warning,
"Value '{0}' was not a known compound drawable name", value);
return;
}
target.SetCompoundDrawablesWithIntrinsicBounds(
left: id,
top: 0,
right: 0,
bottom: 0);
}
}
Then register in your Setup.cs
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterCustomBindingFactory<TextView>(
CompoundDrawablesDrawableNameBinding.BindingIdentifier,
textView => new CompoundDrawablesDrawableNameBinding(textView));
}
Then in your XML you can use
local:MvxBind="CompoundDrawableName <<YOUR PROPERTY TO BIND TO>>"

Resources