Child UserControl TextBox value in Parent UserControl WPF - c#

I have a parent UserControl that has a Button. On button_click a child UserControl is added into Parent UserControl. That newly added Child UserControl have a TextBox.
Now, Parent UserControl has another Button when clicked i want to get the value of TextBox of child UserControl. How can i get that ?
Plus, let's say the child UserControl added on RunTime are more than 1. Then how can i get TextBox value of all of these child UserControls ?
EDIT and UPDATE !
Child UserControl have this method
public string GetText()
{
return ProductNameBox.Text;
}
Parent UserControl have this
public List<UserControl> UserControlList = new List<UserControl>();
public void NewProductModule(object sender, RoutedEventArgs e)
{
AddProductModule productModules = new AddProductModule();
UserControlList.Add(productModules);
}
And This method
private void PreviewPdfFunc(object sender, MouseButtonEventArgs e)
{
foreach (UserControl cnt in UserControlList)
{
MessageBox.Show(cnt +" Total = " + StackPanelContainer.Children.Count);
}
}

First of, add every AddProductModule you create to a list (because you have to store them somewhere)
List<AddProductModule> AllControls = new List<AddProductModule>();
AllControls.Add(YourItem);
Second, you have two Options to get the text
First: Set the button to public
Second: Create a public method which you can call from your Parent, for example
public string GetTBText()
{
return TextBoxExample.Text;
}
In the end, to get all Texts, you could
foreach(AddProductModule item in AllControls)
{
string ValueOfTB = item.GetTBText();
}
EDIT
There was the Problem, that a wrong object type was in use, which made Problems with my answer. Formerly, the type was not AddProductModule, instead it was UserControl, and before that it was var (which was the main Problem to begin with)
If you come here, you should now be able to run the code in my answer without a problem

Related

Winforms TextBox keeps value in UserControl

I am making an app in which there are multiple UserControls stacked onto each other. So the elements go like this: MainForm -> User clicks on a UserControl1 on the MainForm which (UserControl1) has a panel on which there is displayed another UserControl2 with a button. When the user clicks on it, it displays another UserControl3 which is then displayed in the panel beneath the button, where finally the user enters some text in the textbox. I need the data from the textbox in the MainForm so I have MainForm and UserControls connected via EventHandlers and pass my ResponseModel in which there is some dat a that I need to pass to MainForm. The first time this works, an item is created and displayed, after the item there is this "button" (User controls) displayed, in case the user wants to create another one. But then comes the problem when the user types in a different text for a new item, it creates an item with the same text!! Like the textbox was never changed (I have a debugging point set on the constructor to see every time that the textbox is empty). Below is some code and an image, for you to see how this should work. Also when I first delete the item it then doesn't work to create a new item for some reason.
This is how I send the data from the last UserControl:
if (tbx_list_name.Text == "")
MessageBox.Show("You can't create new list without a name!", "Can't create new list!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
else
CreateListTextBoxHandler?.Invoke(this, new ListCreationResponseModel() {
Code = id, ListName = tbx_list_name.Text
});
This is how I create the last UserControl which has the textbox:
control.CreateListTextBoxHandler += GetHandlerData;
panel.Controls.Add(control);
And this is how I get the data one stage down (this practice continues through couple more stages back to MainForm):
public void GetHandlerData(object sender, ListCreationResponseModel e)
{
try
{
panel.Controls.Clear();
CreateListButtonHandler?.Invoke(this, e);
}
catch (Exception ex)
{
_ = new ErrorHandler(ex);
}
}
You seem to have a recursion here. GetHandlerData is added to the CreateListTextBoxHandler event (or delegate) and invokes CreateListTextBoxHandler again, which will call GetHandlerData again...?? But tbx_list_name.Text is passed to the model only once at the top level down to all the other calls.
You can fix this by passing a reference to the textbox instead of the text itself. Then you will always be able to retrieve the current text of the textbox.
public class ListCreationResponseModel
{
private readonly TextBox _listNameTextBox;
public ListCreationResponseModel(TextBox listNameTextBox)
{
_listNameTextBox = listNameTextBox;
}
public int Code { get; set; }
public string ListName => _listNameTextBox .Text;
}
Now, when you retrieve the ListName you don't get a stored value but the actual text of the textbox.
You can create the handler like this:
CreateListTextBoxHandler?.Invoke(this, new ListCreationResponseModel(tbx_list_name) {
Code = id
});

How to programmatically add a Custom Control to a Form and show it?

I'm trying to add a Label to a Windows Form by using another class programmatically. My Label does not appear inside the Form.
I don't know where I'm going wrong.
private void Form1_Load(object sender, EventArgs e)
{
Ticker ticker = new Ticker("ASDF");
ticker.display();
}
public class Ticker : Label
{
string labelText;
Label label = new Label();
public Ticker(string _labelText)
{
labelText = _labelText;
}
public void display()
{
label.Text = labelText;
Controls.Add(label);
}
}
You can make a few changes to your Ticker Custom Control:
You don't need to create a new Label inside your Custom Control: your Control is already a Label, use the this reference to set its properties (see also this keyword (C# Reference)).
The Text is the current Label's Text (this.Text). Store it if you need a copy of it for other reasons (custom painting, usually, so sometimes you need to clear the Text).
Controls is referring to the current class object: it's a Control, so it has a Controls property, which gets the ControlCollection of the child Controls of a Control.
You need to also specify a Point that defines the position of your Custom Control inside its Parent's ClientRectangle.
Even if it's not always required, add a parameter-less Constructor to your Custom Control: if/when it's actually needed, you'll have it already there.
If you don't want to set the Parent Control from the outside, as usual (e.g., var label = new Label(); this.Controls.Add(label);), you need to pass the reference of the Control which will become the Parent Control of your custom Label.
You can the use this reference - a Control type of reference - and add your Label to the Controls collection of the Control reference you receive:
// You want to store a reference to this Control if you need it later...
private Ticker ticker = null;
private void Form1_Load(object sender, EventArgs e)
{
//... or just declare it with: var ticker = new Ticker() if you don't
ticker = new Ticker("The Label's Text");
// [this] of course refers the current class object, Form1
ticker.Display(this, new Point(100, 100));
// Or, display the Label inside a Panel, child of Form1
// Note: if you don't comment the next line, the Label will be moved to panel1
ticker.Display(this.panel1, new Point(10, 50));
}
Here, I'm overloading the Display() method, so it accepts both a Parent reference and a Point value, used to position the Control inside its Parent's Client Area.
The Custom Label also calls BringToFront() on itself, to avoid showing up under some other, already existing, child Control of the new Parent.
public class Ticker : Label
{
public Ticker() : this("ticker") { }
public Ticker(string labelText) => this.Text = labelText;
public void Display(Control parent) => Display(parent, Point.Empty);
public void Display(Control parent, Point position)
{
this.Location = position;
parent.Controls.Add(this);
this.BringToFront();
}
}

How do I clear a user control from a winform?

This is probably a basic question, but I can't find answers because the terms are generic.
I am building a WinForm aplication. Its purpose is to set up memory in a certain chip. I think the best way to organize the application is to have a user control for each chip type, derived from a generic parent class. Think of the children as "iphone," "android" and "blackberry," derived from a parent class "phone".
VS2017 Designer has a Panel where I want the control to be. On startup, I generate an object of the base class and add it to the panel. When I press a button, the old object is deleted and replaced with a new one. Each class has just one control, a label with distinctive text.
The problem is, after I press the button, I see both texts. The panel's Controls collection has just one element, but I see the text from both objects. I have tried Refresh, Update and Invalidate withe the same results.
What do I have to do to make the old text "go away" so the only thing I see is the latest object?
private ChipMemBase ChipMemControl = new ChipMemBase();
public Form1()
{
InitializeComponent();
//tbFeedback.Text = string.Format(fmtString, 0, 1, 2, 3, 4, 5);
cbChipName.SelectedIndex = 0;
tbVersion.Text = Version;
OriginalWindowColor = tbFeedback.BackColor;
ShowChipMemControl();
PrintToFeedback(Version);
}
private void ShowChipMemControl()
{
var ctl = pnlChipMem.GetChildAtPoint(new Point(5,5));
if (null != ctl)
{
if (ctl != ChipMemControl)
{
pnlChipMem.Controls.Remove(ctl);
ctl.Dispose();
pnlChipMem.Update();
Refresh();
}
}
if (null != ChipMemControl)
{
pnlChipMem.Controls.Add(ChipMemControl);
}
}
private void btnMakeChipMemory_Click(object sender, EventArgs e)
{
ChipMemControl = new ChipMemGen2();
ShowChipMemControl();
}
Screenshots before and after clicking Create
Your ShowChipMemControl gets the control at point 5,5 and checks if it's a ChipMemControl then removes it.
I'm guessing that the reason it's not getting removed is that the control at point 5,5 is not a ChipMemControl.
You can use:
pnlChipMem.Controls.Clear()
to remove all the controls
Or:
ChipMemControl cmc = pnlChipMem.Controls.OfType<ChipMemBase>().FirstOrDefault();
if (cmc != null)
{
pnlChipMem.Controls.Remove(cmc);
cmc.Dispose();
}
To only remove the first instance of ChipMemBase on your pnlChipMem panel.
Got it. The problem was from inheritance, not window behavior. Control lblDefault in the base class, carrying the inconvenient text, was still present in the child class. I had to make it Public in the base class and remove it in the child class constructor:
InitializeComponent();
Controls.Remove(lblDefault);
lblDefault.Dispose();
lblDefault = null;
The clue was this article and project:
dynamically-and-remove-a-user-control

Implementing an options dialog

in my application i want to implement an options dialog like you have in VisualStudios if you go to Tools->Options in the menubar. How can i do this? My first idea was to use pages and navigation but maybe there's an easier approach?
It's probably not the easiest way but I wrote this snippet that match your goal and it's a good exercise.
In an empty Windows Forms project add a ListBox (listBox1) and a Panel (panel1). Then create 2 UserControls (UserControl1 and UserControl2), these will be the content that is shown when you click the list.
In your Form1 class we create a ListItem class that will contain your menu options as such:
public partial class Form1 : Form
{
public class ListItem
{
public string Text { get; set; }
public UserControl Value { get; set; }
public ListItem(string text, UserControl value)
{
Text = text;
Value = value;
}
};
...
}
After that you add items to the ListBox right after InitializeComponent() in Form1:
public Form1()
{
InitializeComponent();
listBox1.DisplayMember = "Text";
listBox1.ValueMember = "Value";
listBox1.Items.Add(new ListItem("Item1", new UserControl1()));
listBox1.Items.Add(new ListItem("Item2", new UserControl2()));
}
This will make it so when you use listBox1.SelectedItem it will return an object that you can cast to a ListItem and access the associated UserControl.
To make use of this behaviour, go to designmode and double-click the ListBox, this'll add code for the SelectedIndexChanged event. We use this event to display the UserControl in the Panel panel1. This will clear any old Panel content and add a selected UserControl:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
panel1.Controls.Clear();
UserControl control = (listBox1.SelectedItem as ListItem).Value;
if(control != null)
{
panel1.Controls.Add(control);
control.Dock = DockStyle.Fill;
}
}
I suggest you try adding a button or something to differentiate the UserControls and play around. Have fun! :)
You should create a new Window and show that as opposed to create a page and navigate to it. Then you would call .show() on the new window for it to show.
Then you would change the look of the new window to however you want, the same as editing pages.
If you build your options into a full object model that matches the structure of the options window, then the best way is to use whatever navigation-aware UI binding that your MVVM toolkit uses. The options window would start off as a new root level window to which you would bind the root of your options data model.
So, in short think of the options dialog as a mini-application that uses the same structure as your main MVVM application, but with a different data model root.
If you plan to allow the user to cancel the changes to the options, then you would want your options data model to be clonable so that you can populate the options window with the clone and then swap out the real options with the new data if the user presses OK on the options window. If they select cancel you can just throw the cloned object away and destroy the window.

creating a List of Custom Controls at run-time using the context menu strip Item Click Event

I am working on a custom control that I want to add instances of using the right click context menu item click event.
the only trouble I'm having is setting the position of control on the form here is what I have so far.
public partial class frmMain : Form
{
List<GenericNode> nodeTree;
GenericNode node;
public frmMain()
{
InitializeComponent();
}
private void testItemToolStripMenuItem_Click(object sender, EventArgs e)
{
//place node item in a list with ID so that I can add many unique items
node = new GenericNode();
nodeTree = new List<GenericNode>();
nodeTree.Add(node);
node.Name = "node" + nodeTree.Count;
//set the Location of Control based on Mouse Position
if I try to set the value of node.Location.X orY it tells me that I can't assign the value to it because it's a return value.
//add item from list to form
this.Controls.Add(nodeTree.ElementAt(nodeTree.Count -1));
}
}
Ok I've come back to this and saw nobody had any answers so I came up with a quick dirty one in case you were all scratching your heads.
public partial class frmMain : Form
{
List<GenericNode> nodeTree;
GenericNode node;
Point location;
public frmMain()
{
InitializeComponent();
}
private void testItemToolStripMenuItem_Click(object sender, EventArgs e)
{
//place node item in a list with ID so that I can add many unique items
node = new GenericNode();
nodeTree = new List<GenericNode>();
nodeTree.Add(node);
node.Name = "node" + nodeTree.Count;
//place the current object on the form at given location
location = new Point(MousePosition.X, MousePosition.Y);
node.Location = location;
this.Controls.Add(nodeTree.ElementAt(nodeTree.Count - 1));
}
}
I still can't get it to line up with my mouse though, I've tried the offsets, but It is consistently off by just a little bit.
The MousePosition you are using will be in screen coordinates, so you need to translate them to the Form's coordinates:
node.Location = PointToClient(MousePosition);
Note that the MousePosition you will see at the time the menu click event fires will be the location of the mouse when you click the menu item, not the location when right click to open the context menu.
A couple of other comments:
Don't create a new nodeTree in the click event - that will remove the previous tree and the count will never be over 1. Create it in the constructor or in the line where you define the class member.
Don't create class member variables for "node" or "location" they are only used in the click event handler, and should be defined there.
In the last line, you don't need to retrieve the node from the list, you already have a reference to it.
public partial class MainForm : Form
{
private List<GenericNode> nodeTree;
public MainForm()
{
InitializeComponent();
nodeTree = new List<GenericNode>();
}
private void testitemToolStripMenuItem_Click(object sender, EventArgs e)
{
GenericNode node = new GenericNode();
nodeTree.Add(node);
node.Name = "node" + nodeTree.Count;
node.Location = PointToClient(MousePosition);
this.Controls.Add(node);
}
}

Categories