Binding to Properties of Nested CustomViews in a ViewController - xamarin.ios

Given I have the following setup (simplified version, removed logic to add to parent view and constraints etc).
public class TestViewModel : MvxViewModel
{
string _text;
public string Text
{
get => _text;
set
{
_text = value;
RaisePropertyChanged(() => Text);
}
}
}
public class TestViewController : MvxViewController<TestViewModel>
{
CustomViewA customViewA;
public TestViewController()
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
var bindingSet = this.CreateBindingSet<TestViewController, TestViewModel>();
bindingSet
.Bind(customViewA)
.For(v => v.Text)
.To(vm => vm.Text);
bindingSet.Apply();
}
}
public class CustomViewA : UIView
{
CustomViewB customViewB;
public string Text
{
get => customViewB.Text;
set => customViewB.Text = value;
}
}
public class CustomViewB : UIView
{
UITextField textField;
public string Text
{
get => textField.Text;
set => textField.Text = value;
}
}
Why is it that the bindings do not work? Only if I would make the UITextField in CustomViewB public and directly bind to it in the ViewController rather than the public property that directs to the Text property it seems to work. Like so:
bindingSet
.Bind(customViewA.customViewB.textField)
.For(v => v.Text)
.To(vm => vm.Text);
What am I missing here?

It depends on the requirements you have.
Binding in one direction should work (view model-to-view), I have tested your code and when the ViewModel property changes, the change is propagated to CustomViewA and from there to CusomViewB and finally to the UITextField.
However, the problem is with the opposite direction (view-to-view model). When the user updates the text field, its Text property changes. However, there is nothing notified about this change.
Although the property Text points to the text field, it is not "bound" to it, so when TextField's Text changes, the property itself doesn't know about it and neither does the MvvmCross binding.
In fact, MvvmCross binding in the control-to-view model direction is based on the ability to observe an event that tells the binding to check the new value of the bining source. This is already implemented for UITextField's Text, and it hooks up the EditingChanged event (see source code).
You can still make custom bindings work in the view-to-view model direction by implementing them manually. This is described in the documentation.

Related

Binding a button to a different view model

I have a button in View "A" which already has a bindingSet attached to it (it binds to ViewModel "A"). I have button though which needs to be bound to ViewModel "B".
What is the best way to do this?
Your ViewModel is your Model for your View.
If that ViewModel is made up of parts, then that can be done by aggregation - by having your ViewModel made up of lots of sub-models - e.g:
// simplified pseudo-code (add INPC to taste)
public class MyViewModel
{
public MainPartViewModel A {get;set;}
public SubPartViewModel B {get;set;}
public string Direct {get;set;}
}
With this done, then a view component can be bound to direct sub properties as well as sub properties of sub view models:
set.Bind(button).For("Title").To(vm => vm.Direct);
set.Bind(button).For("TouchUpInside").To(vm => vm.A.GoCommand);
set.Bind(button).For("Hidden").To(vm => vm.B.ShouldHideThings);
As long as each part supports INotifyPropertyChanged then data-binding should "just work" in this situation.
If that approach doesn't work for you... In mvvmcross, you could set up a nested class within the View that implemented IMvxBindingContextOwner and which provided a secondary binding context for your View... something like:
public sealed class Nested : IMvxBindingContextOwner, IDisposable {
public Nested() { _bindingContext = new MvxBindingContext(); }
public void Dispose() {
_bindingContext.Dispose();
}
private MvxBindingContext _bindingContext;
public IMvxBindingContext BindingContext { get { return _bindingContext; } }
public Thing ViewModel {
get { return (Thing)_bindingContext.DataContext; }
set { _bindingContext.DataContext = value; }
}
}
This could then be used as something like:
_myNested = new Nested();
_myNested.ViewModel = /* set the "B" ViewModel here */
var set2 = _myNested.CreateBindingSet<Nested, Thing>();
// make calls to set2.Bind() here
set2.Apply();
Notes:
I've not run this pseudo-code, but it feels like it should work...
to get this fully working, you will also want to call Dispose on the Nested when Dispose is fired on your View
given that Views and ViewModels are normally written 1:1 I think this approach is probably going to be harder to code and to understand later.

How do I assign contents of a string to a text field in Unity 4.6?

I am working on a C# class that imports text data from a web site. That works OK. Where I'm losing it is how to display the text in Unity 4.6 once I have all the text in a string variable. Any advice is appreciated.
Unity 4.6 UI system has component named Text. You can watch video tutorial here. Also I suggest you to check out its API.
As always, you have two options on how to add this component to the game object. You can do it from editor (just click on game object you want to have this component in hierarchy and add Text component). Or you can do it from script using gameObject.AddComponent<Text>().
In case you are not familiar with components yet, I suggest you to read this article.
Anyway, in your script you'll need to add using UnityEngine.UI; at the very top of it, because the Text class is in UnityEngine.UI namespace. Ok, so now back to script that will set the value of Text component.
First you need variable that refers to Text component. It can be done via exposing it to editor:
public class MyClass : MonoBehaviour {
public Text myText;
public void SetText(string text) {
myText.text = text;
}
}
And attaching gameObject with text component to this value in Editor.
Another option:
public class MyClass : MonoBehaviour {
public void SetText(string text) {
// you can try to get this component
var myText = gameObject.GetComponent<Text>();
// but it can be null, so you might want to add it
if (myText == null) {
myText = gameObject.AddComponent<Text>();
}
myText.text = text;
}
}
Previous script is not a good example, because GetComponent is actually expensive. So you might want to cache it’s reference:
public class MyClass : MonoBehaviour {
Text myText;
public void SetText(string text) {
if (myText == null) {
// looks like we need to get it or add
myText = gameObject.GetComponent<Text>();
// and again it can be null
if (myText == null) {
myText = gameObject.AddComponent<Text>();
}
}
// now we can set the value
myText.text = text;
}
}
BTW, the patter of ‘GetComponent or Add if it doesn’t exist yet’ is so common, that usually in Unity you want to define function
static public class MethodExtensionForMonoBehaviourTransform {
static public T GetOrAddComponent<T> (this Component child) where T: Component {
T result = child.GetComponent<T>();
if (result == null) {
result = child.gameObject.AddComponent<T>();
}
return result;
}
}
So you can use it as:
public class MyClass : MonoBehaviour {
Text myText;
public void SetText(string text) {
if (myText == null) {
// looks like we need to get it or add
myText = gameObject.GetOrAddComponent<Text>();
}
// now we can set the value
myText.text = text;
}
}
make sure you import the ui library - using UnityEngine.UI
gameObject.GetComponent<Text>().text - replace .text with any other field for UI Text
I assume that the issue is creating dynamic sized "textbox" rather than just assigning the string to a GUIText GameObject. (If not - just put a GUIText GameObject into your scene, access it via a GUIText variable in your script and use myGUIText.text = myString in Start or Update.)
If I am correct in my assumption, then I think you should just be using a GUI Label:
http://docs.unity3d.com/ScriptReference/GUI.Label.html
If you need to split the string up to place text into different labels or GUITexts, you will need to use substrings

Orchard Query For Page with Show on a menu checked

Hi I want to make a "Query" and put a filter for returning all pages that has "Show on a menu" checked. I did not find a way to do that.. Is it possible?
Try something like this:
using Orchard.Localization;
using Orchard.Projections.Descriptors.Filter;
using Orchard.Navigation;
using IFilterProvider = Orchard.Projections.Services.IFilterProvider;
namespace MyProject.Filters
{
public class MenuPartFilter : IFilterProvider {
public Localizer T { get; set; }
public ProductPartFilter() {
T = NullLocalizer.Instance;
}
public void Describe(DescribeFilterContext describe)
{
describe.For(
"Content", // The category of this filter
T("Content"), // The name of the filter (not used in 1.4)
T("Content")) // The description of the filter (not used in 1.4)
// Defines the actual filter (we could define multiple filters using the fluent syntax)
.Element(
"MenuParts", // Type of the element
T("Menu Parts"), // Name of the element
T("Menu parts"), // Description of the element
ApplyFilter, // Delegate to a method that performs the actual filtering for this element
DisplayFilter // Delegate to a method that returns a descriptive string for this element
);
}
private void ApplyFilter(FilterContext context) {
// Set the Query property of the context parameter to any IHqlQuery. In our case, we use a default query
// and narrow it down by joining with the MenuPartRecord.
context.Query = context.Query.Join(x => x.ContentPartRecord(typeof (MenuPartRecord)));
}
private LocalizedString DisplayFilter(FilterContext context) {
return T("Content with MenuPart");
}
}
}

Xamarin.ios initialize UIView

I am using Xamarin.iOS. I have created UIView with a few UITextFields. I am looking for best way to initialize text value in these textfields from code.
I can pass text data in the constructor of UIViewContoller, but I don't have access to textFields inside it (they are null). I can change text value of textFields in viewDidLoad method.
I don't want to create additional fields in controller class to store data passed by constructor and use them in viewDidLoad. Do you know better solution ?
I don't want to create additional fields in controller class to store
data passed by constructor and use them in viewDidLoad.
But that's how it's meant to be done.
Alternatively, you can create less fields/properties in your viewcontroller if you use a MVVM pattern:
public class UserViewModel {
public string Name { get; set;}
public string Title { get; set;}
}
public class UserViewController : UIViewController
{
UserViewModel viewModel;
public UserViewController (UserViewModel viewModel) : base (...)
{
this.viewModel = viewModel;
}
public override void ViewDidLoad ()
{
userName.Text = viewModel.Name;
userTitle.Text = viewModel.Title;
}
}
That's the kind of pattern which gives you a lot of code reuse accross platforms (android, WP, ...) and clearly separate concerns. It's a (very) little bit of extra code, but it's worth every byte.

Compact Framework 3.5 Update Usercontrol With Main Form Variable Data

This seems like it should be simple, however, I cannot find an example through searching...
How do I update a label on a usercontrol using variables from its parent form?
I am building a compact framework 3.5 application.
Thanks for any and all assistance!!!
You can create a public property on the user control, and you update your label from there, for example:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
public string LabelText
{
get
{
return label1.Text;
}
set
{
label1.Text = value;
}
}
}
You can also return the label itself, although some people may say that would break encapsulation on your design.

Resources