alexSs alexSs - 1 month ago 14
C# Question

Bind Commands dont work on ReactiveUI 6.5.2 Xamarin whitout XAML

Hi someone help me whit this, I don't know why this error appears.
I have one View whit hard coded in C# and Xamarin Forms without XAML file, on this configuration, I have success on appear the view, but the error has launched exceptions when I try to bind commands to ReactiveUI 6.5.2, below you have the exception, and the code:

UNHANDLED EXCEPTION:


System.Exception: Couldn't find a default property for type Xamarin.Forms.Entry
at ReactiveUI.Reflection.getViewExpressionWithProperty (System.Object view, System.Linq.Expressions.Expression vmExpression) [0x00059]
in /Users/paul/code/reactiveui/reactiveui/ReactiveUI/Reflection.cs:259

AND the code:

public class EntryPageCode : ReactiveContentPage<APISettingsViewModel>
{

StackLayout layout;
public Button SaveUrl { get; protected set; }
public Entry Url{ get; set; }

public EntryPageCode()
{
layout = new StackLayout { Padding = new Thickness(0, 20, 0, 0) };
this.Title = "Settings";

Url = new Entry
{
Text = "",//Here the trick
Placeholder = "API url Please"
};

SaveUrl = new Button
{
Text = "Save",
Font = Font.SystemFontOfSize(NamedSize.Large),
BorderWidth = 1,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.CenterAndExpand
};

Label messagemLbl = new Label
{
Text = "Loading",
Font = Font.BoldSystemFontOfSize(50),
HorizontalOptions = LayoutOptions.Center
};

ActivityIndicator Loading = new ActivityIndicator
{
IsRunning = false,
};

layout.Children.Add(new Label { Text = "URL" });
layout.Children.Add(Url);
layout.Children.Add(SaveUrl);
layout.Children.Add(messagemLbl);
layout.Children.Add(Loading);

this.Content = layout;

this.Bind(ViewModel, vm => vm.Url, Url.Text);
this.BindCommand(ViewModel, vm => vm.SaveUrl, v => v.SaveUrl);

this.WhenAnyValue(x => x.ViewModel.IsLoading)
.ObserveOn(RxApp.TaskpoolScheduler)
.Subscribe(busy =>
{
Url.IsEnabled = !busy;
Loading.IsVisible = busy;
Loading.IsRunning = busy;
});
}

}

public abstract class ReactiveContentPage<TViewModel> : ContentPage, IViewFor<TViewModel> where TViewModel : class
{

public ReactiveContentPage()
{
ViewModel = Activator.CreateInstance<TViewModel>();
SetupUserInterface();
SetupReactiveObservables();
}

protected readonly CompositeDisposable SubscriptionDisposables = new CompositeDisposable();

//public static readonly BindableProperty ViewModelProperty =
// BindableProperty.Create<ReactiveContentPage<TViewModel>, TViewModel>(x => x.ViewModel, null, BindingMode.OneWay);

public static readonly BindableProperty ViewModelProperty =
BindableProperty.Create(nameof(ViewModel), typeof(TViewModel), typeof(ReactiveContentPage<TViewModel>), null, BindingMode.OneWay);

#region IViewFor implementation
public TViewModel ViewModel
{
get
{
return (TViewModel)GetValue(ViewModelProperty);
}
set
{
SetValue(ViewModelProperty, value);
}
}
#endregion

#region IViewFor implementation
object IViewFor.ViewModel
{
get
{
return ViewModel;
}
set
{
ViewModel = (TViewModel)value;
}
}
#endregion

protected virtual void SetupUserInterface() { }

protected virtual void SetupReactiveObservables() { }

protected virtual void SetupReactiveSubscriptions() { }

protected override void OnAppearing()
{
SetupReactiveSubscriptions();

base.OnAppearing();
}

protected override void OnDisappearing()
{
SubscriptionDisposables.Clear();
base.OnDisappearing();
}
}

public class APISettingsViewModel : ReactiveObject
{
//await Navigation.PopAsync();
#region Inital Variables
private string _url;
private Realm _realm;

public ReactiveCommand<string> SaveUrl { get; protected set; }

public string Url
{
get { return _url; }
set { this.RaiseAndSetIfChanged(ref _url, value); }
}

readonly ObservableAsPropertyHelper<bool> _isLoading;
public bool IsLoading
{
get { return _isLoading.Value; }
}
#endregion

#region Constructor
/// <summary>
/// Constructor View Model
/// </summary>
/// <param></param>
public APISettingsViewModel()
{

#region Initialize Varables

_realm = Realm.GetInstance();

var config = _realm.All<APISettings>().Where(d => d.Url != null).FirstOrDefault();
if (config != null)
{
_url = config.Url;
}
#endregion

#region Initialize Rx Settings

var canSave = this.WhenAnyValue(x => x.Url, (ur) =>
{
return !String.IsNullOrWhiteSpace(ur) &&
Regex.IsMatch(ur, @"^(http|https|ftp|)\://|[a-zA-Z0-9\-\.]+\.[a-zA-Z](:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&amp;%\$#\=~])*[^\.\,\)\(\s]$",
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
});

SaveUrl = ReactiveCommand.CreateAsyncTask<string>(canSave,

async _ =>
{
return await RealmSaveUrl();
}
);
SaveUrl.IsExecuting.ToProperty(this, x => x.IsLoading, out _isLoading);
SaveUrl.ThrownExceptions
.Subscribe(ex => UserError.Throw("Error", ex));
SaveUrl.Subscribe(
r =>
{
Debug.WriteLine("See it:" + r.ToString());
}
);

#endregion



}
#endregion

#region Methods
private async Task<string> RealmSaveUrl()
{

try
{
var apiSet = _realm.All<APISettings>().FirstOrDefault();

if (apiSet == null)
{
_realm.Write(() =>
{
apiSet = _realm.CreateObject<APISettings>();
apiSet.Url = Url;
});
}
else
{
_realm.Write(() =>
{
apiSet.Url = Url;
});
}

return apiSet.Url;
}
catch (Exception ex)
{
Debug.WriteLine("Error >> " + ex.StackTrace);
return "IHandleObservableErrors";
}


}

#endregion


}

Answer

You need to push this line on Entry instance:

Url = new Entry
        {
            Text = "",
            Placeholder = "API url Please"
        };

You need to put initial text, to this BindableProperty works, off curse. I will edit the code, for some reason of others have the same problem in the future.