Usercontrol that dynamically generates a list of controls on postback - c#

I want to create a reusable user control that allows the user to create and remove tags.
It should have a textbox where the user can enter a tag name, and a button to submit the tag.
After sumbitting the tag, it should dynamically create a label with the tag name with a "X" button to remove it for every entered tag.
Something like this:
[ Textbox ] [Submit]
[X] Tag 1
[X] Tag 2
[X] Tag 3
The issue is that I cannot figure out how to make everything work properly.
Selector.ascx
< markup for the textbox >
< markup for the sumbit button >
<asp:Placeholder runat="server" ID="PHList" />
I'm using an asp:Placeholder to generate the dynamic controls.
Selector.ascx.cs
I need to keep track of the generated controls between postbacks.
I'm using a List<string> stored in the viewstate:
private List<string> SelectedTags
{
get
{
return (List<string>) ViewState["SelectedTags"];
}
set { ViewState["SelectedTags"] = value; }
}
When I click the submit button, I add a new tag to the SelectedTags list, and call the RefreshPlaceholder function:
void RefreshPlaceholder()
{
PHList.Controls.Clear();
foreach(var x in SelectedTags)
{
// generate a label and the "X" button, wiring it with a lambda
}
}
When I click the submit button, a new (button, label) pair is correctly generated and displayed after postback.
When I click any of the generated "X" buttons, however, I get an error because the click event fired by the dynamically generated button cannot find the sender button anymore, since it hasn't been generated yet.
If I try to generate the buttons in OnLoad, the same issue occurs, as postback events occur before Page.Load.
If I try to generate the buttons in OnInit, the ViewState has not been updated yet, so the generated list will be outdated as the SelectedTags viewstate list is not yet updated.
I can't seem to find a solution to make everything work as intended. What am I doing wrong?

Declare a List of type Buttons on OnInit method; then in RefreshPlaceHolder retrieve and loop through selected tags and add them to the List
protected override void OnInit(EventArgs e)
{
List<Button> listButtons ;
base.OnInit(e);
}
void RefreshPlaceholder()
{
listButtons = new List<Button>();
PHList.Controls.Clear();
foreach(var x in SelectedTags)
{
Button b = new Button();
b.Text=x;
listButtons.add(b);
// generate a label and the "X" button, wiring it with a lambda
}
}

Related

How to make a StackPanel reorder when using a Search Bar (TextBox) in WPF?

CONTEXT:
I have a StackPanel with Buttons inside. These buttons are created, named and put inside the stackPanel programmatically. Then I have a TextBox serving as a Search bar with a button that is utilised as search button.
WHAT I WANT TO DO:
I want that, when the TextBox is filled with the Text of certain Button's Label and the Search Button is clicked, the StackPanel removes the buttons that do not seem to match with the TextBox content and then, when I erase the TextBox's content, reappear (I don't mind if it's in the same order than before or not).
PROBLEM:
I have two main problems, both related to the same: when the Search Button is clicked, it succeeds nothing. Invoking a Button by FindName method doesn't seem to work (it gives me a null . So, my question is: Is it possible to make the StackPanel reorder as I put before? In that case, how would I do that calling the Button created programmatically?
CODE:
private void buscarEntrada_Click(object sender, RoutedEventArgs e)
{
//Search button's click event
//All the buttons are named "Btn" + a constantly increasing number (0,1,2,3...)
//stackBotones is my StackPanel, I remove the buttons and then readd them depending on the textbox's content
try
{
for (int i = 0; i < stackBotones.Children.Count; i++)
{
stackBotones.Children.Remove((UIElement)stackBotones.FindName());
}
}
catch (Exception error)
{
MessageBox.Show(error);
}
}
Thank you in advance for reading this.
You can find a child element by its name in the Children collection like this:
string name = txt.Text;
Button button = stackBotones.Children.OfType<Button>().FirstOrDefault(x => x.Name == name);
If you want to remove all elements but the matching one, you could do this in a loop:
string name = "b";
for(int i = stackBotones.Children.Count - 1; i>=0; i--)
{
if (stackBotones.Children[i] is FrameworkElement fe && fe.Name != name)
stackBotones.Children.RemoveAt(i);
}
You may add stackBotones.Children[i] to a list that you then use to repopulate stackBotones.Children based on your logic.

Bindingsource won't update after the textbox Conrol updates

I have a form which its controls are binded to a bindingsrource named "userbindingsource". When the form loads all the value in bindingsrource will be set to equivalent Textbox. But when the textboxes values change , the bindingsrource won't update. and still it shows the very first value
For example after load I change the first name in textbox and then click save Button to call the saveRecord(). when I check (userBindingSource.DataSource as User) , it still contains the first firstname without any changes.
public void SaveRecord()
{
int i = 0;
User user = userBindingSource.DataSource as User;
if (user.Id > 0)
user.State = State.Modified;
using (ECarServiceClient client = new ECarServiceClient())
{
i = client.SaveUser(user);
}
}
how can make the bindingsource updates automatically?
If the textbox binding has DataSourceUpdateMode.OnValidation (the default) and you go right from typing in a textbox to clicking the Save button, the Save click is handled before the textbox validates. Validation is what makes the BindingSource update the User object.
The fix would be to call this.Validate() at the beginning of SaveRecord. This triggers validation of the focused control within the current form and all its ancestors.

Change Value of a Dynamically Created Text Box when button is clicked C#

I have dynamically created a button using a function I made which is meant to increase the value of another button created using a similar function. I'm able to retrieve the name of the dynamically created textbox, but since it is dynamically created, I can't reference it.
I've tried to pass the textbox through as a parameter, but it doesn't appear that I'm able to pass extra parameters:
Button ButtonDecreaseValue= addButton(ItemName, "-");
TextBox TextBoxItemAmount = addTextBox(ItemName, "0");
So, how would I change the value of textbox one when ButtonRemoveItem is clicked?
I've created an event handler for the button, like so:
ButtonIncreaseValue.Click += new EventHandler(ItemDecreased);
But, inside the event handler, I'm unsure how I would change the textbox name.
private void ItemDecreased(object sender, EventArgs e)
{
Button currentItem = (Button)sender;
int ItemNo = Convert.ToInt32(currentItem.Tag);
DataRow ItemInfo = getItemDetails(ItemNo);
ItemInfo[0].ToString();
}
When clicking the button, the corresponding textbox should decrease in value.
Assuming this is Windows Forms
You said you have the name of the TextBox? You should be able to find it with that.
private void ItemDecreased(object sender, EventArgs e)
{
string textBoxName = HoweverYouGetTheDynamicTextBoxName();
// Assumptions:
// 1. ItemDecreased is a method on a Form or a UserControl.
// 2. There is only one TextBox with the given name in the Form or UserControl.
// 3. The TextBox is added to the Form or UserControl's Controls property.
TextBox tb = Controls.Find(textBoxName, true).OfType<TextBox>().SingleOrDefault();
if (tb is null)
{
// Couldn't find the text box for some reason.
}
// tb references the dynamically created text box.
}

Add dynamic ASP.NET controls via a DropDownList to the page

- Primary Info:
In my recent project, I need to have a page with a DropDownList with some items like 'firstName' , 'lastName' , 'Age' and etc. I want to add optional controls to the page when every item selected by user. For example when user select the 'Age' another dropdownlist created dynamically with these values : 'Less than 10'
'Between 10 and 30'
'more than 30'
Here is a button that add this user selection to listBox and let user to choice another options. (I made a query at last according to user choices and send it to db)
- What I do:
I create a dropDownList and set it's AutoPostBack property to true and adds some items in it and user must select one of those item. then I add user SelectedValue of dropDownList in a Cache variable before page post back happens:
protected void DropDownListColumnNameSelectedIndexChanged(object sender, EventArgs e)
{
Cache["SelectedKey"] = dropDownListColumnName.SelectedValue;
}
When user select an item from dropDownList *DropDownList_SelectedIndexChanged* fire, and I must create controls dynamically in a place holder:
var textBoxName = new TextBox
{
ID = "textBoxName",
CssClass = "str-search-textbox-highlight",
ViewStateMode = ViewStateMode.Disabled
};
placeHolderFirstItem.Controls.Add(textBoxName);
- What is the problem?
When I try add new control in current Button_Click event, control added successfully to page but I can't find it by placeHolderFirstItem.Controls.Find("textBoxName") actually placeHolderFirstItem.Controls.Count is always zero. So I can't get textBoxName.Text values.
I try to google that for any solution and I found some solution that I must add controls in Page.OnInit so I add controls in overridden OnInit(e):
protected override void OnInit(EventArgs e)
{
if (!Page.IsPostBack) return;
var textBoxName = new TextBox
{
ID = "textBoxName",
CssClass = "str-search-textbox-highlight",
ViewStateMode = ViewStateMode.Disabled
};
placeHolderFirstItem.Controls.Add(textBoxName);
}
after doing this I can find "textBoxName" in placeHolderFirstItem, but it fire before DropDownList_SelectedIndexChanged !
so how can I add new controls to place holder exactly when user change the dropDownList value and how can I read new controls value?
Thanks in advance,
Mohsen.
- Updated:
Here is the better solution
(http://forums.asp.net/p/1959726/5596531.aspx?p=True&t=635244790943067485&pagenum=1)
When you are dynamically adding controls, you have to reload the controls into the control tree everytime thereafter for it to appear. With the help of viewstate, you could change your code sample to have:
ViewState("ShowTextbox") = true
And then in your init routine:
protected override void OnInit(EventArgs e)
{
if (!Page.IsPostBack) return;
if (ViewState("ShowTextBox") == true) {
var textBoxName = new TextBox
{
ID = "textBoxName",
CssClass = "str-search-textbox-highlight",
ViewStateMode = ViewStateMode.Disabled
};
placeHolderFirstItem.Controls.Add(textBoxName);
}
}
Please note it's much easier to have a control on the control tree, and then show/hide by setting Visible to true/false, because of these ASP.NET control tree issues.

ASP.NET dynamic Command Button only fires every other time

Similar to this question here ASP.Net Dynamic Command Button Event Not Firing but with a slightly different problem.
Provided below is a (very) condensed version of my code.
protected void Page_Load(object sender, EventArgs e)
{
RenderDataItems();
}
private void RenderDataItems()
{
pnlDataItems.Controls.Clear()
DataTable dt = MyClass.GetAllData();
foreach (DataRow dr in dt.Rows)
{
Button b = new Button();
b.Command += new CommandEventHandler(SelectItem);
b.CommandArgument = dr["ID"].ToString();
b.ID = "btnData" + dr["ID"].ToString();
if (hdnDataListID.Value == dr["ID"].ToString())
{
b.Text = "Selected Item";
}
else
{
b.Text = "Pick This Item";
}
pnlDataItems.Controls.Add(b);
}
}
private void SelectItem(object sender, CommandEventArgs e)
{
hdnDataListID.Value = e.CommandArgument.ToString();
RenderDataItems();
}
private void EditSelectItem(int id)
{
MyClass mc = new MyClass(id);
hdnDataListID.Value = mc.ID.ToString();
RenderDataItems();
}
The method SelectItem is only called by the button controls rendered within the RenderDataItems method. The EditSelectItem is called by a separate control that is created dynamically, but does not require the altering that the buttons in the RenderDataItems method requires.
I've run the debugger and stepped through the code to see what happens. When the page is loaded, the RenderDataItems is called from the PageLoad and populates the panel with all of the buttons having "Pick This Text" (because the HiddenField control's value (hdnDataListID) has not been set).
The first time I click one of the buttons, the RenderDataItems from PageLoad fires, followed by the initial population of the buttons, the HiddenField's value is set to the ID, and the second RenderDataItems call happens from within the SelectItem method. The buttons are cleared, and recreated. The correct button has the "Selected Item" text.
The second time I click one of the buttons, the RenderDataItems from PageLoad fires, followed by the initial population of the buttons, but the SelectItem method never fires.
The third time I click one of the buttons, the same functionality happens as the 1st time. The 4th mimics the 2nd. The 5th mimics the 1st. And so on, and so on.
When using the EditSelectItem method from the controls not contained within the panel (it's a DataSource bound GridView row with buttons that calls this method), it does exactly as I'd expect and properly sets the selected / unselected buttons, with calls to both the RenderDataItems and the EditSelectItem method every time.
Any ideas?
P.S. I've already removed all of my AJAX on this page.
You should give your Button b an ID.

Categories

Resources