Extend Xamarin.Forms Controls Functionality with Behaviors

MatthewSoucoupMatthewSoucoup USMember, Insider, University, Developer Group Leader mod

Xamarin.Forms Behaviors are little gems of awesomeness allow you to attach additional functionality to a Xamarin.Forms control (actually, any Xamarin.Forms View ) without having to subclass that control or write any messy code in the code-behind page

Posts

  • xxayyxxayy Member ✭✭
    edited March 14

    Great introduction, thanks :)
    Is there any chance to use the ValidValues calculated at runtime instead of being static? The following code does not work
    ValidValues="{Binding ValidRatings}"

    public string[] ValidRatings { get; set; } = {"Good","Magnificent" }; // NON-STATIC

    Throws an XamlParseException "Cannot assign property "ValidValues": Property does not exists ..."
    The docs say this is by design:
    Xamarin.Forms does not set the BindingContext of a behavior, because behaviors can be shared and applied to multiple controls through styles.

    I want to use this for a list of tags. Whenever the user adds a new tag and selects a value that is already assigned to another tag, it should turn red. Thus I have 0 to n Pickers on one page and a dynamically changing list of already selected tags that need to be considered.

    Any chance to achieve this? Thanks

  • xxayyxxayy Member ✭✭

    OK I found a solution by accessing the bindable's BindingContext and call a validate method. My code:
    Changes in PickerColorBehavior

        void Bindable_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Bound and cast to a picker
            if (!(sender is Picker bindable))
                return;
    
            if (!(bindable.BindingContext is TagSelection viewModel))
                return;
    
            if (viewModel.Validate(bindable.SelectedItem))
            {
                IsValid = true;
                bindable.TextColor = ValidColor;
            }
            else
            {
                IsValid = false;
                bindable.TextColor = InvalidColor;
            }
        }
    

    TagSelection manages one picker's data and selection

    public class TagSelection : PickerSelection<Tag>
    {
        private readonly Func<object, int> _validate;
    
        public bool Validate(object o)
        {
            return _validate(o) <= 1;
        }
    
        public TagSelection(ObservableCollection<Tag> allItems, Tag selected, Func<object, int> validate) : base(allItems, selected)
        {
            _validate = validate; // set to ValidateTagSelection see below
        }
        // ...
    }
    

    In the view model of the Page TagSelection objects are constructed with the following delegate:

        private int ValidateTagSelection(object selection)
        {
            if (selection is Tag tag)
            {
                return SelectedTags.Count(s => s.Selected.Id == tag.Id);
            }
            return 0;
        }
    

    This was just a quick hack as proof of concept and hard coding the type TagSelection is bad. So the Validate method should be rather declared in an interface.

    The only thing missing now: I have the same tag selected twiceand the most recently changed one is marked red. If I now change the other one (regular color) to a distinct value, there is no chance for the red marked tag to clear its color.
    Looks like I need to mess around with messaging ... or postpone this feature ;)

  • This behaviours looks excellent but can it handle my issue with the xamarin entry box in that I am using the box to receive characters from a 2D barcode reader. This string includes a (0x1d) character to separate the data fields. Currently the entry control is being stripping the characters from the string.

    I need the entry box to present the RAW input so that I can process the Data Fields correctly. Can a behaviour be used to override the default behaviour of the control?

Sign In or Register to comment.