How can I add more than custom control using code behind? - forms

Here I create two objects form my Custom control and add them to the form but it show the first one only, why ?
private void frmWelcome_Load(object sender, EventArgs e)
{
// Count number of Contacts in XML
XmlDocument doc = new XmlDocument();
doc.Load(#"C:\Users\Taha\Documents\Visual Studio 2013\Projects\ContactsMangementSystem\ContactsMangementSystem\ContactsData.xml");
int numOfContacts = doc.GetElementsByTagName("Contact").Count;
XmlNodeList nodelist = doc.GetElementsByTagName("Contact");
foreach (XmlNode item in nodelist)
{
// Create Control for each Item
ContactControl.ContactControl userControl = new ContactControl.ContactControl();
// Fill Control With Data
userControl.labelOrganizationText = item.ChildNodes.Item(5).InnerText;
userControl.labelTitleText = item.ChildNodes.Item(0).InnerText;
userControl.labelWorkAddressText = item.ChildNodes.Item(12).InnerText;
userControl.labelWorkPhoneText = item.ChildNodes.Item(8).InnerText;
userControl.labelEmailText = item.ChildNodes.Item(7).InnerText;
Image img = Image.FromFile(item.ChildNodes.Item(9).InnerText);
userControl.ContactImage = img;
// Add item to the form
this.Controls.Add(userControl);
}
ContactControl.ContactControl userControl2 = new ContactControl.ContactControl();
this.Controls.Add(userControl2);
}

If you add controls all by yourself, aka programmaticaly, to the Control collection of a form, you become responsible for handling the position, bounds, size etc. I'll demonstrate the root cause of your issue with a trimmed down code example that is similar to yours.
First I create a UserControl, with one label on it. Set the label property Dock to Fill, Font to Point 28, TextAlign to MiddelCentre and most important Modifiers to Public:
Now we create a new Form in the Designer and add a Timer, a Button, two checkboxes and a FlowLayoutPanel to it. The FlowLayoutPanel has its Anchor property set to Top,Left,Right,Bottom so it will resize if the form resizes. Our design looks like this:
In the Timer_Tick event a new instance of the UserControl is created and either added to the Forms Control collection or the FlowLayoutPanel. There are two variables that keep track of the number of controls created and on which position the control needs to be.
Here is the code in the codebehind of the form:
int position = 0; // what position the control should be
int controlsCreated = 0; // how many controls did we create
private void timer1_Tick(object sender, EventArgs e)
{
// create the control
var ucnext = new UserControl1();
controlsCreated++;
ucnext.label1.Text = controlsCreated.ToString();
// where are we adding the control?
if (!chkFlow.Checked)
{
// do we want to use the Location?
// if not Top will be 0
if (chkLocation.Checked)
{
// Ok, position times height should gives us
// a free spot
ucnext.Top = position * ucnext.Height;
position++;
}
this.Controls.Add(ucnext);
ucnext.BringToFront(); // otherwise we always see control 1
} else
{
// now the FlowLayout takes over all Layout logic
this.flowLayoutPanel1.Controls.Add(ucnext);
}
}
private void button1_Click(object sender, EventArgs e)
{
this.timer1.Enabled = !this.timer1.Enabled;
this.btnStart.Text = this.timer1.Enabled ? "Pause" : "Continue";
}
If we run this we can check and uncheck the options for Location and Adding to the flowlayout to see the various effects of adding controls to either the form directly or the FlowLayout panel.
So keep in mind if you add a control yourself to the Control collection of a Form make sure to set its Location properties (Top, Left) to a suitable value. If you do this wrong your control is going to end up hidden behind another control or is placed outside the visible bounds of the form. If you don't know before hand how many controls you will have consider using one of the container controls, like a FlowLayoutPanel.

Related

Unity how to Create Texts via script?

I'm trying to create Texts via script. I want to create texts with the name of objects of a certain tag.
"For example, if I have two objects named Cube and Sphere and both have a tag of "TargetObj" then their names should be displayed as texts on the screen(in the case Cube, Sphere)"
I want to achieve this regardless of the number of objects. so a loop is needed.
Here is what I've tried so far.
[SerializeField] GameObject LevelCanvas;
targetObjects = GameObject.FindGameObjectsWithTag("TargetObj");
foreach (var obj in targetObjects)
{
Text mytext = LevelCanvas.AddComponent<Text>();
mytext.text = "Find " + obj.name;
Font ArialFont = (Font)Resources.GetBuiltinResource(typeof(Font), "Arial.ttf");
mytext.font = ArialFont;
mytext.material = ArialFont.material;
}
it only shows one object name although I have 2 more objects that were supposed to be shown as well.
You need to create a text object on a new gameobject.
static Text CreateText(Transform parent)
{
var go = new GameObject();
go.transform.parent = parent;
var text = go.AddComponent<Text>();
return text;
}
var mytext = CreateText(LevelCanvas.transform);
myext.text = ...
Many components in unity can only be added once per game object. That means you need a separate game object for every text you want to show.
A typical way to approach this is to create a "prefab" of a game object which has a text element already. This way you can set a default font and other settings.
Then in code you use the Instantiate method on that prefab and via GetComponent() you can access the text component instance to set the desired string to display.

Creating multiple buttons using input field in Unity

I just want to ask how can I multiple the buttons by using the input field? Like for example, I typed 100 in the input field and then the buttons will become 100. Please bear in mind that I'm new in Unity. Thank you!
First you need to define what button you want to instantiate, on the UI Canvas or a 3D world button.
public class TestingScript : MonoBehaviour{
// the button to create
public GameObject button;
}
You need to create a prefab or assign an existing button in the world for the script to use as a template when creating them. Next you need to create the input field itself and assign the reference to it through code or in the inspector view, here I am going to have it assigned in the inspector.
// assigned in inspector view
public TMP_InputField inputField;
It should look like this in the component view
Now where you call the function is your choosing, I am doing it through a unity button on which I have the script attached
The method here would look something like this
public void CreateButtons()
{
// here we assume the input is always an integer, otherwise you would create a try catch clause
int amount = int.Parse(inputField.text);
// using world 0 position here, you would need to define where you want them yourself
Vector2 pos = new Vector2(0, 0);
for (int i = 0; i < amount; i++)
{
// the loop will execute the amount of times read from input field
Instantiate(button, pos, Quaternion.identity);
pos.x += 0.5f;
}
}
Note I am using TextMeshPro elements here but the standard unity ones would work too.

Unity, scalling the width of a dynamically created Gameobject in a a scroll view content box

I am trying to add 100 dynamically created buttons to a scroll view in Unity, but I have a problem letting the scroll view automatically adjust the width of the buttons to match the width of my screen.
When I tried to add the buttons manually it worked fine , but when I do this by code I get another results.
The code I am using :
public GameObject button;
public GameObject scrollviewcontents;
void Start()
{
for (int i =0; i<=100;i++) {
GameObject dbutton = Instantiate(button);
dbutton.name = i.ToString();
dbutton.transform.parent = scrollviewcontents.transform;
}
}
and The results I get :
Results
Results with comments
I just want the buttons to look like as they are added manually, any help ???
By default when instantiating an object the object keeps the same world space position, rotation and scale as before. try this instead:
dbutton.transform.SetParent(scrollviewcontents.transform, false);

NSTableRowView not calling DrawBackground correctly during drag-drop operations

I have an NSTableView with a set of rows. There are two types of rows - titles and data - which are displayed differently.
I am dragging an object from within my app onto the data rows. Drag and drop works fully and only data rows accept the drop - but there is a display issue.
I am using a custom NSTableRowView, and what I expect to see is that if I drag my object over a data row, then DrawDraggingDestinationFeedback should be called (it is) to draw my custom background used during drag operations. If I then drag my object out of the data row, I expect DrawBackground to be called (it is SOMETIMES) to redraw my standard background.
Heres the detail:
If I drag my object onto a data row and then completely away from the NSTableView, DrawDraggingDestinationFeedback is called on entry and DrawBackground is called on exit.
If I drag my object onto a row, and then down to the next row,
DrawDraggingDestinationFeedback is called for the top row, then when I move down DrawDraggingDestinationFeedback is called for the lower row, but DrawBackground is not called for the first row (i.e. its background color is not reset). This leaves BOTH rows looking like they are active drop destinations.
Weirdly, the final DrawBackground on exiting the table is called.
Here is my custom NSTableRowView:
public class CustomSelectionRowView : NSTableRowView
{
/// The log object
private static Logger log = LogManager.GetCurrentClassLogger();
private string type;
private DataItem ditem;
public CustomSelectionRowView(string type, DataItem ditem = null) : base()
{
this.type = type;
this.ditem = ditem;
DraggingDestinationFeedbackStyle = NSTableViewDraggingDestinationFeedbackStyle.Regular;
SelectionHighlightStyle = NSTableViewSelectionHighlightStyle.Regular;
}
public override void DrawDraggingDestinationFeedback(CGRect dirtyRect)
{
log.Debug(ditem.title);
if (type == "Data")
{
// This can accept a drag-drop
var color = NSColor.Gray;
var p = NSGraphicsContext.CurrentContext.GraphicsPort;
p.SetFillColor(color.CGColor);
p.FillRect(dirtyRect);
}
else
base.DrawDraggingDestinationFeedback(dirtyRect);
}
public override void DrawBackground(CGRect dirtyRect)
{
log.Debug(ditem.title);
if (type == "Data")
{
// We definitely have a Data block
var color = NSColor.FromName("MainPaneDataBackground");
var p = NSGraphicsContext.CurrentContext.GraphicsPort;
p.SetFillColor(color.CGColor);
p.FillRect(dirtyRect);
}
else
base.DrawBackground(dirtyRect);
}
}
Anyone have any ideas of how to work around this?
Many thanks.
[p.s. note that while my code here is C#/Xamarin... any swift help would be gratefully accepted - I speak both]

Get position of object in Grid Layout

How can I get the actual position of an object in a grid layout? In my current iteration symbolPosition keeps returning 0,0,0.
public void OnPointerClick (PointerEventData eventData)
{
// Instantiate an object on click and parent to grid
symbolCharacter = Instantiate(Resources.Load ("Prefabs/Symbols/SymbolImage1")) as GameObject;
symbolCharacter.transform.SetParent(GameObject.FindGameObjectWithTag("MessagePanel").transform);
// Set scale of all objects added
symbolCharacter.transform.localScale = symbolScale;
// Find position of objects in grid
symbolPosition = symbolCharacter.transform.position;
Debug.Log(symbolPosition);
}
The position value wont be updated until the next frame. In order to allow you to make a lot of changes without each one of them causing an entire recalculation of those elements it stores the items that need to be calculated and then updates them at the beginning of the next frame.
so an option is to use a Coroutine to wait a frame and then get the position
public void OnPointerClick (PointerEventData eventData)
{
// Instantiate an object on click and parent to grid
symbolCharacter = Instantiate(Resources.Load ("Prefabs/Symbols/SymbolImage1")) as GameObject;
symbolCharacter.transform.SetParent(GameObject.FindGameObjectWithTag("MessagePanel").transform);
// Set scale of all objects added
symbolCharacter.transform.localScale = symbolScale;
StartCoroutine(CoWaitForPosition());
}
IEnumerator CoWaitForPosition()
{
yield return new WaitForEndOfFrame();
// Find position of objects in grid
symbolPosition = symbolCharacter.transform.position;
Debug.Log(symbolPosition);
}
This may not have been in the API when this question was asked/answered several years ago, but this appears to work to force the GridLayoutGroup to place child objects on the frame in which child objects are added, thus removing then need for a coroutine.
for(int i = 0; i < 25; i++){
GameObject gridCell = Instantiate(_gridCellPrefab);
gridCell.transform.SetParent(_gridLayoutTransform,false);
}
_gridLayoutGroup.CalculateLayoutInputHorizontal();
_gridLayoutGroup.CalculateLayoutInputVertical();
_gridLayoutGroup.SetLayoutHorizontal();
_gridLayoutGroup.SetLayoutVertical();
From here, you can get the positions of the 'gridCells' same frame.
There is another function, which seems like it should fulfill this purpose, but it isn't working for me. The API is pretty quiet on what exactly is going on internally:
LayoutRebuilder.ForceRebuildLayoutImmediate(_gridLayoutTransform);