MVVMCross Android button enable - xamarin.android

I am new to MVVMCross framework for Android and having trouble on how to enable/disable a button. I wasn't able to find a documentation around this area.
Code ViewModel:
private bool _buttonEnabled;
public bool ButtonEnabled
{
get
{ return string.IsNullOrEmpty(EmailLogin);}
set
{
_buttonEnabled = value;
RaisePropertyChanged(() => ButtonEnabled);
}
}
Android axml:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/Login"
android:background="#color/yellow"
local:MvxBind="Enabled(ButtonEnabled)"/>
But unable to get it to work. What I'm trying to achieve is that if EmailLogin variable is NullOrEmpty then button should be disabled. Where am i going wrong ?
Hopefully i'm not pushing this but can i add additional binding to it so that if it is disabled or enabled i can change the color of the button ?

You just need to change the binding syntax to this:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/Login"
android:background="#color/yellow"
local:MvxBind="Enabled ButtonEnabled"/>
I don't know if you are doing it because your code doesn't show everything, but you will also need to call RaisePropertyChanged(() => ButtonEnabled); inside the setter of your EmailLogin property.
Also if you want to change the color of your button, you can install the official color plugin. Usage would be something like this:
private MvxColor _myColor;
public MvxColor MyColor
{
get
{ return _myColor; }
set
{
_myColor = value;
RaisePropertyChanged(() => MyColor);
}
}
And then: local:MvxBind="Enabled ButtonEnabled; TextColor MyColor"

Related

NativeScript - How to get the caret position of TextView in iOS

I'm using NativeScript-Vue and I'm having a problem finding out the caret position of TextView in iOS
<StackLayout>
<TextView ref="input" editable=true #loaded="getView"/>
</StackLayout>
...
getView(args) {
console.log(args.object.ios) // return {}
console.log(this.$refs.input.ios) // return {}
}
...
https://discourse.nativescript.org/t/getting-cursor-position-of-textview-in-ios/5931
I have read the solution above but it seems like only support TypeScript?
What I get from the this.$refs.input.nativeView.ios is always {} (empty)
Does anyone know what I missed? Or any available approach is welcome! Cheers
You have to use this.$refs.input.nativeView.ios only, it may print empty object as it's a native component.
I created a function that allows me to get the selected text. From this function you could also determine where your caret position is.
function getSelectedText(view) {
let selection = {
start: view.ios.selectedRange.location,
end: view.ios.selectedRange.location + view.ios.selectedRange.length,
length: view.ios.selectedRange.length
}
return selection;
}
To use this function just get the refrence of your textfield:
getSelectedText(this.$refs.textField.nativeView);

Close app after closing last fragment

I am writing an app with Xamarin.Android and MvvmCross. I am using fragments and have a "content container" design where I show the fragments in my content_frame view:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center">
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true" />
</FrameLayout>
Everything works, and I can navigate through my app by showing/closing the fragments in my content_frame, but when I close the last fragment, instead of closing the app, it shows a blank screen instead. I reckon I can fix this in my MvxAppCompatViewPresenter class but I don't know how? I currently don't have anything in my view presenter:
public class ViewPresenter : MvxAppCompatViewPresenter
{
public ViewPresenter(IEnumerable<Assembly> androidViewAssemblies) : base(androidViewAssemblies)
{
}
public override void Show(MvxViewModelRequest request)
{
base.Show(request);
}
public override void Close(IMvxViewModel viewModel)
{
base.Close(viewModel);
}
}
Here is the first fragment:
[MvxFragmentPresentation(typeof(LoginViewModel), Resource.Id.content_frame, true)]
[Register("myapp.droid.fragments.LoginSelectionFragment")]
public class LoginSelectionFragment : BaseFragment<LoginSelectionViewModel>
{
protected override int FragmentId => Resource.Layout.fragment_login_selection;
}
One of the approaches you can use, is to not add the fragments that are starting fragments to the backstack by setting the MvxFragmentPresentation property AddToBackStack to false (false is also the default if no parameter is passed).
The idea there is that for the first fragment you would rely on the activity being added to the backstack. Essentially, the first fragment and the activity could then be considered the same with respect to the backstack, eliminating the blank screen.
However, this would only work if the starting fragments do not need to be added to the backstack, with in the same activity context, later in the navigational flow. In future versions of MvvmCross you would easily be able to overcome this limitation via the updated IMvxOverridePresentationAttribute.

ImageView in ListView briefly shows previous image on scroll with RecycleElement enabled

I have a list of the following class:
public class Set {
public string IconUrl { get; set; }
}
This list is bound to a ListView:
<ListView ItemsSource="{Binding Sets}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Image Source="{Binding IconUrl}" />
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
When the view loads and the user starts scrolling, the cells are reused and the Image briefly shows the previous image before the new image is downloaded and rendered.
Is there a way to prevent this kind of behavior without disabling RecycleElement?
I haven't tried this but on ViewCell you have Disappearing and Appearing events that you can hook into.
You may want to look at releasing the image source on the Disappearing event handler, but sometimes this can occur sometime later I think from recollection, so you may also want to try releasing the image on the Appearing event handler that hopefully will be executed prior to the display on the screen?
We have solved this by manually setting the Image source to null to force the render of the new images. we achieve this by using OnBindingContextChanged Event of the ListView. Hope this helps.
Edited (Added code below):
We have something like this:
public class PeopleCell : ViewCell
{...
Image profileImage;
public PeopleCell()
{
profileImage = new Image
{
VerticalOptions = LayoutOptions.CenterAndExpand,
HorizontalOptions = LayoutOptions.CenterAndExpand,
BackgroundColor = Color.FromHex("f5f5f5"),
Source = ImageSource.FromFile("profile_blankimage"),
};...
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
people = BindingContext as CustomerViewModel;
if(people.Customer.Avatar != null)
profileImage.Source = ImageSource.FromUri(new Uri(people.Customer.Avatar.Url));

How do I prevent one specific character to be entered into a UITextView (in Xamarin)?

I need to prevent users from entering a caret ("^") into a notes field that is implemented in a UITextView. I found this question: prevent lower case in UITextView, but it's not clear to me when/how often the shouldChangeTextInRange method will be called. Is it called for each keystroke? Is it named this way because it will be called once for a paste? Instead of preventing the entire paste operation, I'd rather strip out the offending carets, which it doesn't look like that method can do.
Our main application (written in C++Builder with VCL components) can filter keystrokes, so that if ^ is pressed, it beeps and the character is not added to the text field. I would like to replicate that behavior here.
Is there any way to do that sanely in Xamarin? I'm doing iOS first, and might be asking about Android later.
Thanks for your help!
Are you using Xamarin.Forms to build your UI? If you're going to be targeting Android, I highly recommend doing so.
If that is the case, then you can easily do this with a custom Entry subclass:
public class FilteredEntry : Entry
{
private string FilterRegex { get; set; }
public FilteredEntry (string filterRegex)
{
// if we received some regex, apply it
if (!String.IsNullOrEmpty (filterRegex)) {
base.TextChanged += EntryTextChanged;
FilterRegex = filterRegex;
}
}
void EntryTextChanged (object sender, TextChangedEventArgs e)
{
string newText = e.NewTextValue;
(sender as Entry).Text = Regex.Replace (newText, FilterRegex, String.Empty);
}
}
Usage:
// The root page of your application
MainPage = new ContentPage {
Content = new StackLayout {
VerticalOptions = LayoutOptions.Center,
Children = {
new FilteredEntry(#"\^")
}
}
};
A typed ^ will be stripped out of the Entry's Text.

Nested Silverlight Datagrid - Row Details works great, but I want a button!

I'm using a silverlight 3 datagrid, and within it, I'm nesting related records in another control by using the rowdetails (visibilitymode = visiblewhenselected).
I really like how this works, but I'd much rather have the grid display the row details when a "+" button is pressed, much as a tree will expand when you click a node.
I tried programmatically defining the template by using resources like this:
<Grid.Resources>
<DataTemplate x:Key="EmptyTemplate">
<StackPanel>
<!--<TextBlock Text="Empty Template!!!" />-->
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="SongTemplate">
<StackPanel>
<AdminControls:ArtistSongControl x:Name="ArtistSongControl" />
</Stack>
</DataTemplate>
</Grid.Resources>
And in the grid's LoadingRowDetails event, I'd choose which template to set by:
e.Row.DetailsTemplate = (DataTemplate)LayoutRoot.Resources["SongTemplate"];
This sortof worked, but I found that I had problems with collapsing previous rows details template, and even crashed ie8 (not sure if that's related).
Basically, I really like how the silverlight 3 datagrid works, and even how the rowdetailstemplate stuff is implemented. I simply would like to defer loading any details until a row is expanded purposely (as a tree would be). All of the 3rd party grids seem to do this, and microsoft's is soooo close. Does anyone have any idea how to solve this one?
Thanks, Dennis
Dennis,
In case you haven't already found an answer to this, I wanted the same behavior and solved it by customizing the RowHeaderTemplate, which lets you throw a button in the header for each row. Then I implemented a handler for the button like so:
private void ToggleButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
ToggleButton button = sender as ToggleButton;
DataGridRow row = button.GetVisualAncestorOfType<DataGridRow>();
if (button.IsChecked == true)
{
row.DetailsVisibility = Visibility.Visible;
//Hide any already expanded row. We only want one expanded at a time for simplicity and
//because it masks a virtualization bug in the datagrid.
if (_expandedRow != null)
_expandedRow.DetailsVisibility = Visibility.Collapsed;
_expandedRow = row;
}
else
{
row.DetailsVisibility = Visibility.Collapsed;
_expandedRow = null;
}
}
Note that GetVisualAncestorOfType<> is an extension method I've implemented to dig into the visual tree.
You'll also need to set the datagrid's HeadersVisibility property to Row or All
here is another way to achieve what you are trying to do:
In the DataGrid set up a LoadingRow Event like this:
<data:DataGrid LoadingRow="ItemsGrid_LoadingRow" .....
In the DataGrid create a Template Column which will contain a Button such as the following:
<data:DataGridTemplateColumn CellStyle="{StaticResource DataGridCellStyle1}" CanUserReorder="False">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="ViewButton" Click="ToggleRowDetailsVisibility" Cursor="Hand" Content="View Details" />
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
In the LoadingRow Event locate the button that is (in this case) stored in the first column of the DataGrid, then store the current DataGridRow into the buttons Tag element
private void ItemsGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
var ViewButton = (Button)ItemsGrid.Columns[0].GetCellContent(e.Row).FindName("ViewButton");
ViewButton.Tag = e.Row;
}
In the Buttons EventHandler (in this case ToggleRowDetailsVisibility) we will extract the Row so that we can toggle its DetailsVisibility
In the LoadingRow Event locate the button that is (in this case) stored in the first column of the DataGrid, then store the current DataGridRow into the buttons Tag element
private void ToggleRowDetailsVisibility(object sender, RoutedEventArgs e)
{
var Button = sender as Button;
var Row = Button.Tag as DataGridRow;
if(Row != null)
{
if(Row.DetailsVisibility == Visibility.Collapsed)
{
Row.DetailsVisibility = Visibility.Visible;
//Hide any already expanded row. We only want one expanded at a time for simplicity and
//because it masks a virtualization bug in the datagrid.
if (CurrentlyExpandedRow != null)
{
CurrentlyExpandedRow.DetailsVisibility = Visibility.Collapsed;
}
CurrentlyExpandedRow = Row;
}
else
{
Row.DetailsVisibility = Visibility.Collapsed;
CurrentlyExpandedRow = null;
}
}
}
You will notice that "CurrentlyExpandedRow", this is a Global variable, of type DataGridRow, that we store the currently expanded row in, this allows us to close that Row when a new one is to be opened.
Hope this helps.
In addition to the answer uxrx provided here is the code for finding an ancestor
public static partial class Extensions
{
public static T FindAncestor<T>(DependencyObject obj) where T : DependencyObject
{
while (obj != null)
{
T o = obj as T;
if (o != null)
return o;
obj = VisualTreeHelper.GetParent(obj);
}
return null;
}
public static T FindAncestor<T>(this UIElement obj) where T : UIElement
{
return FindAncestor<T>((DependencyObject)obj);
}
}
For this:
DataGridRow row = button.GetVisualAncestorOfType<DataGridRow>();
We can use as:
HyperlinkButton button = sender as HyperlinkButton;
DataGridRow Row = DataGridRow.GetRowContainingElement(button)
I suggest you take a look at the RowVisibilityChanged event on the datagrid, basically when the row visibility changes to "Visible", then load the info for the row.

Resources