What is the format for databinding to a complex "object"? I have a linq to sql class that has containment, i.e object.containedobject.
I want to reference the sub objects fields declarative.
So I've tried my MySubField.MyBasicProperty and that did not work, as well as, MySubField_MyBasicProperty.
Thanks for any help!
I found my answer, it is a problem with boundfield class and not databinding.
http://www.iridescence.no/post/FixingBoundFieldSupportforCompositeObjects.aspx
I found solution and sharing for anyone who comes after me in the future.
You need to override the objectdatasource updating method to replace the param names. This is only possible if the objectypename property of the objectdatasource is not set or else they will be read only.
Here is my example:
protected void ObjectDataSource1_Updating(object sender, ObjectDataSourceMethodEventArgs e)
{
foreach (string currentKey in e.InputParameters.Keys)
{
if (currentKey.Contains("."))
{
string newKey = currentKey.Replace(".", "_");
object myValue = null;
if (e.InputParameters[currentKey] != null)
myValue = e.InputParameters[newKey];
if (e.InputParameters.Contains(newKey))
e.InputParameters.Remove(newKey);
e.InputParameters.Add(newKey, myValue);
e.InputParameters.Remove(currentKey);
}
}
Related
As per the title, I know the code I've posted below is utter poo, this is why I need your help!
I've put way too many hours into this, and it's either down to inexperience, a bug or I've screwed up somewhere.
I have a user control with a view properties that access the ViewState and two user controls within that display the properties.
Within the page_load of the user control, depending on the value of some of the properties, it will toggle the visibility of the controls within:
public partial class PatientStatus : System.Web.UI.UserControl
{
public string PatientName { get { return ViewState["PatientName"] as string; } set { ViewState["PatientName"] = value; } }
public bool ClinicianView { get { return Convert.ToBoolean(ViewState["ClinicianView"]); } set { ViewState["ClinicianView"] = value; } }
public string RangeTitle { get { return ViewState["RangeTitle"] as string; } set { ViewState["RangeTitle"] = value; } }
public int? RangeLimitNormSys { get { return ViewState["RangeLimitNormSys"] as int?; } set { ViewState["RangeLimitNormSys"] = value; } }
public int? RangeLimitNormDia { get { return ViewState["RangeLimitNormDia"] as int?; } set { ViewState["RangeLimitNormDia"] = value; } }
protected void Page_Load(object sender, EventArgs e)
{
bool ispostback = IsPostBack;
if (ispostback && ((System.Web.UI.WebControls.Repeater)(this.Parent.Parent)).DataSource != null)
{
object itm = ((RepeaterItem)this.Parent).DataItem;
if (itm is AppointmentRow)
{
AppointmentRow row = itm as AppointmentRow;
PatientName = row.Name;
RangeTitle = row.Range;
RangeLimitNormDia = row.RangeLimitNormDia;
RangeLimitNormSys = row.RangeLimitNormSys;
ispostback = false;
}
else if (itm is ReadingRow)
{
ReadingRow row = itm as ReadingRow;
PatientName = row.Name;
RangeTitle = row.Range;
RangeLimitNormDia = row.RangeLimitNormDia;
RangeLimitNormSys = row.RangeLimitNormSys;
ispostback = false;
}
else if (itm is PatientRow)
{
PatientRow row = itm as PatientRow;
PatientName = row.Name;
RangeTitle = row.Range;
RangeLimitNormDia = row.RangeLimitNormDia;
RangeLimitNormSys = row.RangeLimitNormSys;
ispostback = false;
}
}
if (!ispostback)
{
if (!string.IsNullOrWhiteSpace(RangeTitle))
{
placeHolder.Visible = true;
literalNA.Visible = false;
}
}
}
}
Previously the Page_Load event simply contained:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (!string.IsNullOrWhiteSpace(RangeTitle))
{
placeHolder.Visible = true;
literalNA.Visible = false;
}
}
}
But on postback, the RangeTitle property was always null, so even when removing the isPostBack statement, it didn't work property.
The only way to resolve it, was to really fudge it by the first block of code.
On the Page_Load of the parent page, and on !isPostBack I'm calling a method that gets and binds data to the repeater. This works fine. But on a postback, i'm calling the same method and for some reason, the user control isn't populating.
Any idea's?
Merry Christmas
Gav
Edit
In response to #jwiscarson
I have a table that is generated via an ASP:Repeater and within the ItemTemplate, I have added a User Control which contains a PlaceHolder and a Literal. Also within the User Control is a number of Properties.
Then on databind I pass across a number of values to the user control (I've tried both OnItemDataBound and inline using Eval). Then on the User Control's Page_Load event, as per the second block of code above, I first check if it's a postback, if not, I then check to see if the Property RangeTitle has a value.
If RangeTitle does not have a value, I then hide the placeholder that contains HTML that would display the RangeTitle and show a literal that displays N/A.
When loading the page for the first time, (!isPostBack) it works fine. But as soon as I create a postback, the User Controls within the repeater all revert to N/A even when their RangeTitle properties had a value.
On debugging, I set a breakpoint in the Page_Load of the User Control. When I first load the page, I can see that my properties have been populated correctly. Then on postback, Page_Load is called on the UserControl and the properties are populated correctly, then Page_Load is called again, but this time, the properties are empty.
i.e.
!isPostBack
UserControl::Page_Load < Correct data
isPostBack
UserControl::Page_Load < Correct data
myButton_Click (bind new data)
UserControl::Page_Load < No data
To make things even more confusing. The method called within myButton_Click to bind the data, is the exact same method called in the Page_Load of the Page to populate the repeater on !isPostBack
Thanks ;)
I would check to make sure that DataItem is accessible in this function. I think you need to listen to the ItemDataBound event and perform this work in that event.
Beyond that, it's difficult to suggest anything else concrete. I don't really understand why you're doing what you're doing (if you have this information in a Repeater, why does it also need to be in ViewState?). If you could explain your rationale for doing this, it might help me and anyone else who visits this question. You say that you're just trying to show/hide some specific items on the page. This is pretty complicated code without a lot of justification for that complication.
As an aside: you really, really need to break this code down and think about what you're trying to accomplish. Here are a few suggestions:
Separate the scopes inside your if and else if statements into functions that return the data you need.
Do not include lines like ((System.Web.UI.WebControls.Repeater)(this.Parent.Parent)).DataSource != null in an if statement. Perform this cast separately and store it in a variable, or write a small function that checks this.
Statements like this.Parent.Parent and other references to Parent controls are code smells, in my opinion. Even on a normal page, this would be a code smell, but what exactly is this.Parent going to reference when you include it in a UserControl?
I'm using an asp:ObjectDatasource to handle an insert (done through Linq to SQL) to my SQL database. The insert function returns an integer.
My problem is that how could I get the returned value of the function. Should I manually call the insert function through code behind or could I get it from the asp:ObjectDataSource as a key/value pair like how I send data through the asp:ObjectDataSource for the insert function to use?
Here's the insert function code:
public static int InsertSpotlightList(string spotlight_list_name)
{
ALI_DBDataContext ctx = new ALI_DBDataContext();
DAL.Plugin_Spotlight_List temp_spotlight_list = new Plugin_Spotlight_List();
temp_spotlight_list.Spotlight_List_Name = spotlight_list_name;
temp_spotlight_list.Spotlight_List_IsDeleted = false;
temp_spotlight_list.Spotlight_List_ForMall = false;
try {
ctx.Plugin_Spotlight_Lists.InsertOnSubmit(temp_spotlight_list);
ctx.SubmitChanges();
}
catch(Exception ex) { }
return temp_spotlight_list.Spotlight_List_Id;
}
Found the answer, you need to use: the OnInserted event of the asp:ObjectDataSource.
Here's an example:
protected void ObjDS_OnInserted(object sender, ObjectDataSourceStatusEventArgs e)
{
var ret_val = e.ReturnValue;
}
Sorry for the trouble. Should've researched more until I really can't think of a solution.
{
--
public static IEnumerable<Datarow> Codes(string topvalue)
{
DataTable itemCodes = new DataTable();
itemCodes.Columns.Add("itemId");
itemCodes.Columns.Add("itemCode");
itemCodes.Rows.Add(0, firstCallingCode);
DataTable Codes = GetAllItems().Tables[0];
foreach (DataRow item in Codes.Rows)
{
if (item["ItemCode"] != DBNull.Value)
{
itemCodes.Rows.Add(item.Field<int?>("itemId"), item.Field<string>("itemCode"));
}
}
return itemCodes.AsEnumerable();d
}
how can i bind it to dropdownlist: i tried this
ddcodes.datasource = codes.getenumerable();
ddcodes.databind();
when i do this i get error about typecast. i can not solve it tried a lot please help.
my method is actually this
public static IEnumerable"Datarow" Codes(string topvalue)
dont know why editor took that datarow off. bracket and datarow.
You just need to pass in the return value from the Codes method.
ddcodes.datasource = Codes();
ddcodes.databind();
You don't need to "get" an enumerable. The Codes method is already returning one.
I am developing a custom control that needs to display a dropdownlist as a composite control.
The drop down list gets populated from a Rest web service. The problem I am facing is that the dropdownlist only has DataTextField and DataValueField but I need a way of storing more values in the control i.e. I have a couple of other properties I need to access for the selected item.
What is the best way of going about this?
Here is the code I have so far:
[ValidationProperty("SelectedValue")]
public class SelectSurveyControl : Panel
{
private DropDownList ddlSurveys;
public string SelectedSurveyId
{
get
{
return SelectedValue;
}
}
public string SelectedSurveyJavascriptEmbedCode
{
get
{
return this.ddlSurveys.SelectedItem.Attributes[""];
}
}
public string SelectedValue
{
get
{
return ddlSurveys.SelectedValue;
}
set
{
if (ddlSurveys == null)
{
ddlSurveys = new DropDownList();
}
ddlSurveys.SelectedValue = value;
}
}
protected override void OnLoad(EventArgs e)
{
base.OnInit(e);
if (ddlSurveys == null)
{
ddlSurveys = new DropDownList();
}
IList<Survey> surveys = GetSurveys();
this.ddlSurveys.DataSource = surveys;
this.ddlSurveys.DataTextField = "title";
this.ddlSurveys.DataValueField = "id";
this.ddlSurveys.DataBind();
ddlSurveys.SelectedValue = this.SelectedValue;
ddlSurveys.CssClass = "umbEditorTextFieldMultiple charlimit";
ddlSurveys.Attributes.Add("SurveyId", SelectedSurveyId);
ddlSurveys.Attributes.Add("JavascriptEmbedingCode", SelectedSurveyId);
this.Controls.Add(ddlSurveys);
}
public IList<Survey> GetSurveys()
{
...
}
}
Try using a string join/split to store and retrieve the various values, then you don't have to customize your dropdown list very much.
For Example:
Text: Some Title
Value: 1|testing test|2/12/2010
This will let you store as many values as you want, so long as you choose an appropriate character to join and split on. I usually use the bar, as in my example above.
Side Note: I was looking at your selected value set handler and it needs some tweaking. You shouldn't check for a null drop down list, instead you should call EnsureChildControls() before each get and set instead. Make sure you override the CreateChildControls() method and create your controls there.
You could use a hidden field and iterate thru a copy of the returned Surveys like this:
foreach(Survey s in Surveys){
string val = s.id + ":" + s.<property1> + ":" + s.<property2>;
hiddenField.Value += val +",";
}
When you need to read from the hidden field, you use String.Split to separate the values into arrays using ',' as the separator and in each array, you split again using ':'.
In the first split Array1[0] who be the survey id and Array1[n!=0] would be the properties of the Survey with the id = Array1[0]. Array[n!=0] would then be split into Array2.
I would suggest handling empty property values with an empty string or something or else you might end up with unequal lengths especially if you specify StringSplitOptions.RemoveEmptyEntries.
Agricfowl
Is it possible to dynamically (and generically) clear the state of all of a user control's child controls? (e.g., all of its TextBoxes, DropDrownLists, RadioButtons, DataGrids, Repeaters, etc -- basically anything that has ViewState)
I'm trying to avoid doing something like this:
foreach (Control c in myUserControl.Controls)
{
if (c is TextBox)
{
TextBox tb = (TextBox)c;
tb.Text = "";
}
else if (c is DropDownList)
{
DropDownList ddl = (DropDownList)c;
ddl.SelectedIndex = -1;
}
else if (c is DataGrid)
{
DataGrid dg = (DataGrid)c;
dg.Controls.Clear();
}
// etc.
}
I'm looking for something like this:
foreach (Control c in myUserControl.Controls)
c.Clear();
...but obviously that doesn't exist. Is there any easy way to accomplish this dynamically/generically?
I was going to suggest a solution similar to Task's except (as sixlettervariables points out) we need to implement it as 1 extension method and essentailly switch on the precise type of the control passed in (i.e. copy your logic that you posted in your question).
public static class ControlExtensions
{
public static void Clear( this Control c )
{
if(c == null) {
throw new ArgumentNullException("c");
}
if (c is TextBox)
{
TextBox tb = (TextBox)c;
tb.Text = "";
}
else if (c is DropDownList)
{
DropDownList ddl = (DropDownList)c;
ddl.SelectedIndex = -1;
}
else if (c is DataGrid)
{
DataGrid dg = (DataGrid)c;
dg.Controls.Clear();
}
// etc....
}
}
It is not particularly elegent looking method but your code in your page/control is now the more succinct
foreach (Control c in myUserControl.Controls) {
c.Clear();
}
and you can of course now call control.Clear() anywhere else in you code.
You can do
foreach (Control c in myUserControl.Controls) {
myUserControl.Controls.Remove(c);
}
Because Controls is just a list, you can call Remove() on it, passing it what you want to remove.
EDIT: Oh I'm sorry, I didn't read it correctly. I don't know of a way to do this, maybe someone here who is good with Reflection could make it where you could do like
foreach (Control c in myUserControl.Controls) {
c = new c.Type.GetConstructor().Invoke();
}
or something, to turn it into a freshly made component.
I haven't tested it, but clearing viewstate for the usercontrol may work. You could expose a custom method on the user control as well:
usercontrol:
public void Clear()
{
this.ViewState.Clear();
}
page:
myUserControlInstance.Clear();
Now again I haven't tested. It's possible this will only clear the StateBag for the UserControl container, and not its nested/child controls.. if the above doesn't work you could try using recursion to walk down the control tree to clear viewstate for all children:
usercontrol:
public void Clear()
{
ClearViewState(this.Controls);
}
private void ClearViewState(ControlCollection cc)
{
foreach(Control c in cc)
{
if(c.HasControls())
{
//clear the child controls first
ClearViewState(c.Controls);
}
//then clear the control itself
c.ViewState.Clear();
}
}
page:
myUserControlInstance.Clear();
Just an idea. I haven't tested it but I think in theory it could work. One implication would be to call Clear at the correct point in the page/controls lifecycle, otherwise it may not work.
Hope this helps!
myUserControl.Controls.ToList().ForEach(c => myUserControl.Controls.Remove(c));
However, be careful, because you modify the iterating list. This could lead to some strange behaviour.
Setting EnableViewState="false" on the individual controls might save you the work, if it doesn't cause other problems for you in this instance.
What about the Control.ClearChildViewState method?
MSDN states
Deletes the view-state information for all the server control's child controls.
I have never used this though. So I am unsure if it will help you. Sounds good though, I think :)
Why not do as you suggest:
foreach (Control c in myUserControl.Controls)
c.Clear();
And then implement Clear:
public static class UserController
{
public static void Clear( this Control c )
{
c.Controls.Clear();
}
public static void Clear( this TextBox c )
{
c.Text = String.Empty;
}
}
That should do it.