How to bind to View's layout_weight in MvvmCross? - xamarin.android

What is the easiest way to bind to View's (or any other Android control) weight? Because this property doesn't have a setter, I tried custom binding, but id doesn't seem to work:
public class ViewWeightCustomBinding : MvxAndroidTargetBinding
{
public ViewWeightCustomBinding(object target) : base(target)
{
}
public override Type TargetType
{
get { return typeof (int); }
}
protected override void SetValueImpl(object target, object value)
{
var realTarget = target as View;
if(target == null)
return;
ViewGroup.LayoutParams layoutParameters = realTarget.LayoutParameters;
realTarget.LayoutParameters = new LinearLayout.LayoutParams(layoutParameters.Width, layoutParameters.Height,
(int) value);
}
}
registration in setup:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
registry.RegisterFactory(new MvxSimplePropertyInfoTargetBindingFactory(typeof(ViewWeightCustomBinding), typeof(View), "ViewWeight"));
base.FillTargetFactories(registry);
}
And .axml
<View
android:layout_width="0dp"
android:layout_height="3dp"
android:background="#color/green_holo"
local:MvxBind="ViewWeight Id" />
I can see Waring in debug window:
[0:]
MvxBind:Warning: 5.20 Failed to create target binding for binding ViewWeight for Id
[0:] MvxBind:Warning: 5.20 Failed to create target binding for binding ViewWeight for Id
01-31 10:54:57.247 I/mono-stdout( 3795): MvxBind:Warning: 5.20 Failed to create target binding for binding ViewWeight for Id

MvxSimplePropertyInfoTargetBindingFactory can only be used for real C# properties.
For invented "pseudo" properties, you need to use a custom binding registration like that shown in the n=28 tutorial -
protected override void FillTargetFactories(Cirrious.MvvmCross.Binding.Bindings.Target.Construction.IMvxTargetBindingFactoryRegistry registry)
{
registry.RegisterCustomBindingFactory<BinaryEdit>(
"N28",
binary => new BinaryEditFooTargetBinding(binary) );
base.FillTargetFactories(registry);
}
https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/blob/master/N-28-CustomBinding/CustomBinding.Droid/Setup.cs

Related

Dynamically changing the localization of an app using Avalonia and resource-files is not working

Intro
I'm working on an application and I want to be able to change the language when the app is running. For cross-platform compatibility I'm using AvaloniaUI.
I've found a few helpful articles:
Simple localization in WPF
Simple localization in WPF, extended for multiple resource-files
Answer to question on StackOverflow (basically the first link)
The problem
On startup of the app a binding is created (in LocExtensionWithMultipleResxFiles) between my control on the view and string this[string key] ( in TranslationSourceWithMultipleResxFiles). The app correctly loads the translations on startup.
On my View I have a button, the ClickEvent correctly sets TranslationSourceWithMultipleResxFiles.Instance.CurrentCulture but the text in my view doesn't update. I'm not sure where I did something wrong or if I need to change the code somewhere, so any help is appreciated.
My code
Using the above articles I have the following code:
TranslationSourceWithMultipleResxFiles contains a Dictionary for all the ResourceManagers that are used. string this[string key] returns the translated text. CurrentCulture is the property you set to change the Culture.
public class TranslationSourceWithMultipleResxFiles : INotifyPropertyChanged
{
public static TranslationSourceWithMultipleResxFiles Instance { get; } = new TranslationSourceWithMultipleResxFiles();
private readonly Dictionary<string, ResourceManager> resourceManagerDictionary = new Dictionary<string, ResourceManager>();
// key is the baseName + stringName that is binded to, this returns the translated text.
public string this[string key]
{
get
{
var (baseName, stringName) = SplitName(key);
string? translation = null;
if (resourceManagerDictionary.ContainsKey(baseName))
translation = resourceManagerDictionary[baseName].GetString(stringName, currentCulture);
return translation ?? key;
}
}
// the culture TranslationSourceWithMultipleResxFiles uses for translations.
private CultureInfo currentCulture = CultureInfo.InstalledUICulture;
public CultureInfo CurrentCulture
{
get { return currentCulture; }
set
{
if (currentCulture != value)
{
currentCulture = value;
NotifyPropertyChanged(string.Empty); // string.Empty/null indicates that all properties have changed
}
}
}
// WPF bindings register PropertyChanged event if the object supports it and update themselves when it is raised
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public void AddResourceManager(ResourceManager resourceManager)
{
if (!resourceManagerDictionary.ContainsKey(resourceManager.BaseName))
resourceManagerDictionary.Add(resourceManager.BaseName, resourceManager);
}
public static (string baseName, string stringName) SplitName(string name)
{
int idx = name.LastIndexOf('.');
return (name.Substring(0, idx), name.Substring(idx + 1));
}
}
In xaml you set the Translation.ResourceManager per UserContorl/Window etc. This is used so multiple resource files can be used in the application. Each child Control looks to this ResourceManager for their translations.
public class Translation : AvaloniaObject
{
public static readonly AttachedProperty<ResourceManager> ResourceManagerProperty = AvaloniaProperty.RegisterAttached<Translation, AvaloniaObject, ResourceManager>("ResourceManager");
public static ResourceManager GetResourceManager(AvaloniaObject dependencyObject)
{
return (ResourceManager)dependencyObject.GetValue(ResourceManagerProperty);
}
public static void SetResourceManager(AvaloniaObject dependencyObject, ResourceManager value)
{
dependencyObject.SetValue(ResourceManagerProperty, value);
}
}
Creates a Binding between the Control on the view and the correct ResourceManager.
public class LocExtensionWithMultipleResxFiles : MarkupExtension
{
public string StringName { get; } // Key name of the translation in a resource file.
public LocExtensionWithMultipleResxFiles(string stringName)
{
StringName = stringName;
}
// Find out what ResourceManager this control uses
private ResourceManager? GetResourceManager(object control)
{
if (control is AvaloniaObject dependencyObject)
{
object localValue = dependencyObject.GetValue(Translation.ResourceManagerProperty);
if (localValue != AvaloniaProperty.UnsetValue)
{
if (localValue is ResourceManager resourceManager)
{
TranslationSourceWithMultipleResxFiles.Instance.AddResourceManager(resourceManager);
return resourceManager;
}
}
}
return null;
}
// Create a binding between the Control and the translated text in a resource file.
public override object ProvideValue(IServiceProvider serviceProvider)
{
object? targetObject = (serviceProvider as IProvideValueTarget)?.TargetObject;
if (targetObject?.GetType().Name == "SharedDp") // is extension used in a control template?
return targetObject; // required for template re-binding
string baseName = GetResourceManager(targetObject)?.BaseName ?? string.Empty; // if the targetObject has a ResourceManager set, BaseName is set
if (string.IsNullOrEmpty(baseName)) // if the targetobjest doesnt have a RM set, it gets the root elements RM.
{
// rootObject is the root control of the visual tree (the top parent of targetObject)
object? rootObject = (serviceProvider as IRootObjectProvider)?.RootObject;
baseName = GetResourceManager(rootObject)?.BaseName ?? string.Empty;
}
if (string.IsNullOrEmpty(baseName)) // template re-binding
{
if (targetObject is Control frameworkElement)
baseName = GetResourceManager(frameworkElement.TemplatedParent)?.BaseName ?? string.Empty;
}
// create a binding between the Control and the correct resource-file
var binding = new ReflectionBindingExtension
{
Mode = BindingMode.OneWay,
Path = $"[{baseName}.{StringName}]", // This is the ResourceManager.Key
Source = TranslationSourceWithMultipleResxFiles.Instance,
FallbackValue = "Fallback, can't set translation.",
TargetNullValue = StringName,
};
return binding.ProvideValue(serviceProvider);
}
}
My View
<Window <!-- Standard Window xaml -->
xmlns:l="clr-namespace:TestAppForMVVMwithBaseClasses.Localization"
l:Translation.ResourceManager="{x:Static p:Resources.ResourceManager}">
<StackPanel>
<TextBlock Text="{l:LocExtensionWithMultipleResxFiles String1}"/>
<Button Content="Nl" Click="CurrentCultureNl_Click"/>
<Button Content="En" Click="CurrentCultureEn_Click"/>
</StackPanel>
</Window>

Binding between an Object and a SimpleIntegerProperty

I have a combo box over my GUI in JavaFX.
This Combo Box is composed of a complex type elements :
public class DureeChoiceBoxElement extends ObservableValueBase<DureeChoiceBoxElement> {
private IntegerProperty duree;
#Override
public String toString() {
return duree.get() + " an";
}
}
I want to map (or bind) the selected complex element with my model which contains the simple type :
public class Pel {
private IntegerProperty duree = new SimpleIntegerProperty(1);
public Property<Number> dureeProperty() {
return duree;
}
public void setDuree(Integer duree) {
this.duree.setValue(duree);
}
public Integer getDuree() {
return duree.getValue();
}
}
How to do it ?
I tried in the controller with :
public class PelController {
#FXML
private ChoiceBox<DureeChoiceBoxElement> duree;
//etc..
pel.dureeProperty().bind(createElapsedBindingByBindingsAPI2(duree.getValue()));
/*
* #return an ObjectBinding of immutable TimeElapsed objects for the player
*/
private ObjectBinding<Property<Number>> createElapsedBindingByBindingsAPI2(
final DureeChoiceBoxElement dureeChoiceBoxElement) {
return Bindings.createObjectBinding(new Callable<Property<Number>>() {
#Override
public IntegerProperty call() throws Exception {
return dureeChoiceBoxElement.dureeProperty();
}
}, dureeChoiceBoxElement.dureeProperty());
}
}
But it doesn't work (even not compile). I want to say that "Bind this simple property to this complex Object calling the method I give you through the method named "createElapsedBindingByBindingsAPI2(..)".
It is logical read but I didn't managed to make it works anyway.
That's poor ....
Any help please :).
Example that (obviously) works with legacy code style (Swing coding) :
duree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<DureeChoiceBoxElement>() {
#Override
public void changed(ObservableValue<? extends DureeChoiceBoxElement> observable,
DureeChoiceBoxElement oldValue, DureeChoiceBoxElement newValue) {
// changement durée
log.debug("Durée sélectionnée : {}", duree.getSelectionModel().getSelectedItem().getDuree());
log.debug("Durée bindée ? : {}", pel.getDuree());
pel.setDuree(duree.getSelectionModel().getSelectedItem().getDuree());
}
});
Like this my model is set to selected item. But it implies some boilerplate code. Any better idea based on high level bindings of JavaFX ?

MVVMCross binding text on Ended event UITextField

I am trying to implement a custom binding on a subclass of UITextField so that the bound value is set when the user is done editing instead of with each keystroke because some interim values are invalid in the viewmodel (for example, while setting 'Age' to '26', a value of '2' is invalid so I'd like to wait to set the value until both digits are there). Something similar to setting UpdateSourceTrigger in xaml. I looked at several examples here:
MvvmCross UITextField custom binding is similar, as is MvvmCross: change update source trigger property of binding on MonoDroid (but for Android). I've also watch N=28 custom binding and looked at the source for MvxUITextFieldTextTargetBinding.
I think I'm close, but my custom binding never gets created and the UITextFields in my app still FireValueChanged with every keystroke.
I created the following Custom Binding:
public class UITextFieldFocusChangedBinding : MvxTargetBinding
{
private bool _subscribed;
private UITextField _view;
public UITextFieldFocusChangedBinding(UITextField target) : base(target)
{
_view = target;
}
public override void SetValue(object value)
{
if (_view == null) return;
_view.Text = (string)value;
}
public override void SubscribeToEvents()
{
var view = _view;
if (view == null)
return;
view.Ended += TextFieldOnEnded;
}
private void TextFieldOnEnded(object sender, EventArgs eventArgs)
{
var view = _view;
if (view == null)
return;
if (!view.IsFirstResponder)
FireValueChanged(view.Text);
_subscribed = true;
}
public override Type TargetType
{
get { return typeof(string); }
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.TwoWay; }
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
var view = _view;
if (view != null && _subscribed)
{
view.Ended -= TextFieldOnEnded;
_subscribed = false;
}
}
base.Dispose(isDisposing);
}
}
My setup.cs contains the following:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterPropertyInfoBindingFactory(typeof(Bindings.UITextFieldFocusChangedBinding),typeof(UITextField), "Text");
}
and in my MvxViewController I have:
var set = this.CreateBindingSet<LifeNeedsView, LifeNeedsViewModel>();
set.Bind(_txtFinMedExpenses).To(vm => vm.FinalMedicalExpenses);
set.Apply();
The bindings work (values are passing correctly) but with every keystroke. Any suggestions on what I might be missing?

PowerDesigner addin develop

Anyone knows how to develop an add-in for PowerDesigner? I was reading the document of PowerDesigner about how to create an ActiveX Add-in, it says "The ActiveX must implement a specific interface called IPDAddIn to become a PowerDesigner add-in.". But I don't know where the interface IPDAddIn is, and how to implement it ?
Here is the online document
I have this old example, which could give some ideas, even if not everything it up-to-date.
using PdAddInTypLib;
namespace MineSpace
{
[ComVisible(true)]
[Guid("A6FA0D26-77E8-4DD3-B27E-F4050C3D5188")]
public class Launcher : IPdAddIn {
// Main() manages the console or GUI interface
// the PdAddIn interface is managed by an instance of Launcher
[ComVisible(false)]
[STAThread]
public static void Main(String[] args) {
}
public Launcher() {
_app = null;
}
// IPdAddIn implementation
public void Initialize(Object anApplication) {
try {
_app = (PdCommon.Application)anApplication;
}
catch (Exception e) {
// process
}
}
public void Uninitialize() {
}
public String ProvideMenuItems(String aMenu, Object anObj) {
return "";
}
public int IsCommandSupported(String aMenu, Object anObj, String aCommand) {
return 0;
}
public void DoCommand(String aMenu, Object anObj, String aCommand) {
}
private PdCommon.Application _app;
}
}
with the corresponding part in the class declaration:
[HKEY_CLASSES_ROOT\MyPlugin.Launcher]
#="MyPlugin.Launcher"
[HKEY_CLASSES_ROOT\MyPlugin.Launcher\CLSID]
#="{13749EFC-1ADA-4451-8C47-FF0B545FF172}"
[HKEY_CLASSES_ROOT\CLSID\{13749EFC-1ADA-4451-8C47-FF0B545FF172}]
#="MyPlugin.Launcher"
[HKEY_CLASSES_ROOT\CLSID\{13749EFC-1ADA-4451-8C47-FF0B545FF172}\InprocServer32]
#="C:\windows\System32\mscoree.dll"
"ThreadingModel"="Both"
"Class"="MyPlugin.Launcher"
"Assembly"="MyPlugin, Version=1.0.1402.33688, Culture=neutral, PublicKeyToken=null"
"RuntimeVersion"="v1.0.3705"
[HKEY_CLASSES_ROOT\CLSID\{13749EFC-1ADA-4451-8C47-FF0B545FF172}\ProgId]
#="MyPlugin.Launcher"
[HKEY_CLASSES_ROOT\CLSID\{13749EFC-1ADA-4451-8C47-FF0B545FF172}\Implemented Categories\{62C8FE65-4EBB-45E7-B440-6E39B2CDBF29}]
And the corresponding code to declare the add-in in PowerDesigner. If the File value is present, PowerDesigner could call DllRegisterServer on it, if the component is not yet registered.
[HKEY_LOCAL_MACHINE\SOFTWARE\Sybase\PowerDesigner 10\Addins\MyPlugin Launcher]
"Enable"="No"
"Class"="MyPlugin.Launcher"
"Type"="ActiveX"
"File"="d:\\myplugin\\myplugin.exe"

mvc-mini-profiler, entity framework gives: The space 'SSpace' has no associated collection

I'm trying to use the mvc-mini-profiler in my mvc application. I created a wrapper for my context and Castle Windsor creates the instance. However, I get the error "The space 'SSpace' has no associated collection". The edmx is in assembly A, DigidosEntities in assembly B and this is in assembly C. Any idea what can be the problem? I got the latest version of the profiler.
public interface IDataStore : IDisposable
{
int SaveChanges(int personId);
IObjectSet<TEntity> CreateObjectSet<TEntity>() where TEntity : class;
}
public class ProfiledDigidosEntities : IDataStore, IDisposable
{
private DigidosEntities _context = null;
public ProfiledDigidosEntities()
{
var connectionString = ConfigurationManager.ConnectionStrings["DigidosEntities"].ConnectionString;
var connection = new EntityConnection(connectionString);
var conn = ProfiledDbConnection.Get(connection);
_context = ObjectContextUtils.CreateObjectContext<DigidosEntities>(conn); /* Error: The space 'SSpace' has no associated collection */
}
public void Dispose()
{
if (_context != null)
_context.Dispose();
}
public int SaveChanges(int personId)
{
return _context.SaveChanges(personId);
}
public IObjectSet<TEntity> CreateObjectSet<TEntity>() where TEntity : class
{
return _context.CreateObjectSet<TEntity>();
}
}
Ok, here was my problem: The profiler wants a workspace to make a new profiled connection, the workspace is created through this method (in ObjectContextUtils.cs):
static MetadataCache()
{
workspace = new System.Data.Metadata.Edm.MetadataWorkspace(
new string[] { "res://*/" },
new Assembly[] { typeof(U).Assembly });
}
As you can see it will search in assembly of the type you want to create. Since in my case the type of the model was not in the same assembly, the creation of the workspace failed. Moving the DigidosEntities to the same assembly as the edmx fixed it.

Resources