Binding the Enabled property of a Android button with the MvvmCross - button

I have a problem when I try to bind the "Enabled" property of my Android Button to a Boolean of my ViewModel using the MvvmCross framework and I really don't know the origin of it.
So I have a ViewModel which contains the two following properties :
private ProjectDetailDTO _projectDetail;
public ProjectDetailDTO ProjectDetail
{
get { return this._projectDetail; }
set
{
_projectDetail = value;
RaisePropertyChanged(() => ProjectDetail);
RaisePropertyChanged(() => HasPicture);
}
}
private bool _hasPicture;
public bool HasPicture
{
get { return ((this.ProjectDetail != null) && !String.IsNullOrEmpty(this.ProjectDetail.Pictures)); }
set { _hasPicture = value;
RaisePropertyChanged(() => HasPicture);
}
}
As you would understand, my button is bind to the HasPicture property. So I have the following code for my button in my .axml file :
<Button
local:MvxLang="Text LblSeePicturesValue"
local:MvxBind="Enabled HasPicture,Click ShowProjectPicturesCommand"
android:id="#+id/buttonPictures"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true" />
I don't think it's a ViewModel problem because my WP application works well with this code. In fact, my ProjectDetailDTO is filled by calling a web service, so by an asynchronous method. I think it's why when the binding is realized the HasPicture property has the false value. But with my ViewModel code, the HasPicture property should be updated when the ProjectDetailDTO is filled. Is there anything I did wrong in my Android View?
Thanks for any help !

I think what you are seeing here is some interaction between ICommand.CanExecute and the Enabled property. There's a discussion about this on https://github.com/MvvmCross/MvvmCross/issues/729
To work around this, try switching the binding to:
local:MvxBind="Click ShowProjectPicturesCommand;Enabled HasPicture"
(Also note that the separator in bindings is a ; - not a ,)

Related

MvvmCross MvxAsyncCommand CanExecute does not disable/enable attached button

I am using MvvmCross in a Xamarin.Forms solution. I have the command attached to the button with the following code
<Button Text="Login" Command="{Binding LoginCommand}"/>
The command is setup in the ViewModel like so
private ICommand _loginCommand;
public ICommand LoginCommand
{
get
{
_loginCommand = _loginCommand ?? new MvxAsyncCommand(Login,
() =>
{
return IsOnline;
});
return _loginCommand;
}
}
I then query when the network connection changes which should in turn change the boolean returned in the CanExecute Func in the MvxAsyncCommand.
{
base.OnlineSignalChanged(isOnline);
(LoginCommand as MvxAsyncCommand)?.RaiseCanExecuteChanged(); //tried calling other methods here as well in case I was calling the wrong thing
}
In that last code block I am able to stop a breakpoint to see that the CanExecute is queried again. However, the button is not enabled/disabled properly.
Is this a know issue with MvvmCross implementation of MvxAsyncCommand? I ended up using the advice of the following thread use John Thiriet version of an AsyncCommand and all works well.
How to implement Async Command

How do you set up Caliburn Micro guard methods for items not in the view?

I'm trying to get Caliburn Micro going in my project and I'm having trouble understanding guard methods (Can*) in the context of properties that aren't bound to the view (forgive me if I'm getting my terminology wrong).
I've adapted Tim Corey's example to add this snippet:
private bool _connected;
public bool Connected
{
get { return _connected; }
set { _connected = value; NotifyOfPropertyChange(() => Connected); }
}
public bool CanSayHello(bool connected)
{
return connected;
}
public void SayHello(bool connected)
{
Console.WriteLine("Hello!");
}
public bool CanClearText(string firstName, string lastName)
{
if (String.IsNullOrWhiteSpace(firstName) && string.IsNullOrWhiteSpace(lastName))
{
Connected = false;
return false;
}
else
{
Connected = true;
return true;
}
}
And the associated xaml is:
<!-- Row 4 -->
<Button x:Name="ClearText" Grid.Row="4" Grid.Column="1">Clear Names</Button>
<Button x:Name="SayHello" Grid.Row="4" Grid.Column="2">Say Hello</Button>
The SayHello button is never enabled (even though it seems like it should have the same state as CanClearText). The intent is to use Connected as an intermediary property; in the application I'm putting together, the idea is that Connected is actually set by an external Message (from a model--not connected to a view).
This question gets to it a bit, but I'd like to avoid explicitly calling NotifyOfPropertyChange(() => CanSayHello); and let the Caliburn Micro framework do the work for me. I'm pretty new to this, and I'm sure there's something simple I'm missing--thanks for your help.
Caliburn Micro, in this particular, case might not provide a easy solution. You would have to trigger the NotifyPropertyChanged manually for the dependend properties like CanSayHello each time Connected Property changes. However, you could make use of Fody Extensions which could do the same for you, without explicitly typing the code.
For example,
[AlsoNotify(nameof(CanSayHello))]
public bool Connected
{
get { return _connected; }
set { _connected = value; NotifyOfPropertyChange(() => Connected); }
}
This, with the help of Code weaving would do the NotifyPropertyChanged for without having to explicitly do so yourself.

Why does setting IsFocused in ViewModel not focus the Entry in the View

It seems like the ViewModel gets an update of the Focused state of an Entry (based on user interaction) but setting a value on the bound property in the ViewModel has no effect.
I have this in my View:
<Entry IsFocused="{Binding Focused, Mode=TwoWay}"/>
And this in my ViewModel:
public bool Focused
{
get
{
return _focused;
}
set
{
_focused = value; // This gets invoked on user interaction with UI, but setting it programatically has no effect on the Entry's focus.
OnPropertyChanged(nameof(Focused));
}
}
Why is the above not working for me?
Is it because IsFocussed is read-only? If so, why am I even allowed to specify Mode=TwoWay?
public bool IsFocused
{
get { return (bool)GetValue(IsFocusedProperty); }
}
You guess right: the IsFocused property is indeed readonly.
They indeed could throw an exception in a setter, but design decisions are tough and must be coherent between all ui elements.

Passing the search term from SearchHandler to ContentPage in Xamarin Forms 4

I'm trying to make use of the new SearchHandler implemented as part of Xamarin Forms 4. I've found it pretty easy so far to get suggestions populated but now I want to raise an event, or follow the suggested method of handling when a search is confirmed.
public class FoodSearchHandler: SearchHandler
{
IFoodDataStore dataStore = new FoodDataStore();
protected override void OnQueryConfirmed()
{
base.OnQueryConfirmed();
// What to do here?
}
protected override void OnQueryChanged(string oldValue, string newValue)
{
base.OnQueryChanged(oldValue, newValue);
if(!string.IsNullOrWhiteSpace(newValue)
{
// Populate suggestions
ItemsSource = dataStore.GetSuggestions(newValue);
}
else
{
ItemsSource = null;
}
}
}
public partial class FoodsPage : ContentPage
{
ObservableCollection<Food> Foods = new ObservableCollection<Food>();
public ItemsPage()
{
InitializeComponent();
// Wire up the search handler
Shell.SetSearchHandler(this, new FoodSearchHandler());
BindingContext = this;
}
}
Unfortunately, althought the alpha docs mention the search handler they don't contain any details on how to use it and the sample apps only demonstrate populating the suggestions.
Does anyone out there have a pointer to offer on how I should be notifying my ContentPage that my SearchHandler confirmed a search?
So, after reading the Shell docs some more, it seems what I want to do in this situation is use of Shell's new Navigation and navigate to a route passing the search text as a query, for example:
protected override void OnQueryConfirmed()
{
base.OnQueryConfirmed();
var shell = Application.Current.MainPage as Shell;
shell.GoToAsync($"app:///fructika/search?query={Query}", true);
}
N.B. It doesn't look like passing data works right now or if it does I'm doing it wrong but I'll raise a separate question about that.

How to sort a JavaFX ListProperty

it seems I have encountered a problem with ListProperties. ObservableLists implement the marker interface SortableList that allows them to be sorted efficiently, firing only a single event. ListProperties do not implement this interface (how could they...?). Instead they use the default implementation of the List interface, firing a lot of changes.
The only solution I can see would be to call sort() on the underlying List directly. But this collides with the scheme of returning the Property itself:
public ObservableList getSomeList()
{
return someListProperty();
}
which ensures that ListChangeListener remain registered when the underlying List is exchanged.
I would be happy to get some input, maybe I missed something?
I guess the SortableList you refer to is the one used in FXCollections.sort.
ListProperty could implement the SortableList interface.
It may indeed be a good idea, since this would allow you to choose the way the wrapped list is sorted, if e.g. FXCollections.sort is used on the property. You could use FXCollections.sort on the contained list in this case.
How could they? Like this:
class MyListProperty<T> extends ListPropertyBase<T> implements SortableList<T> {
...
#Override
public void sort() {
ObservableList<T> list = getValue();
if (list != null) {
FXCollections.sort((ObservableList<Comparable>) list);
}
}
#Override
public void sort(Comparator<? super T> comparator) {
ObservableList<T> list = getValue();
if (list != null) {
FXCollections.sort(list, comparator);
}
}
}
The only problem is, that SortableList is inside the com.sun.javafx.collections package (see It is a bad practice to use Sun's proprietary Java classes?).
About your collision with the property scheme: there is none, if you define the property the intended way, see Using JavaFX Properties and Binding section Understanding Properties
The property would be implemented like this:
private final ListProperty<MyClass> someList = ...;
public ObservableList<MyClass> getSomeList() {
return someList.get();
}
public void setSomeList(ObservableList<MyClass> newList) {
someList.set(newList);
}
public ListProperty<MyClass> someListProperty() {
return someList;
}
The ListProperty has to ensure the ListChangeListeners registered to it receive the change events from the wrapped list.
Maybe you got confused with readonly list properties used in fxml, but a ListProperty is not readonly.
You could still use this property in a fxml file, but you'd need to use a value of type ObservableList:
<!-- imports -->
<ContainingClass xmlns:fx="http://javafx.com/fxml/1">
<someList>
<FXCollections fx:factory="observableArrayList">
<!-- list content goes here -->
</FXCollections>
</someList>
</ContainingClass>

Resources