Xamarin.Forms.EntryCell: How to trigger the event while input any key - xamarin.forms

I have a problem with Xamarin.Forms.EntryCell. I want to know how to trigger an event if the text of the EntryCell has changed. Not the event 'Completed', it is just triggered when I press Enter after inputting anything.
<EntryCell
Label="User Id"
x:Name="UserIdEntryCell"
HorizontalTextAlignment="End"
Completed="UserIdCompleted"/>

Obviously there is no event that is fired when the text is changed (see the docs). Anyway, this does not mean that you can't achieve what you want, by means of Text property. Since you are using the event I do not assume that you use MVVM (you should really give it a try, though), hence we'll have to create a property we will bind the EntryCell.Text to in your view (I'm assuming a view, but for a page it would be quite similar)
In your code behind add a property Text that calls HandleTextChanged from its setter:
class MyView : ContentView
{
string _text;
public string Text
{
get => _text;
set
{
_text = value;
HandleTextChanged();
}
}
private void HandleTextChange()
{
// do whatever you need to do
}
}
You can bind this property to EntryCell.Text from your XAML
Now MyView.Text will be set every time the text in your EntryCell changes.

Related

Xamarin forms iOS CollectionView steals focus after ItemsSource property is changed

I have a page with a collection view and a search bar that filters its contents.
I want the filtering to happen as the user types in the search bar, so I bind the TextChanged event of the SearchBar to a command in the view model, like so
var eventToCommandBehavior = new EventToCommandBehavior()
{
EventName = nameof(searchBar.TextChanged),
};
eventToCommandBehavior.SetBinding(EventToCommandBehavior.CommandProperty, nameof(MyViewModel.StartOrResetSearchTimerCommand));
searchBar.Behaviors.Add(eventToCommandBehavior);
In the view model:
public ICommand StartOrResetSearchTimerCommand => new Command(() =>
{
StartOrResetSearchTimer();
});
private void StartOrResetSearchTimer()
{
if (!timerStarted)
{
searchTimer = new Timer(_ => PerformSearch(), null, searchTimeout, searchTimeout);
timerStarted = true;
}
else
ResetTimer();
}
private void PerformSearch()
{
//my code
OnPropertyChanged(collectionViewItemsSourceBinding);
}
The StartOrResetSearchTimerCommand filters the ItemsSource binding, and calls OnPropertyChanged(itemsSourceBinding) to update the UI.
On Android and UWP everything works as expected. However on iOS, when OnPropertyChanged is called, the focus moves out of the search bar, resulting to the soft keyboard being closed after each keyboard input.
Has anyone else encountered this? Any suggestions?
I have already tried not using this approach, and only filter the ItemsSource when the search button is pressed, which works, when there is something to search for (ie, there is some input in the search bar)
When the search bar text is empty (ie after Backspace) then the search button is greyed out.
Update
For now, I am using this workaround:
Perfom the search only when the search button is pressed, and on TextChanged, check if the text is empty and reset the ItemsSourceworka
TextChanged is called anytime the text in the query box is changed. You can use this event to update your ItemsSource when the Text of the searchbar changes. You can refer to this part of the official example. First, given your ItemsSource, when you enter text in the searchbar, call the OnTextChanged event to update your ItemsSource, so that the real-time search keyboard will not lose focus.

How to configure ExpressionTextBox bindings / OwnerActivity when used in a dialog?

Our group is working on a Custom Activity Designer around our Email activity. It's a pretty straight forward designer, allow the user to enter settings / creds, but instead of cluttering the activity designer with all the settable options, we thought about putting some settings in a dialog window. (Which opens when you click the button beside the server address box).
Some of our email activity properties are InArguments so we are trying to make use of the ExpressionTextBox to display these values without much luck. The main problem is we aren't sure how to properly set up the binding and the OwnerActivity on the ExpressionTextBox. In the Activity Designer's xaml this is simply done by setting Expression=ModelItem.Property using a converter for the InArgument and setting the OwnerActivity=ModelItem, like this:
<view:ExpressionTextBox HintText="Enter a VB Expression" Expression="{Binding ModelItem.ServerAddress, ConverterParameter=In, Converter={StaticResource ArgumentToExpressionConverter}, Mode=TwoWay}" ExpressionType="{x:Type system:String}" OwnerActivity="{Binding ModelItem}" Margin="2" MaxLines="1" />
If anyone has any ideas on how we could accomplish this in a dialog, please advise.
Well, this is more a WPF\MVVM question than WF4, really.
When developing custom activities designers you just have to keep one thing in mind: any change made on designer\dialog should be reflected on ModelItem. Either through XAML binding expressions or through code on ModelItem.Properties property.
Now, when and how you do it, there are several answers to that but that's really an implementation detail and depends on how you want to do it.
Lets assume you're showing the dialog on button-beside-the-server-address-box click. And lets also assume you've access to dialog textboxes through their name. At that point, you've access to ModelItem so just set its properties as needed:
private void ButtonNextToServerAddressBox_OnClick(object sender, RoutedEventArgs e)
{
var dialog = new ServerAddressEditor();
var result = dialog.ShowDialog();
if (result ?? false)
{
ModelItem.Properties["Server"].SetValue(new InArgument<string>(dialog.ServerTextBox.Text));
ModelItem.Properties["Port"].SetValue(new InArgument<string>(dialog.PortTextBox.Text));
// ... set all other properties
}
}
Now, if you are using any other pattern, or you want pure MVVM, it can be a little more tricky because of how ModelItem works. But this is a totally fine approach.
I resolved this by creating a property in the dialog's ViewModel to hold the Activity Designer's ModelItem.
public ModelItem OwnerActivity {
get { return _OwnerActivity; }
set { _OwnerActivity = value; }
}
vm.OwnerActivity = this.DataContext.ModelItem;
I then set the Xaml for the Expression Text Box in my dialog to binding to this:
<view:ExpressionTextBox HintText="Enter a VB Expression" Expression="
{Binding Path=OwnerActivity.ServerAddress, ConverterParameter=In, Converter=
{StaticResource ArgumentToExpressionConverter}, Mode=TwoWay}" ExpressionType="
{x:Type system:String}" OwnerActivity="{Binding OwnerActivity}" Margin="2"
MaxLines="1" />
Because I'm now binding directly to the ModelItem from the Activity Designer, any change made to the ModelItem property from the dialog is ALWAYS committed, even if you choose to Cancel from the dialog. To wire up the Ok/Cancel buttons so they work accordingly, I did the following in the dialog:
// declare a ModelEditingScope to make changes transactional
private ModelEditingScope _editScope;
// add this to the constructor of the dialog to begin transactional edits on the ModelItem
_editScope = editorViewModel.OwnerActivity.BeginEdit();
// ok & cancel button click event to commit or revert the changes.
private void OK_Click(object sender, RoutedEventArgs e)
{
_editScope.Complete();
this.DialogResult = DialogResult.OK;
this.Close();
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
_editScope.Revert();
this.DialogResult = DialogResult.Cancel;
this.Close()
}

How to select an item of mx:ComboBox on the basis of substring entered through keyboard

I am using <mx:ComboBox /> and I want to select a matching item on the basis of string entered through keyboard. Currently, <mx:ComboBox /> selects the first matching item based on the first character only. I want this functionality to be customized. I am unable to find that KeyboardEvent listener which does the matching so that I can override it.
To do this yourself, you should look at the following bits and pieces of code below from the ComboBox and ListBase classes. ListBase is what the ComboBox component uses for it's drop down list.
The ComboBox appears to be deferring the keyboard input to the drop down list. It then listens for events from the drop down list to know when the selection has changed (as a result of keyboard or mouse input).
Flex components usually override a method called keyDownHandler() to process the keyboard input when they have focus. Starting there, we come across ComboBox line 2231:
// Redispatch the event to the dropdown
// and let its keyDownHandler() handle it.
dropdown.dispatchEvent(event.clone());
event.stopPropagation();
So now the keyDownHandler() in the drop down list will get executed. That method has a giant switch statement, where the default case statement on line 9197 of ListBase looks like this:
default:
{
if (findKey(event.charCode))
event.stopPropagation();
}
This is where the drop down list decides what to select based on keyboard input (when the input is not an arrow key or page up, etc.). The protected findKey() method simply calls the public findString() method to do this work.
So to override this behavior yourself:
extend the ListBase class and override the findKey() or findString() methods with your custom logic
extend ComboBox class and override the createChildren() method so you can instantiate your custom ListBase class instead of the default one.
Here is the class which I've used in order to make it work. searchStr is user inputted string which needs to be matched. If no dataprovider item gets matched to the searchStr, the overridden listener falls back to the default behaviour. I am using Timer to flush the inputted searchStr after 2 seconds. The possible drawback is that it is assuming the dataprovider to be a collection of String values. But you can modify it accordingly as the need may be.
public class CustomComboBox extends ComboBox
{
private var searchStr:String="";
private var ticker:Timer;
public function CustomComboBox()
{
super();
ticker = new Timer(2000);
ticker.addEventListener(TimerEvent.TIMER, resetSearchString);
}
override protected function keyDownHandler(event:KeyboardEvent):void
{
super.keyDownHandler(event);
// code to search items in the list based on user input.
// Earlier, the default behavior shows the matched items in the dropdown, based on first character only.
// user input is invisible to user.
if((event.charCode>=0x20 && event.charCode<=0x7E) || event.charCode==8) //Range of printable characters is 0x20[space] to 0x7E[~] in ASCII. 8 is ASCII code of [backspace].
{
ticker.reset();
ticker.start();
if(event.charCode==8)
{
if(searchStr=="")
return;
searchStr = searchStr.substr(0, searchStr.length-1);
}
else
{
searchStr += String.fromCharCode(event.charCode);
searchStr = searchStr.toLowerCase();
}
for each(var str:String in dataProvider)
{
if(str.toLowerCase().indexOf(searchStr, 0)>-1)
{
this.selectedItem = dropdown.selectedItem = str;
dropdown.scrollToIndex(dropdown.selectedIndex);
break;
}
}
}
}
/**
* reset the search string and reset the timer.
**/
private function resetSearchString(evt:TimerEvent):void
{
searchStr = "";
ticker.reset();
}
}

Why is 'textField' not instantiated when I subclass TextArea in Flex?

I'm experimenting with TextArea and inheritance to implement some additional functionality on the protected textField property.
Unfortunately, when I create a new instance of the subclass, this property is set to null. I'm probably misunderstanding the way super() works, but I thought it would have been instantiated after the constructor finished.
Here's a small snippet of code which extends TextArea:
public final class ExtTextArea extends TextArea {
public function ExtTextArea() {
super();
}
public function testTextField():void {
if (textField == null)
Alert.show("null!");
}
}
}
The invoking code is simple:
var extTextArea:ExtTextArea = new ExtTextArea();
extTextArea.testTextField();
The Alert in ExtTestArea appears every time I run this code.
Why is this? Is there something more I need to do to access the textField property?
Since textField is "the internal UITextField that renders the text of this TextArea" I believe it will remain null until you add it to the display via .addChild(...). I ran a quick test to verify that once I've added it to the display, it is no longer null. You might want to add an event handler to the "creation complete" event and adjust it at that point (I think).
The Flex SDK comes with source code, so you can take a peek and see when this field is initialized. It is not initialized in the constrcutor, but you will see that a new TextField instantiated by createChildren(), which is called when the component is added to a layout container.

FLEX: Programmatically remove Alert?

I need to programmatically remove an alert.
This is why:
My application uses BrowserManager to enable deep linking based off of the content in the #hash part of the url. If an alert is currently up, and the user hits the back button, the application will revert back to its previous state. But the Alert will still be up, and in many cases irrelevant at that point.
So is there a way to programmatically remove the Alert? so when the hash fragment changes I can remove it.
Thanks!
It turns out the Alert.show function returns an Alert reference and then just uses PopUpManager to add it to the display list. so if you capture the return reference when you call Alert.show you can tell PopUpManager to remove it. :)
You can do this by keeping the Alert object as member data, and then setting its visible property to false when you're done with it. Next time you need to show an Alert, don't create a new one - grab the one you've already created and set its properties, then set visible to true again.
private var myAlert : Alert;
public void showAlert( message: String, title : String ) : void
{
hideAlert();
myAlert = Alert.show( message, title, Alert.OK | Alert.NONMODAL );
}
public void hideAlert() : void
{
if( myAlert != null && myAlert.visible ) {
myAlert.visible = false;
}
}
I don't think that is possible.
You can create your own alert component subclassing TitleWindow and then use PopupManager to show/hide them.

Resources