/// /// Used to add a header / toolbar to a Saleslogix grid view. /// Note that this won't work inside of a dialog, for several reasons. /// [DefaultProperty("GridTitle")] [ToolboxData("<{0}:SlxGridHelper runat=server>")] [ParseChildren(true)] public class SlxGridHelper : WebControl, INamingContainer { [Description("Title to be displayed above the grid")] public String GridTitle { get; set; } [Description("Whether to show the 'Add' button (GridTitle has to be set for this to work)"), DefaultValue(true)] public bool ShowAddButton { get; set; } [Description("Whether to show the 'Delete' button (GridTitle has to be set for this to work)"), DefaultValue(true)] public bool ShowDeleteButton { get; set; } [Description("Id of an existing button to use for the add action. This must be an IButtonControl.")] public String AddButtonId { get; set; } [Description("Id of an existing button to use for the delete action. This must be an IButtonControl.")] public String DeleteButtonId { get; set; } /// /// If true, then the datasource will be automatically set up /// (pulling from the collection property set up in the child insert info) /// [Description("If true, then the datasource will be automatically set up"), DefaultValue(false)] public bool AutoBind { get; set; } /// /// Set this to a comma-separated list of fields to pull (this is only necessary when pulling "deep" properties, /// e.g., Contact.Account.MainPhone) /// public String BindFieldNames { get; set; } [Description("ID for the grid we need to display this header for")] public String GridId { get; set; } [Description("Specification for the popup form. The ChildInsertInfo needs to be filled in in order for all functionality to be enabled."), DesignerSerializationVisibility( DesignerSerializationVisibility.Content), PersistenceMode(PersistenceMode.InnerProperty)] public MyDialogSpecs DialogSpecs { get; set; } [Description("Content of this template will be added to the toolbar, before the save button. GridTitle must be specified."), TemplateContainer(typeof(SlxGridHelper)), PersistenceMode(PersistenceMode.InnerProperty)] public ITemplate ToolbarContent { get; set; } private GridView _grid; /// /// Retrieve a reference to the page-level work item. /// This can be used to get to the main entity. /// private PageWorkItem WorkItem { get { return ((Sage.Platform.Application.UI.Web.ApplicationPage)Page).PageWorkItem; /* * This is from the FAQ but not working as of 7.5 IPageWorkItemLocator locatorService = Sage.Platform.Application.ApplicationContext.Current.Services.Get(); return locatorService.GetPageWorkItem(); */ } } private WebEntityListBindingSource _dataSource; /// /// Create (if necessary) and return the data source used for the grid. /// By default this creates a datasource pulling from the property specified under ChildInsertInfo. /// protected virtual WebEntityListBindingSource DataSource { get { if (_dataSource == null) { _dataSource = new WebEntityListBindingSource(DialogSpecs.ChildInsertInfo.ChildEntityType, DialogSpecs.ChildInsertInfo.ParentEntityType, DialogSpecs.ChildInsertInfo.ParentsCollectionPropertyName, System.Reflection.MemberTypes.Property); } return _dataSource; } } /// /// Tie event handlers to the (optional) specified buttons. /// /// protected override void OnInit(EventArgs e) { base.OnInit(e); if (!String.IsNullOrEmpty(AddButtonId)) { var btnAdd = ((IButtonControl)Parent.FindControl(AddButtonId)); if (btnAdd == null) throw new InvalidOperationException("Unable to locate button '" + AddButtonId + "'"); btnAdd.Click += new EventHandler(btnAdd_Click); } if (!String.IsNullOrEmpty(DeleteButtonId)) { var btnDelete = ((IButtonControl)Parent.FindControl(DeleteButtonId)); if (btnDelete == null) throw new InvalidOperationException("Unable to locate button '" + DeleteButtonId + "'"); btnDelete.Click += new EventHandler(btnDelete_Click); } } /// /// Tie event handlers for the grid. /// /// protected override void OnLoad(EventArgs e) { base.OnLoad(e); if (Visible) { _grid = Parent.FindControl(GridId) as GridView; if (_grid == null) throw new InvalidOperationException("Unable to locate grid id '" + GridId + "'"); if (_grid.DataKeyNames == null || _grid.DataKeyNames.Length == 0) _grid.DataKeyNames = new String[] { "Id" }; _grid.RowDataBound += new GridViewRowEventHandler(grid_RowDataBound); _grid.RowEditing += new GridViewEditEventHandler(grid_RowEditing); if (this.AutoBind) { DataSource.Bindings.Add(new WebEntityListBinding(DialogSpecs.ChildInsertInfo.ParentsCollectionPropertyName, _grid)); } if(_grid.AllowSorting) _grid.Sorting += new GridViewSortEventHandler(delegate { }); if(_grid.AllowPaging) _grid.PageIndexChanging += new GridViewPageEventHandler(grid_PageIndexChanging); } } /// /// Bind the grid. /// /// protected override void OnPreRender(EventArgs e) { base.OnPreRender(e); if (Visible && this.AutoBind) { object currentEntity = WorkItem.Services.Get().GetEntity(); if (currentEntity != null) { DataSource.SourceObject = currentEntity; if (!String.IsNullOrEmpty(BindFieldNames)) DataSource.BindFieldNames = BindFieldNames.Split(new char[] { ',' }) .Select(s => s.Trim()) .ToArray(); WorkItem.Services.Get().WatchBindingSource(DataSource); DataSource.Bind(); } } } /// /// Set up event handlers for clicking on the grid's rows /// /// /// void grid_RowDataBound(object sender, GridViewRowEventArgs e) { if (e.Row.RowType == DataControlRowType.DataRow) { if (DialogSpecs != null) { e.Row.Attributes.Add("ondblclick", Page.ClientScript.GetPostBackEventReference(_grid, "Edit$" + e.Row.RowIndex.ToString())); } e.Row.Attributes.Add("onclick", Page.ClientScript.GetPostBackEventReference(_grid, "Select$" + e.Row.RowIndex.ToString())); if (e.Row.RowIndex == _grid.SelectedIndex) { // for some reason Saleslogix f. this one up e.Row.CssClass += _grid.SelectedRowStyle.CssClass; } } } /// /// Handler to open the edit form when they want to edit a row. /// /// /// void grid_RowEditing(object sender, GridViewEditEventArgs e) { string id = (string)_grid.DataKeys[e.NewEditIndex].Value; IWebDialogService dlgService = WorkItem.Services.Get(); dlgService.SetSpecs(DialogSpecs.TopPosition, DialogSpecs.LeftPosition, DialogSpecs.DialogHeight, DialogSpecs.DialogWidth, DialogSpecs.ActiveSmartPartID, DialogSpecs.Title, DialogSpecs.CenterDialog); dlgService.EntityType = this.DialogSpecs.ChildInsertInfo.ChildEntityType; dlgService.EntityID = id; dlgService.ShowDialog(); } void grid_PageIndexChanging(object sender, GridViewPageEventArgs e) { _grid.PageIndex = e.NewPageIndex; } /// /// Delete the selected record (the confirmation dialog is assumed to have already been done, client-side) /// /// /// void btnDelete_Click(object sender, EventArgs e) { int selectedIndex = _grid.SelectedIndex; if (selectedIndex >= 0) { string id = (string)_grid.DataKeys[selectedIndex].Value; if (String.IsNullOrEmpty(id)) throw new InvalidOperationException("Grid DataKey is empty - did you set DataKeyNames?"); IPersistentEntity child = (IPersistentEntity)EntityFactory.GetById(DialogSpecs.ChildInsertInfo.ChildEntityType, id); if (child != null) { // remove reference from the parent's collection object parentEntity = WorkItem.Services.Get().GetEntity(); object parentCollection = DialogSpecs.ChildInsertInfo.ParentsCollectionProperty.GetValue(parentEntity, null); MethodInfo funRemove = parentCollection.GetType().GetMethod("Remove"); funRemove.Invoke(parentCollection, new object[] { child }); // THEN we can delete the entity child.Delete(); // make sure we refresh all updatepanels WorkItem.Services.Get().RefreshAll(); } } } /// /// Pop up the edit form for an add. /// This requires the ChildInsertInfo and DialogSpecs to be fully populated. /// /// /// void btnAdd_Click(object sender, EventArgs e) { IWebDialogService dlgService = WorkItem.Services.Get(); dlgService.SetSpecs(DialogSpecs.TopPosition, DialogSpecs.LeftPosition, DialogSpecs.DialogHeight, DialogSpecs.DialogWidth, DialogSpecs.ActiveSmartPartID, DialogSpecs.Title, DialogSpecs.CenterDialog); dlgService.EntityType = DialogSpecs.ChildInsertInfo.ChildEntityType; dlgService.SetChildIsertInfo( DialogSpecs.ChildInsertInfo.ChildEntityType, DialogSpecs.ChildInsertInfo.ParentEntityType, DialogSpecs.ChildInsertInfo.ParentReferenceProperty, DialogSpecs.ChildInsertInfo.ParentsCollectionProperty); dlgService.ShowDialog(); } /// /// If the "GridTitle" was specified we'll create the header table here (optionally including add /// and delete button) /// protected override void CreateChildControls() { base.CreateChildControls(); if (!String.IsNullOrEmpty(GridTitle)) { Controls.Add(CreateHeaderTable()); } } public override void DataBind() { EnsureChildControls(); base.DataBind(); } /// /// Create the caption/toolbar line for the grid. /// /// private Control CreateHeaderTable() { Table tblHeader = new Table(); tblHeader.CssClass = "mainContentHeaderTable"; tblHeader.Style.Add("width", "100%"); TableRow tblRow = new TableRow(); TableCell tblCell = new TableCell(); tblCell.Text = GridTitle; tblRow.Cells.Add(tblCell); tblCell = new TableCell(); tblCell.CssClass = "mainContentHeaderToolsRight"; if (ToolbarContent != null) { PlaceHolder contentPlaceholder = new PlaceHolder(); tblCell.Controls.Add(contentPlaceholder); ToolbarContent.InstantiateIn(contentPlaceholder); } if (ShowAddButton) { ImageButton btnAdd = new ImageButton(); btnAdd.ID = "btnAdd"; btnAdd.AlternateText = "Add Record"; btnAdd.ImageUrl = "ImageResource.axd?scope=global&type=Global_Images&key=Plus_16x16"; ((IButtonControl)btnAdd).Click += new EventHandler(btnAdd_Click); tblCell.Controls.Add(btnAdd); } if (ShowDeleteButton) { ImageButton btnDelete = new ImageButton(); btnDelete.ID = "btnDelete"; btnDelete.AlternateText = "Delete Selected Record"; btnDelete.ImageUrl = "ImageResource.axd?scope=global&type=Global_Images&key=Delete_16x16"; btnDelete.OnClientClick = "return confirm('Are you sure you wish to delete this record?')"; ((IButtonControl)btnDelete).Click += new EventHandler(btnDelete_Click); tblCell.Controls.Add(btnDelete); } // todo - we should have a way to add control from the form's definition tblRow.Cells.Add(tblCell); tblHeader.Rows.Add(tblRow); return tblHeader; } #region Design time support /// /// Wrap around DialogSpecs to get the properties to show up correctly in the form designer. /// public class MyDialogSpecs : WebDialogSpecs { public MyDialogSpecs() { base.ChildInsertInfo = new MyChildInsertInformation(); } public new MyChildInsertInformation ChildInsertInfo { get { return this.MyChildInsertInfo; } } [DesignerSerializationVisibility( DesignerSerializationVisibility.Content), PersistenceMode(PersistenceMode.InnerProperty)] public MyChildInsertInformation MyChildInsertInfo { get { return (MyChildInsertInformation)base.ChildInsertInfo; } set { base.ChildInsertInfo = value; } } } /// /// Wrapper for ChildInsertInfo to enable support in the designer. /// public class MyChildInsertInformation : ChildInsertInformation { public String ParentEntityTypeName { set { base.ParentEntityType = Type.GetType(value); if (base.ParentEntityType == null) throw new InvalidOperationException("The value '" + value + "' cannot be converted to a type"); } } public String ChildEntityTypeName { set { base.ChildEntityType = Type.GetType(value); if (base.ChildEntityType == null) throw new InvalidOperationException("The value '" + value + "' cannot be converted to a type"); } } public String ParentReferencePropertyName { get; set; } public String ParentsCollectionPropertyName { get; set; } public new PropertyInfo ParentReferenceProperty { get { return ChildEntityType.GetProperty(ParentReferencePropertyName); } } public new PropertyInfo ParentsCollectionProperty { get { return ParentEntityType.GetProperty(ParentsCollectionPropertyName); } } } #endregion }