I can't get my properties to save (or possibly load, or both).
The custom properties show in the property pane just fine (Well, the formatting isn't great, but they're there). But nothing I do can make the properties save, or load back into either the web part or the property pane.
I'm not sure what I'm doing wrong - I've been through a number of tutorials and they all say to do what I'm doing here, or variations on it, but nothing works.
Here's my code:
ChargeWebPart.ascx:
<div>
<table style="width:100%; border: 1px solid black;">
<tr>
<td>
<h3 style="text-align:center">Charge Calculator</h3>
</td>
</tr>
<tr>
<td>
<table style="width:100%">
<tbody>
<tr>
<td colspan="2">
<h3 style="text-align:center"><asp:Label runat="server" ID="lblInvestorChargePct">x%</asp:Label></h3>
</td>
<td colspan="2">
<h3 style="text-align:center"><asp:Label runat="server" ID="lblFounderChargePct">y%</asp:Label></h3>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</div>
ChargeWebPart.cs:
partial class ChargeWebPart : WebPart
{
private double _investorPct;// = 0.0;
private double _founderPct;// = 0.0;
[WebBrowsable(true), Personalizable(PersonalizationScope.Shared)]
public double FounderPct {
get
{
return _founderPct;
}
set
{
_founderPct = value;
}
}
[WebBrowsable(true), Personalizable(PersonalizationScope.Shared)]
public double InvestorPct
{
get
{
return _investorPct;
}
set
{
_investorPct = value;
}
}
}
ChargeWebPart.ascx.cs:
[ToolboxItemAttribute(false)]
public partial class ChargeWebPart : WebPart
{
public ChargeWebPart WebPart { get; set; }
public ChargeWebPart()
{
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
InitializeControl();
}
protected void Page_Load(object sender, EventArgs e)
{
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
lblFounderChargePct.Text = FounderPct.ToString() + "%";
lblInvestorChargePct.Text = InvestorPct.ToString() + "%";
}
public override EditorPartCollection CreateEditorParts()
{
return new EditorPartCollection(base.CreateEditorParts(),
new[]
{
new CustomEditorPart
{
ID = ID + "_editorPart"
}
});
}
}
public class CustomEditorPart : EditorPart
{
private TextBox _investorPct;
private TextBox _founderPct;
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
_investorPct = new TextBox();
Controls.Add(new LiteralControl("Percentage charge to investor: "));
Controls.Add(_investorPct);
_founderPct = new TextBox();
Controls.Add(new LiteralControl("\r\nPercentage charge to founder: "));
Controls.Add(_founderPct);
}
public override bool ApplyChanges()
{
EnsureChildControls();
double ipct;
double fpct;
double.TryParse(_investorPct.Text, out ipct);
((ChargeWebPart)WebPartToEdit).InvestorPct = ipct;
double.TryParse(_founderPct.Text, out fpct);
((ChargeWebPart)WebPartToEdit).FounderPct = fpct;
return true;
}
public override void SyncChanges()
{
EnsureChildControls();
var webpart = ((ChargeWebPart)WebPartToEdit);
_investorPct.Text = webpart.InvestorPct.ToString();
_founderPct.Text = webpart.FounderPct.ToString();
}
}
}
Seems like you are going overboard with the custom editor. If you simply need those two web part properties than you don't need to create the custom editor.
Your files can simply look like this:
ChargeWebPart.ascx:
<div>
<table style="width:100%; border: 1px solid black;">
<tr>
<td>
<h3 style="text-align:center">Charge Calculator</h3>
</td>
</tr>
<tr>
<td>
<table style="width:100%">
<tbody>
<tr>
<td colspan="2">
<h3 style="text-align:center"><asp:Label runat="server" ID="lblInvestorChargePct"><%= this.InvestorPct %></asp:Label></h3>
</td>
<td colspan="2">
<h3 style="text-align:center"><asp:Label runat="server" ID="lblFounderChargePct"><%= this.FounderPct %> %</asp:Label></h3>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
ChargeWebPart.ascx.cs:
[ToolboxItemAttribute(false)]
public partial class ChargeWebPart: WebPart
{
private double _investorPct;// = 0.0;
private double _founderPct;// = 0.0;
[WebBrowsable(true), Personalizable(PersonalizationScope.Shared)]
public double FounderPct
{
get
{
return _founderPct;
}
set
{
_founderPct = value;
}
}
[WebBrowsable(true), Personalizable(PersonalizationScope.Shared)]
public double InvestorPct
{
get
{
return _investorPct;
}
set
{
_investorPct = value;
}
}
public ChargeWebPart()
{
}
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
InitializeControl();
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
I think the only thing you were missing was including <%= this.InvestorPct %> and <%= this.FounderPct %> in your markup file. Which will display the values of those properties. Otherwise it is saving and loading the properties just fine.
Related
Checkbox list items have default "Text", "Value", "Enabled" and "Selected" properties.
I need to add an "ImageUrl" property to each item in my list.
I use this code:
foreach (Zone zn in ZonesList)
{
ListItem item = new ListItem(zn.Name, zn.Id.ToString());
item.Attributes.Add("ImageUrl", zn.Image );
item.Selected = false;
visitPlaceList.Items.Add(item);
}
visitPlaceList.DataBind();
but it still doesn't show any properties other than the defaults.
How can this be achieved?
A property does get added, but as a span element surrounding the input and label. It looks like this
<span imageurl="www.google.nl">
<input id="ctl00_mainContentPane_visitPlaceList_1" type="checkbox" name="ctl00$mainContentPane$visitPlaceList$1" />
<label for="ctl00_mainContentPane_visitPlaceList_1">Name 1</label>
</span>
So if you need it with jQuery, get the correct element.
<asp:CheckBoxList ID="visitPlaceList" runat="server" ClientIDMode="Static"></asp:CheckBoxList>
<script>
$("#visitPlaceList input").change(function () {
var imageurl = $(this).closest('span').attr('imageurl');
console.log(imageurl);
});
</script>
Very interesting question! It exposes the limitations that web controls sometimes have.
I believe the proper way to solve it is by creating a custom (web) control. It is far from trivial though especially since both ListItem and CheckBoxList are sealed classes.
It can also be solved by creating a user control (ascx). The following can be improved but you get the idea:
ImageCheckBoxList.ascx
<asp:Repeater ID="Repeater1" runat="server">
<HeaderTemplate>
<table>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<asp:CheckBox ID="CheckBox1" runat="server" /><asp:Image ID="Image1" runat="server" ImageUrl='<%# Eval("ImageUrl") %>' /><asp:Label ID="Label1" runat="server" AssociatedControlID="CheckBox1" Text='<%# Eval("Text") %>'></asp:Label>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
ImageCheckBoxList.ascx.cs
public partial class ImageCheckBoxList : System.Web.UI.UserControl
{
public IList<ImageListItem> Items { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Repeater1.DataSource = Items;
Repeater1.DataBind();
}
for (int i = 0; i < Repeater1.Items.Count; i++)
{
var checkBox = (CheckBox)Repeater1.Items[i].FindControl("CheckBox1");
if (checkBox != null)
{
Items[i].Checked = checkBox.Checked;
}
}
}
}
where ImageListItem is:
public class ImageListItem
{
public string Text { get; set; }
public string Value { get; set; }
public string ImageUrl { get; set; }
public bool Checked { get; set; }
public ImageListItem(string text, string value, string imageUrl)
{
Text = text;
Value = value;
ImageUrl = imageUrl;
Checked = false;
}
}
Here's how to use it in a Web Forms page:
ASPX
<%# Register TagPrefix="uc" TagName="ImageCheckBoxList" Src="ImageCheckBoxList.ascx" %>
<uc:ImageCheckBoxList ID="ImageCheckBoxList1" runat="server" />
<asp:Button ID="Button1" runat="server" Text="Button" OnClick="Button1_Click" />
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
Code-behind
protected void Page_Load(object sender, EventArgs e)
{
ImageCheckBoxList1.Items = new List<ImageListItem>()
{
new ImageListItem("Item 1", "Item1", "Images/1.png"),
new ImageListItem("Item 2", "Item2", "Images/2.png"),
new ImageListItem("Item 3", "Item3", "Images/3.png")
};
}
protected void Button1_Click(object sender, EventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach (ImageListItem item in ImageCheckBoxList1.Items)
{
if (item.Checked)
{
sb.AppendLine($"{item.Text} with value {item.Value} is checked.");
}
}
Label1.Text = sb.ToString();
}
I have a user control:
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs" Inherits="StratPlan.Main.UserCons.WebUserControl1" %>
<div>
<table>
<tr>
<td>title: </td>
<td>
<asp:TextBox ID="TitleTextBox" runat="server"/>
</td>
</tr>
<tr>
<td>strategy id: </td>
<td>
<asp:TextBox ID="StrategyIdTextBox" runat="server"/>
</td>
</tr>
<tr>
<td>company: </td>
<td>
<asp:TextBox ID="CompanyTextBox" runat="server"/>
</td>
</tr>
</table>
</div>
In its code behind:
public partial class WebUserControl1 : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
TitleTextBox.Text = ExpTitle;
StrategyIdTextBox.Text = ExpStrategyId;
CompanyTextBox.Text = ExpCompany;
}
public string ExpTitle
{
get { return this.TitleTextBox.Text; }
}
public string ExpStrategyId
{
get { return this.StrategyIdTextBox.Text; }
}
public string ExpCompany
{
get { return this.CompanyTextBox.Text; }
}
}
Then in my page, I have a list view:
<div>
<asp:ListView ID="ListView1" runat="server">
<ItemTemplate>
<uc1:WebUserControl1 runat="server" ID="WebUserControl1" ExpStrategyId='<%# Bind(StrategyId) %>' ExpTitle='<%# Bind(Title) %>' ExpCompany='<%# Bind(CompanyName) %>'/>
</ItemTemplate>
</asp:ListView>
</div>
And I bind the datasource like this in code behind:
public void LoadGridView()
{
localVm.EntityList = localVm.RetrieveMany(localVm.SearchItem);
ItemsGridView.AutoGenerateColumns = false;
ListView1.DataSource = localVm.EntityList;
ListView1.DataBind();
}
But whenever I go to my page, it doesn't give the value to the user control. what am I doing wrong?
The pages Load event is called before data bound controls are bound to their data. Change it to PreRenderComplete or Render like this
public partial class WebUserControl1 : System.Web.UI.UserControl
{
protected void Page_PreRenderComplete(object sender, EventArgs e)
{
TitleTextBox.Text = ExpTitle;
StrategyIdTextBox.Text = ExpStrategyId;
CompanyTextBox.Text = ExpCompany;
}
...
}
have a look at ASP.NET Page Life Cycle Overview
I am essentially trying to add some clickable way to delete or edit entries from my table. These entries are all saved on an access database which populates the table. My biggest problem is that I am unsure on how I can program the clickable method so that it saves which username I am trying to edit/delete. Any advice would be appreciated.
Relevant code:
main.aspx
<%# Page Language="C#" AutoEventWireup="true" CodeFile="main.aspx.cs" Inherits="main" %>
<%# Reference Control="~/UserInfoBoxControl.ascx" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:PlaceHolder runat="server" ID="phUserInfoBox" />
</div>
<asp:Button id="login" runat="server" Text="edit profile" onclick="btnRegister_click" />
<asp:Button id="create" runat="server" Text="logout" onclick="btnLogout_click" />
</form>
</body>
</html>
main.aspx.cs
using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class main : System.Web.UI.Page
{
private OleDbConnection bookConn;
private OleDbCommand oleDbCmd = null;
private String connParam = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source='F:\test\Database21.accdb'; Persist Security Info=False;";
protected void Page_Load(object sender, EventArgs e)
{
{
OleDbDataReader reader;
bookConn = new OleDbConnection(connParam);
bookConn.Open();
oleDbCmd = new OleDbCommand("SELECT user_name, fname, lname FROM profiles",bookConn);
reader = oleDbCmd.ExecuteReader();
while (reader.Read())
{
UserInfoBoxControl MyUserInfoBoxControl =(UserInfoBoxControl)LoadControl("UserInfoBoxControl.ascx");
phUserInfoBox.Controls.Add(MyUserInfoBoxControl);
MyUserInfoBoxControl.UserName = reader.GetString(0);
MyUserInfoBoxControl.FirstName = reader.GetString(1);
MyUserInfoBoxControl.LastName = reader.GetString(2);
}
bookConn.Close();
}
}
protected void btnRegister_click(object sender, EventArgs e)
{
Response.Redirect("myprofile.aspx");
}
protected void btnLogout_click(object sender, EventArgs e)
{
Response.Redirect("index.aspx");
}
}
UserInfoBoxControl.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeFile="UserInfoBoxControl.ascx.cs" Inherits="UserInfoBoxControl" %>
<table>
<tr>
<th>UserName</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
<tr>
<td><%= this.UserName %> </td>
<td><%= this.FirstName %></td>
<td><%= this.LastName %></td>
</tr>
</table>
UserInfoBoxControl.ascx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class UserInfoBoxControl : System.Web.UI.UserControl
{
private string userName;
private string fname;
private string lname;
public string UserName
{
get { return userName; }
set { userName = value; }
}
public string FirstName
{
get { return fname; }
set { fname = value; }
}
public string LastName
{
get { return lname; }
set { lname = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
You'll want to use model binding where you define a class type to show in the grid.
http://www.asp.net/web-forms/overview/presenting-and-managing-data/model-binding/updating-deleting-and-creating-data
Then you'll specify in your gridview with commands to update/delete/etc your records. In those methods you'll get the applicable info and delete.
Ex grid:
<asp:GridView runat="server" ID="studentsGrid"
ItemType="ContosoUniversityModelBinding.Models.Student" DataKeyNames="StudentID"
SelectMethod="studentsGrid_GetData"
UpdateMethod="studentsGrid_UpdateItem" DeleteMethod="studentsGrid_DeleteItem"
AutoGenerateEditButton="true" AutoGenerateDeleteButton="true"
AutoGenerateColumns="false">
and then notice how the id is passed in:
public void studentsGrid_DeleteItem(int studentID)
{
using (SchoolContext db = new SchoolContext())
{
var item = new Student { StudentID = studentID };
db.Entry(item).State = EntityState.Deleted;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
ModelState.AddModelError("",
String.Format("Item with id {0} no longer exists in the database.", studentID));
}
}
}
If this is your first time with web forms, I'd honestly look at going the MVC route and using MVC Scaffolding. You cannot use the Access database though with entity framework you'd need to use your OLEDB code directly like you did above (or not use access if that's an option)
I ended up figuring it out. All I did was add a button in the web user control box and in the code behind created a new session variable.
UserInfoBoxControl.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeFile="UserInfoBoxControl.ascx.cs" Inherits="UserInfoBoxControl" %>
<table>
<tr>
<th>UserName</th>
<th>First Name</th>
<th>Last Name</th>
</tr>
<tr>
<td><%= this.UserName %> </td>
<td><%= this.FirstName %></td>
<td><%= this.LastName %> <asp:Button id="login" runat="server" Text="edit user" onclick="btnEdit_click" /></td>
</tr>
</table>
UserInfoBoxControl.ascx.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class UserInfoBoxControl : System.Web.UI.UserControl
{
private string userName;
private string fname;
private string lname;
public string UserName
{
get { return userName; }
set { userName = value; }
}
public string FirstName
{
get { return fname; }
set { fname = value; }
}
public string LastName
{
get { return lname; }
set { lname = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnEdit_click(object sender, EventArgs e)
{
Session["UserNameMod"] = userName;
Response.Redirect("userinfo.aspx");
}
}
I have a custom control derived from the CompositeControl class, which is defined as:
[ToolboxData("<{0}:ContractControl runat=server></{0}:ContractControl>")]
public class ContractControl : CompositeControl
{
private int contractID = 0;
private ContractTileControl tileControl = null;
private ContractDetailControl detailControl = null;
private HtmlGenericControl contractMainDiv = null;
public int ContractID
{
get { return this.contractID; }
set { this.contractID = value; }
}
public ContractTileControl TileControl
{
get { return this.tileControl; }
set { this.tileControl = value; }
}
public ContractDetailControl DetailControl
{
get { return this.detailControl; }
set { this.detailControl = value; }
}
public ContractControl()
{
this.contractMainDiv = new HtmlGenericControl("div");
this.contractMainDiv.ID = "contractMainDiv";
this.contractMainDiv.Attributes.Add("class", "contractMain");
}
#region protected override void OnPreRender(EventArgs e)
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
CreateChildControls();
}
#endregion
#region protected override void CreateChildControls()
protected override void CreateChildControls()
{
if (tileControl != null)
{
this.contractMainDiv.Controls.Add(tileControl);
}
if (detailControl != null)
{
this.contractMainDiv.Controls.Add(detailControl);
}
this.Controls.Add(contractMainDiv);
}
#endregion
}
When I add a number of them to a placeholder control they render fine, but when I try to bind the same ones to a Repeater, the child composite controls tileControl and detailControl do not render, only the contractMainDiv does.
The repeater is defined as:
<asp:Repeater ID="myRepeater" runat="server" EnableTheming="true">
<HeaderTemplate>
<table border="0" cellpadding="0" cellspacing="0">
</HeaderTemplate>
<ItemTemplate>
<tr><td><easit:ContractControl ID="contractControl" runat="server" />
</td></tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
And I bind to it by first generating a List<ContractControl> and then calling:
List<ContractControl> controls = new List<ContractControl>();
//Generate custom controls for each element in input dictionary
//Create TileControl and DetailControl
//Create ContractControl and add it to collection
ContractControl contractControl = new ContractControl();
contractControl.ContractID = contract.Contract.Id;
contractControl.TileControl = tile;
contractControl.DetailControl = detail;
controls.Add(contractControl);
myRepeater.DataSource = controls;
myRepeater.DataBind();
Yet the resulting table contains the right number of items, but the child 'CompositeControl's do not get rendered at all, only the contractMainDiv shows up.
I have a repeater control in a user control. The repeater control has a label and a textbox. A new row is added in repeater control on the click of a button. If i add one row and enter some value in textBox and then again add a new row in the repeater control the value entered in the textbox of first row gets lost.
Can anyone help me out how to retain the value of textbox after postback.
You are losing it because you do not have a If not IsPostBack wrapped around the code that sets the value of the textbox.
So every time you execute the page the value is being reset. Wrap the code that sets the value around ispostback and the new value will not be overridden.
if (!Page.IsPostBack) { // set value }
You may get help from the following link:
http://ranafaisal.wordpress.com/2009/02/17/dynamically-adding-removing-textboxes-in-aspnet-repeater/
I have tried myself and found a solution:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace RepeaterTest
{
public class Car
{
private string _year;
private string _make;
private string _model;
public string Year
{
get
{
return _year;
}
set
{
_year = value;
}
}
public string Make {
get { return _make; }
set { _make=value;}
}
public string Model
{
get { return _model; }
set { _model = value; }
}
public Car(string year,string make,string model){
_year = year;
_make = make;
_model = model;
}
public Car(){
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Collections;
namespace RepeaterTest
{
public class Cars:CollectionBase
{
public int Add(Car car) {
return List.Add(car);
}
}
}
in code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace RepeaterTest
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack) {
Cars cars = new Cars();
Car c1 = new Car("2011", "Marcedez", "Marcedez");
Car c2 = new Car("2012", "BMW", "135i");
cars.Add(c1);
cars.Add(c2);
Repeater1.DataSource = cars;
Repeater1.DataBind();
}
}
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "Add") {
//Car carLastEntered = new Car();
Cars cars = new Cars();
foreach (RepeaterItem item in Repeater1.Items) {
Car car = new Car();
car.Year = ((TextBox)(item.FindControl("txtYear"))).Text;
car.Make = ((TextBox)(item.FindControl("txtMake"))).Text;
car.Model = ((TextBox)(item.FindControl("txtModel"))).Text;
cars.Add(car);
//carLastEntered = car;
}
//cars.Add(carLastEntered);
Repeater1.DataSource = cars;
Repeater1.DataBind();
}
}
}
}
in aspx:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="RepeaterTest.WebForm1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Repeater ID="Repeater1" runat="server"
onitemcommand="Repeater1_ItemCommand">
<HeaderTemplate>
<table cellpadding="0" cellspacing="5">
<tr style="padding-top: 5px;">
<td colspan="6">
<asp:Label ID="lblInstructions" runat="server" Text="Add your cars here:" />
</td>
</tr>
<tr runat="server" id="trHeader" style="font-weight: bold;">
<td>Year</td>
<td>Make</td>
<td>Model</td>
<td></td>
<td></td>
<td></td>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td><asp:TextBox ID="txtYear" runat="server" Width="65"
Text='<%#DataBinder.Eval(Container.DataItem, "Year")%>' /></td>
<td><asp:TextBox ID="txtMake" runat="server" Width="70"
Text='<%#DataBinder.Eval(Container.DataItem, "Make")%>' /></td>
<td><asp:TextBox ID="txtModel" runat="server" Width="70"
Text='<%#DataBinder.Eval(Container.DataItem, "Model")%>' /></td>
</tr>
</ItemTemplate>
<FooterTemplate>
<tr style="padding-top: 5px;">
<td colspan="6">
<asp:Button ID="btnAdd" runat="server"
Text="Add Car" CommandName="Add" />
</td>
</tr>
</table>
</FooterTemplate>
</asp:Repeater>
</div>
</form>
</body>
</html>
Hope this helps. Thanks.