0

I have created a custom control(list) with an image, 2 labels and a button. I want to invoke button click of the custom control. My whole control will also have a separate event. enter image description here

Here is my usercontrol tamplate code:

namespace __
{
    public class SelectMultipleBasePage<T> : ContentPage
    {

        public class WrappedSelection<T> : INotifyPropertyChanged
        {
            public T Item { get; set; }
            bool isSelected = false;
            public bool IsSelected
            {
                get
                {
                    return isSelected;
                }
                set
                {
                    if (isSelected != value)
                    {
                        isSelected = value;
                        PropertyChanged(this, new PropertyChangedEventArgs("IsSelected"));
                        //                      PropertyChanged (this, new PropertyChangedEventArgs (nameof (IsSelected))); // C# 6
                    }
                }
            }
            public event PropertyChangedEventHandler PropertyChanged = delegate { };
        }
        public class BackGroundColorConverter : IValueConverter
        {
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                if (value is bool)
                {
                    if ((bool)value)
                    {
                        return Color.FromHex("#DEE4EA");
                    }
                    else
                    {
                        return Color.White;
                    }
                }
                else
                {
                    return Color.White;
                }
            }

            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                throw new NotImplementedException();
            }

        }

        public class WrappedItemSelectionTemplate : ViewCell
        {
             static int i=0; // don't forget to make it static 
            public WrappedItemSelectionTemplate()
                : base()
             {

                 var items = RecordingListPage.items;
                 Grid objGrid = new Grid();
                 objGrid.RowDefinitions.Add(new RowDefinition
                 {
                     Height = new GridLength(1, GridUnitType.Star)
                 });
                 objGrid.ColumnDefinitions.Add(new ColumnDefinition
                 {
                     Width = new GridLength(75, GridUnitType.Absolute),
                 });
                 objGrid.ColumnDefinitions.Add(new ColumnDefinition
                 {
                     Width = new GridLength(1, GridUnitType.Star)
                 });
                 objGrid.ColumnDefinitions.Add(new ColumnDefinition
                 {
                     Width = new GridLength(75, GridUnitType.Absolute),
                 });

                 // Column 1:-
                 Image objImage = new Image();
                 objImage.SetBinding(Image.SourceProperty, new Binding("Item.Image"));
                 objGrid.Children.Add(objImage, 0, 0);
                 // Column 2:-
                 StackLayout objStackLayoutCol2 = new StackLayout();
                 objGrid.Children.Add(objStackLayoutCol2, 1, 0);

                 Label name = new Label()
                 {
                     Text = "Name",
                     Style = (Style)Application.Current.Resources["LabelStyle"],
                 };
                 Label date = new Label()
                 {
                     Text = "Date",
                     Style = (Style)Application.Current.Resources["LabelStyleTiny"]
                 };
                 name.SetBinding(Label.TextProperty, new Binding("Item.Name"));
                 date.SetBinding(Label.TextProperty, new Binding("Item.Date"));
                 objStackLayoutCol2.Children.Add(name);
                 objStackLayoutCol2.Children.Add(date);
                 objStackLayoutCol2.Padding = new Thickness(10);

                 Label objImageView = new Label();
                 objImageView.Text = FontAwesome.FAPencilSquareO;
                 objImageView.FontSize = 35;
                 objImageView.VerticalOptions = LayoutOptions.CenterAndExpand;
                 objImageView.HorizontalOptions = LayoutOptions.CenterAndExpand;
                 objImageView.TextColor = Color.Black;

                 StackLayout stv = new StackLayout();
                 stv.Children.Add(objImageView);
                 stv.Padding = new Thickness(10);
                 stv.HorizontalOptions = LayoutOptions.Center;
                 stv.VerticalOptions = LayoutOptions.Center;

                 objImageView.StyleId = items[i].Id.ToString();
                 i++;
                 if (i == items.Count)
                 {
                     i = 0;
                 }

                 var tapGestureRecognizer = new TapGestureRecognizer();
                  tapGestureRecognizer.Tapped += OnImageBtnTapped;
                 objImageView.GestureRecognizers.Add(tapGestureRecognizer);

                 objGrid.Children.Add(stv, 2, 0);

                 var moreAction = new MenuItem { Text = "More" };

                 moreAction.SetBinding(MenuItem.CommandParameterProperty, new Binding("."));
                 moreAction.Clicked += (sender, e) =>
                 {
                     var mi = ((MenuItem)sender);
                     //Debug.WriteLine("More Context Action clicked: " + mi.CommandParameter);
                 };

                 var deleteAction = new MenuItem { Text = "Delete", IsDestructive = true }; // red background
                 deleteAction.Icon = Device.OnPlatform("Icons/cancel.png", "cancel.png", "Images/cancel.png");
                 deleteAction.SetBinding(MenuItem.CommandParameterProperty, new Binding("."));
                 deleteAction.Clicked += (sender, e) =>
                 {
                     var mi = ((MenuItem)sender);
                     //Debug.WriteLine("Delete Context Action clicked: " + mi.CommandParameter);
                 };

                 //
                 // add context actions to the cell
                 //
                 ContextActions.Add(moreAction);
                 ContextActions.Add(deleteAction);
                 //objGrid.Padding = new Thickness(10);

                 StackLayout st = new StackLayout();
                 st.Children.Add(objGrid);
                 st.Children.Add(new BoxView() { Color = Color.FromHex("#A4B3C1"), WidthRequest = 100, HeightRequest = 1 });


                 View = st;


             }

            private void OnImageBtnTapped(object sender, EventArgs e)
            {
                var tappedImage = (Label)sender;
                var ImageId = Convert.ToInt32(tappedImage.StyleId);
                Application.Current.Properties["ItemId"] = ImageId;
                MessagingCenter.Send(new RedirectClass.OpenRecordingDetails(), RedirectClass.OpenRecordingDetails.Key);
            }

        }
        public static List<WrappedSelection<T>> WrappedItems = new List<WrappedSelection<T>>();
        public SelectMultipleBasePage(List<T> items)
        {
            WrappedItems = items.Select(item => new WrappedSelection<T>() { Item = item, IsSelected = false }).ToList();
            ListView mainList = new ListView()
            {
                ItemsSource = WrappedItems,
                ItemTemplate = new DataTemplate(typeof(WrappedItemSelectionTemplate)),
            };

            mainList.ItemSelected += (sender, e) =>
            {
                if (e.SelectedItem == null) return;
                var o = (WrappedSelection<T>)e.SelectedItem;
                o.IsSelected = !o.IsSelected;
                ((ListView)sender).SelectedItem = null; //de-select
            };
            Content = mainList;
            mainList.HasUnevenRows = true;


            if (Device.OS == TargetPlatform.WinPhone)
            {   // fix issue where rows are badly sized (as tall as the screen) on WinPhone8.1
                mainList.RowHeight = 40;
                // also need icons for Windows app bar (other platforms can just use text)
                ToolbarItems.Add(new ToolbarItem("All", "check.png", SelectAll, ToolbarItemOrder.Primary));
                ToolbarItems.Add(new ToolbarItem("None", "cancel.png", SelectNone, ToolbarItemOrder.Primary));
            }
            else
            {
                ToolbarItems.Add(new ToolbarItem("All", null, SelectAll, ToolbarItemOrder.Primary));
                ToolbarItems.Add(new ToolbarItem("None", null, SelectNone, ToolbarItemOrder.Primary));
            }
        }
        void SelectAll()
        {
            foreach (var wi in WrappedItems)
            {
                wi.IsSelected = true;
            }
        }
        void SelectNone()
        {
            foreach (var wi in WrappedItems)
            {
                wi.IsSelected = false;
            }
        }
        public static List<T> GetSelection()
        {
            return WrappedItems.Where(item => item.IsSelected).Select(wrappedItem => wrappedItem.Item).ToList();
        }
    }
}

I have an event OnImageBtnTapped which I want to access from my content page. How can I do that?

EDIT

Thanks to @Jason, I managed to get event click function in the code that is using the control.

In the custom control class:

private void OnImageBtnTapped(object sender, EventArgs e)
{
   if (OnImageSelected != null) 
   {
        OnImageSelected(sender,e);
   }
}

In the code that is using the control,

SelectMultipleBasePage<ListItems>.OnImageSelected += ListPage_OnImageSelected;

void ListPage_OnImageSelected(object sender, EventArgs e)
{

    var tappedImage = (Label)sender;
    var ImageId = Convert.ToInt32(tappedImage.StyleId);

    Application.Current.Properties["ItemId"] = ImageId;

    MessagingCenter.Send(new RedirectClass.OpenRecordingDetails(), RedirectClass.OpenRecordingDetails.Key);
}

2 Answers 2

4

this is no different than creating a custom event for any C# code

First, create a public event and handler in your class:

// note: you may want to create your own ImageEventArgs class that inherits from EventArgs
public delegate void ImageSelectedHandler(object sender, EventArgs e);
public event ImageSelectedHandler OnImageSelected;

next, inside the control you can call the handler if it exists

private void OnImageBtnTapped(object sender, EventArgs e)
{
  var tappedImage = (Label)sender;
  var ImageId = Convert.ToInt32(tappedImage.StyleId);
  Application.Current.Properties["ItemId"] = ImageId;

  MessagingCenter.Send(new RedirectClass.OpenRecordingDetails(), RedirectClass.OpenRecordingDetails.Key);

  // check if a handler is assigned
  if (OnImageSelected != null) {
    OnImageSelected(this,new EventArgs(...));
  }
}

finally, in the code that is using the control, just define an event handler like you normally would

myControl.OnImageSelected += delegate {
  // handler logic goes here
};
Sign up to request clarification or add additional context in comments.

1 Comment

it did work but somehow I cannot define an even handle like this: SelectMultipleBasePage<ListItems> multiPage =new SelectMultipleBasePage<ListItems>(items); multiPage.OnImageSelected += RecordingListPage_OnImageSelected; I want to use this control and event on different pages. How do I do that?
0

You can hand over the handler from the parent to the child. Then it involves no static event handlers. You just have to add a parameter to your WrappedItemSelectionTemplate and create your template using new DataTemplate(() => new WrappedItemSelectionTemplate(HandleImageSelected)).

public class SelectMultipleBasePage<T> : ContentPage
{
    public delegate void ImageSelectedHandler(object sender, EventArgs e);
    public event ImageSelectedHandler OnImageSelected;

    public class WrappedItemSelectionTemplate : ViewCell
    {
        private readonly ImageSelectedHandler _parentHandler;
        public WrappedItemSelectionTemplate(ImageSelectedHandler parentHandler)
            : base()
        {
            _parentHandler = parentHandler;

            // ...
            View = st;
        }

        private void OnImageBtnTapped(object sender, EventArgs e)
        {
            //...
            _parentHandler.Invoke(sender, e);
        }

    }
    public static List<WrappedSelection<T>> WrappedItems = new List<WrappedSelection<T>>();
    public SelectMultipleBasePage(List<T> items)
    {
        WrappedItems = items.Select(item => new WrappedSelection<T>() { Item = item, IsSelected = false }).ToList();
        ListView mainList = new ListView()
        {
            ItemsSource = WrappedItems,
            ItemTemplate = new DataTemplate(() => new WrappedItemSelectionTemplate(HandleImageSelected)),
        };
        // ...
    }

    private void HandleImageSelected(object sender, EventArgs e)
    {
        if (OnImageSelected != null)
        {
            OnImageSelected(sender, e);
        }
    }
}

12 Comments

How do I access the event in the page that is using the control?
SelectMultipleBasePage<ListItems> multiPage =new SelectMultipleBasePage<ListItems>(items); multiPage.OnImageSelected += RecordingListPage_OnImageSelected;
Not working. Error: Error 20 Member 'AppName.SelectMultipleBasePage<AppName.ListItems>.OnImageSelected' cannot be accessed with an instance reference; qualify it with a type name instead
then you have a static somewhere.
Yes, sorry it was a mistake from my side. But now I can access the event but getting System.NullReferenceException: Object reference not set to an instance of an object while loading the page. I will debug to see where the error is coming.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.