如何为自定义控件创建事件
Posted
技术标签:
【中文标题】如何为自定义控件创建事件【英文标题】:How to create an event for custom control 【发布时间】:2016-08-11 09:26:23 【问题描述】:我创建了一个带有图像、2 个标签和一个按钮的自定义控件(列表)。我想调用自定义控件的按钮单击。我的整个控件也将有一个单独的事件。
这是我的用户控件模板代码:
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();
我有一个事件 OnImageBtnTapped 我想从我的内容页面访问它。我该怎么做?
编辑
感谢@Jason,我设法在使用控件的代码中获得了事件点击功能。
在自定义控件类中:
private void OnImageBtnTapped(object sender, EventArgs e)
if (OnImageSelected != null)
OnImageSelected(sender,e);
在使用控件的代码中,
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);
【问题讨论】:
【参考方案1】:这与为任何 C# 代码创建自定义事件没有什么不同
首先,在你的类中创建一个公共事件和处理程序:
// 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;
接下来,在控件内部你可以调用处理程序(如果存在)
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(...));
最后,在使用控件的代码中,像往常一样定义一个事件处理程序
myControl.OnImageSelected += delegate
// handler logic goes here
;
【讨论】:
它确实有效,但不知何故我无法定义这样的偶数句柄:SelectMultipleBasePage<ListItems> multiPage =new SelectMultipleBasePage<ListItems>(items); multiPage.OnImageSelected += RecordingListPage_OnImageSelected;
我想在不同的页面上使用这个控件和事件。我该怎么做?【参考方案2】:
您可以将处理程序从父级移交给子级。然后它不涉及静态事件处理程序。您只需向您的WrappedItemSelectionTemplate
添加一个参数并使用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);
【讨论】:
如何访问正在使用该控件的页面中的事件?SelectMultipleBasePage<ListItems> multiPage =new SelectMultipleBasePage<ListItems>(items); multiPage.OnImageSelected += RecordingListPage_OnImageSelected;
不工作。错误:Error 20 Member 'AppName.SelectMultipleBasePage<AppName.ListItems>.OnImageSelected' cannot be accessed with an instance reference; qualify it with a type name instead
那么你在某处有一个static
。
是的,很抱歉这是我的错误。但现在我可以访问该事件,但在加载页面时获得System.NullReferenceException: Object reference not set to an instance of an object
。我会调试看看哪里出错了。以上是关于如何为自定义控件创建事件的主要内容,如果未能解决你的问题,请参考以下文章
如何为 SFSafariViewController 中的控件设置自定义字体?