How to „cluster“ XF-controls to a user defined control in code?

FredyWengerFredyWenger CHInsider ✭✭✭✭✭

Hi all

I want to create my own controls with some "clustered" standard-controls for use it on various pages, but don't know, how to do it .
I further need to have access to the events in the own control (e.g. button.clicked(), text.changed, etc.).
I have to do this in code (without xaml).

My Idea:

Example Definition of "control-class":

public class EntryMitLoeschButton : StackLayout 
{
  public EntryMitLoeschButton() // Entry with delete-button
  {
    var sl = new StackLayout { HeightRequest = 50, WidthRequest = 200, Orientation = StackOrientation.Horizontal };
    var MyEntry = new Entry();
    var btLoeschen = new Button();
    btLoeschen.Text = "XX";
    btLoeschen.Clicked += (sender, e) =>
    {
       MyEntry.Text = "";
     };
     sl.Children.Add(MyEntry);
     sl.Children.Add(btLoeschen);
  }
}

On the Page (dont' work):

var objectx = new EntryMitLoeschButton(); // create the "user-object"
PageStack.childred.add(object): // add the user-object to the StackLayout of the page

Eventhandler to Entry MyEntry (in "EntryMitLoeschenButton"):

??xxdont'knowxx??.MyEntry .TextChanged += (sender, e) =>
{// do something};

Questions:
- is that even possible?
- If yes, can somebody drive me in the right direction please?

Thanks a lot for every feedback.

Posts

  • DirkWeltzDirkWeltz DEMember ✭✭✭

    You should use ContentView for this (http://iosapi.xamarin.com/?link=T:Xamarin.Forms.ContentView). You create a ContentView, which Content is your StackLayout and so on. After that, you create your own events. This events are called by the events of the views you added to the Content.

  • DirkWeltzDirkWeltz DEMember ✭✭✭
        public class EntryMitLoeschButton :ContentView 
        {
          public event EventHandler<TextChangedEventArgs> TextChanged;
    
          public EntryMitLoeschButton() // Entry with delete-button
          {
            var sl = new StackLayout { HeightRequest = 50, WidthRequest = 200, Orientation = StackOrientation.Horizontal };
            var MyEntry = new Entry();
            MyEntry.TextChanged += HandleTextChanged;
            var btLoeschen = new Button();
            btLoeschen.Text = "XX";
            btLoeschen.Clicked += (sender, e) =>
            {
               MyEntry.Text = "";
             };
             sl.Children.Add(MyEntry);
             sl.Children.Add(btLoeschen);
             Content = sl;
          }
    
          public void HandleTextChanged(object sender, TextChangedEventArgs args)
          {
            handle = TextChanged;
            if (handle != null)
            {
              handle(sender, args)
            }
          }
        }
    

    I don't have this tested, so be careful ;).

    Another possibility is to make MyEntry as property public with only get, so you could access it from outside.

  • MaxMengMaxMeng NZMember ✭✭✭

    just use this.Children.Add instead of s1.ChildrenAdd

    and create a property public Entry MyEntry {get; set;} then you can add event directly to it.

  • DirkWeltzDirkWeltz DEMember ✭✭✭

    @MaxMeng: The first line doesn't work. If you do it this way, than the Entry isn't in the StackLayout. The second line should be enough.

    @FredyWenger: Both solutions work. For me, the solution with the puplic property is the quick and dirty one. The solution with the content view is one, that I would even give away to others ;).

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭
    edited February 2015

    @MaxMeng:
    First, thanks for your suggestion.
    Unfortunately, it don't work for my needs:
    When I only add:
    public Entry MyEntry {get; set;}
    it don’t work (the object is not created / showed, if I add it to the StackLayout of the Page).

    If I change sl.add to this.add:
    The added controls (Entry and Button) are added to the Page instead to the StackLayout of the “User-Control (at runtime).
    Then the Controls are showed on the Page (but without the StackLayout) and the controls / events to the controls can’t be accessed from the Page (System.NullReferenceException). Only the internal defined event Button-click works.
    So.. this is no solution like needed.

    @Charlenni:
    Strike - sunk!:smiley:
    You have drive me in the correct direction (one more time!).
    => Wenn ich könnte, würde ich Dir ein Bier(fass) spendieren!
    According to your example, I was able to exactly reach my target.
    I have enhanced your code with some parameters, methods and event-handlers and now have what I need.

    As I think that this theme is very important and can save much time and also enhance XF for "new Controls", I post my (pre-final) code below and also attach a .pdf with screenshots, some explanations and also the code (like below).

    In XF there exist an Entry-Control (without any further functionality).
    In XF there further exists a SearchBar-Control which is not consistent over the platforms:

    • In WP, it is only an Entry (without delete-mark and without search-Icon)
    • In Android there is a search-icon, but no delete-mark)
    • In iOS there is a search-icon and a delete-mark, but you can not access the events on an easy way

    As don't need/want a search-icon for my implementation, but want to have a delete-button on all Platforms, I have designed my "Custom Control" exactly for that needs.
    So my "Custom Control" can be used for a "erasable Entry" on all Platforms (including WP).
    If you need further Buttons (e.g. search-button), it should be easy for you, to enhance the example with further functionality.

    So in the .pdf in the attachment, you have all needed information's to implement it for your own needs.
    Hope this helps someone...

    Code:

    Object-Definition:

           public class EntryMitLoeschButton : ContentView
            {
                public event EventHandler<TextChangedEventArgs> TextChanged; // EventHandler TextChanged of the Entry MyEntry
                public event EventHandler TextGeloescht; // Custom EventHandler, that is fired, wenn the delete-button is clicked (to handle it to the instance-object)
                Entry MyEntry;
                public void SetFocus()  // method to set the focus to the Entry (MyEntry) over the instance-object
                {
                    MyEntry.Focus();
                }
                public string Text { get; set; } // Property to access the text in Entry from instance-object
                public EntryMitLoeschButton(String DefaultText, int Breite) // Parameters for set Placeholder-Text of the entry and the width of the Custom.Control
                {
                    // Custom settings to the platforms
                    int ButtonGroesse = 40;
                    int SchriftGroesse = 20;
                    if (Device.OS == TargetPlatform.WinPhone)
                    {
                        ButtonGroesse = 70;
                        SchriftGroesse = 26;
                    };
                    int EntryBreite = Breite - ButtonGroesse; // calculate the width of the Entry depending of parameter width and button-width
                    var sl = new StackLayout { WidthRequest = Breite, Orientation = StackOrientation.Horizontal }; // create base-StackLayout
                    MyEntry = new Entry { WidthRequest = EntryBreite };
                    MyEntry.TextChanged += HandleTextChanged; // redirect EventHandler to HandleTextChanged (see below)
                    MyEntry.Placeholder = DefaultText;  // Overtake the parameter DefaultText
                    var btLoeschen = new Button { WidthRequest = ButtonGroesse, HeightRequest = ButtonGroesse, BackgroundColor = Color.Red, FontAttributes = FontAttributes.Bold, TextColor = Color.White, FontSize = SchriftGroesse };
                    btLoeschen.Text = "X";
                    btLoeschen.Clicked += (sender, e) =>
                    {
                        MyEntry.Text = "";  // delete text of Entry
                        MyEntry.Focus(); // set focus to entry after delete text
                        TextGeloescht(sender, e);  // fire the custom-event TextGeloescht to catch it to the instance-object
                    };
                    sl.Children.Add(MyEntry);  // add Entry to base-StackLayout
                    sl.Children.Add(btLoeschen); // add delete-button to base-StackLayout
                    Content = sl; // set content of ContentView to base-StackLayout
                }
     
    
            public void HandleTextChanged(object sender, TextChangedEventArgs args) // catches TextChanged to Entry (MyENry)
            {
                var handle = TextChanged;
                Text = args.NewTextValue; // Overtake Text in Property (see above)
                if (handle != null)
                {
                    handle(sender, args);
                }
            }
        }
    

    Create an object-instance:

         int Breite = 0;
         if (Device.OS == TargetPlatform.WinPhone)
            { Breite = 400; }
         else
            { Breite = 260; }
           var slEntryFreizeitAngebot = new EntryMitLoeschButton("Bsp. Golf, Squash", Breite);
    

    Listen to the TextChanged-event of the object-instance:

        slEntryFreizeitAngebot.TextChanged += (sender, e) =>
        {
          // do whatever you want…
        };
    

    Listen to the (custom) button-click-event of the object-instance:

        slEntryFreizeitAngebot.TextGeloescht += (sender, e) =>
        {
          // do whatever you want…
        };
    

    Set focus to the Entry in the object-instance:
    slEntryFreizeitAngebot.SetFocus();

  • DirkWeltzDirkWeltz DEMember ✭✭✭

    @FredyWenger: Perfect! And well documented as always. Thank you for sharing.

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @Charlenni:
    Thanks for your positive comment and your startup-help to find the right path - you are a real pathfinder :blush:

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    Additional information’s to the example:

    If you implement the event-hander in the class:

    public event EventHandler TextGeloescht

    You have to define an event handler (if you need it or not) to the generated object (otherwise you will have an exception, as soon as the event fires at runtime .

    In the example above, this is:

    slEntryFreizeitAngebot.TextGeloescht += (sender, e) =>
    {
       // do whatever you want…or nothing, but define the handler!
    };
    

    I further have enhanced my real-life implementation with a workaround for the “WP-keyboard-Enter”-problem (keyboard to an Entry/SearchBar don’t dismiss, if the “Enter-Key” on soft-keyboard is pressed).

    Details to this problem under:
    https://forums.xamarin.com/discussion/33895/how-to-dismiss-keyboard-in-wp-when-enter-key-is-pressed#latest

    Code:

    MyEntry.Completed += (sender, e) =>  // Event is fired, when the “Enter-Key” is pressed
    {
      if (Device.OS == TargetPlatform.WinPhone) // Do it only for WP (as iOS and Android works)
      {
         MyEntry.IsEnabled = false;  // Disable Entry -> Seems to cause the Keyboard to dismiss
         MyEntry.IsEnabled = true; // Re-enable Entry
      }
    

    If you enhance the example with this code (after the MyEntry-definition in the class), the keyboard also should dismiss in WP, as soon as the “Enter-Key” from the soft-keyboard is pressed.

  • DirkWeltzDirkWeltz DEMember ✭✭✭

    @FredyWenger: You have to define an event handler, because you don't check, if it is null before you call it. If you change the handling like this

                btLoeschen.Clicked += (sender, e) =>
                {
                    MyEntry.Text = "";  // delete text of Entry
                    MyEntry.Focus(); // set focus to entry after delete text
                    var handler = TextGeloescht;
                    if (handler != null)
                    {
                             handler(sender, e);  // fire the custom-event TextGeloescht to catch it to the instance-object
                    }
                };
    

    than it works, even if you haven't defined an event handler.

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @Charlenni:
    Thanks once again - I have overlooked that - shame on me :wink:
    So @ all: add the check for !=null above to the example-code ad you don't have to add the event-handler to the instance of the object, if you don't need it.

Sign In or Register to comment.