Dynamic list of buttons in Unity UI Scrollview not showing - c#

I have a canvas which contains a panel housing a scroll view. (The scroll view contains the default Viewport -> Content setup). I don't think it's relavent but I removed the scroll bars.
I want to display a dynamic list of prefabs (buttons) in the scroll view. When debugging, I can see that the prefabs are being created correctly with the Content as the parent, but nothing shows up in the scrollview.
I followed a tutorial that had me add Vertical Layout Group and Content Size Fitter components to the Content GameObject. I tried to play around with the options in those components while running to see if I had them misconfigured, but the prefabs were never displayed.
Before running:
After running:
Shouldn't really matter much but here's the code I am using for testing. (In Start() - and I know this isn't the most efficient.)
for (int i = 0; i < 5; i++)
{
Game game = new Game
{
ID = i.ToString(),
OpponentID = i.ToString(),
OpponentImage = "pig",
OpponentName = "Test " + i,
OpponentScore = new Score { Pigs = 2, Bulls = 1 },
Points = 350,
Score = new Score { Pigs = 1, Bulls = 1 },
Status = "active",
Turn = "6XMvi7Xv2maXDdFN7dwrhLaMrkE3"
};
var gameObj = Instantiate(gamePrefab, scrollViewContent.transform, false);
var canvas = gameObj.transform.Find("Canvas").GetComponent<Canvas>();
canvas.enabled = false;
var gameObjButton = canvas.transform.GetChild(0);
gameObjButton.Find("OpponentInfo").Find("OpponentName").GetComponent<Text>().text = game.OpponentName;
gameObjButton.Find("OpponentInfo").Find("OpponentScore").GetComponent<Text>().text = game.OpponentScore.ToString();
// // gameObj.transform.Find("OpponentProfilePicture").GetComponent<Image>()
gameObjButton.Find("OpponentInfo").Find("Status").GetComponent<Text>().text = GameObject.Find("FirebaseRef")?.GetComponent<FirebaseRef>()?.user?.UserId;
gameObjButton.Find("PlayerInfo").Find("PlayerScore").GetComponent<Text>().text = game.Score.ToString();
gameObjButton.Find("PlayerInfo").Find("PlayerPoints").GetComponent<Text>().text = game.Points.ToString();
canvas.enabled = true;
}

Related

Trying to make a radio button group with BEMCheckBoxes

I'd like to make a radio button group that will be like this when nothing is checked already using BEMCheckBoxes:
It appears to me that I can't simply add the number in the button.
So I decided to make a view with a label and a checkbox centered in it.
Here is the code I have so far:
// Add An horizontal stackView with check group
UIStackView uIStackView = new UIStackView();
uIStackView.TranslatesAutoresizingMaskIntoConstraints = false;
uIStackView.Axis = UILayoutConstraintAxis.Horizontal;
uIStackView.Alignment = UIStackViewAlignment.Center;
qolEvaluationCard.AddSubview(uIStackView);
uIStackView.Anchor(top: criteria.BottomAnchor, leading: criteria.LeadingAnchor, trailing: criteria.TrailingAnchor, size: new CGSize(0, 32));
BEMCheckBoxGroup bEMCheckBoxGroup = new BEMCheckBoxGroup();
bEMCheckBoxGroups.Add(bEMCheckBoxGroup);
for (int score= 1; score <= 10; score++)
{
var container = new UIView(new CGRect(0, 0, 32, 32));
var label = new UILabel();
label.Text = $#"{score}";
label.TintColor = UIColor.Black;
container.AddSubview(label);
var checkbox = new BEMCheckBox(new CGRect(0, 0, 32, 32));
container.AddSubview(checkbox);
checkbox.BoxType = BEMBoxType.Circle;
checkbox.OnAnimationType = BEMAnimationType.Stroke;
checkbox.Center = label.Center = new CGPoint(container.Frame.Size.Width / 2, container.Frame.Size.Height / 2);
// Transparent BackgroundColor
checkbox.BackgroundColor = null;
checkbox.TintColor = UIColor.Gray;
checkbox.OnTintColor = optimistic_orange;
bEMCheckBoxGroup.AddCheckBoxToGroup(checkbox);
uIStackView.AddArrangedSubview(container);
}
The result is not what I expect but I can't figure out what is wrong:
Why are my checkboxes not transparent to let appear the label?
There is also a problem of distribution within the UIStackView obviously.
Any help appreciated.
The Anchor extension method is:
internal static void Anchor(this UIView uIView, NSLayoutYAxisAnchor top = null, NSLayoutXAxisAnchor leading = null, NSLayoutYAxisAnchor bottom = null, NSLayoutXAxisAnchor trailing = null, UIEdgeInsets padding = default, CGSize size = default)
{
uIView.TranslatesAutoresizingMaskIntoConstraints = false;
if (top != null)
{
uIView.TopAnchor.ConstraintEqualTo(top, padding.Top).Active = true;
}
if (leading != null)
{
uIView.LeadingAnchor.ConstraintEqualTo(leading, padding.Left).Active = true;
}
if (bottom != null)
{
uIView.BottomAnchor.ConstraintEqualTo(bottom, -padding.Bottom).Active = true;
}
if (trailing != null)
{
uIView.TrailingAnchor.ConstraintEqualTo(trailing, -padding.Right).Active = true;
}
if (size.Width != 0)
{
uIView.WidthAnchor.ConstraintEqualTo(size.Width).Active = true;
}
if (size.Height != 0)
{
uIView.HeightAnchor.ConstraintEqualTo(size.Height).Active = true;
}
}
EDIT 1: Just found out with the help of the Reveal tool that My labels size was ambiguous.
var label = new UILabel();
becomes
var label = new UILabel(new CGRect(0, 0, 32, 32));
This solves the 'invisible label' issue.
my answer is Swift codes, but give You concept of works.
I suggest You to break down problem to simple steps.
just try to layout checkboxes (BEMCheckBox) correctly on UIStackView, then create custom View that contain UILabel + BEMCheckBox and now add them to UIStackView and review layout.
Why label not appear on checkbox?
because you add label first, then you put checkbox on it.
container.addSubview(checkbox)
container.addSubview(label)
Setup StackView:
to achive checkboxes horizontal layout
let stackView: UIStackView = {
let stack = UIStackView(arrangedSubviews: customCheckboxes)
stack.spacing = 8
stack.distribution = .fillEqually
stack.axis = .horizontal
stack.alignment = .fill
return stack
}()
then snap stackView to corners using autoLayout or whatever
I think using UIStackView is the best choice, don't use UICollectionView for this simple condition.
How to update label state when checkbox selected?
I didn't use BEMCheckBoxGroup before, but just reviewed source codes and there is a function notifyCheckBoxSelectionChanged trigger when checkbox selection change.
func notifyCheckBoxSelectionChanged(_ checkBox: BEMCheckBox) {
if checkBox.on {
// Change selected checkbox to this one
selectedCheckBox = checkBox
} else if checkBox == selectedCheckBox {
// Selected checkbox was this one, clear it
selectedCheckBox = nil
}
}
but it's not pubic, selectedCheckBox is what you need to know about this group. I think you should add target on valueChanged to this group, I don't see any public API to delegate user selection of checkboxes!

Order buttons that represent attached monitors by Windows Display order

I have the below code to draw a button(s) onto a panel for each monitor that is currently connected to the PC.
var padding = 5;
var buttonSize = new Size(95, 75);
for (int i = 0; i < Screen.AllScreens.Length; i++)
{
var screen = Screen.AllScreens[i];
Button monitor = new Button
{
Name = "Monitor" + screen,
AutoSize = true,
Size = buttonSize,
Location = new Point(12 + i * (buttonSize.Width + padding), 14),
BackgroundImageLayout = ImageLayout.Stretch,
BackgroundImage = Properties.Resources.display_enabled,
TextAlign = ContentAlignment.MiddleCenter,
Font = new Font("Segoe UI", 10, FontStyle.Bold),
ForeColor = Color.White,
BackColor = Color.Transparent,
Text = screen.Bounds.Width + "x" + screen.Bounds.Height
};
monitorPanel.Controls.Add(monitor);
}
For devices with one or more monitors attached, the code works just fine and this is the end result:
However, while it works, I'd like to (if possible) order them as they appear in the Windows display view:
In the first screenshot, they are ordered as 2 | 3 | 1 instead of 3 | 2 | 1.
Is what I'm after possible?
Multiple screens in Windows are handled as one, big, "glued" working area. Based on that, order of screens in configuration is according to their position in the new working area. Assuming that all of your screens are in one line, you can just use
screen.Bounds.X
as ordering property. If the screens are layed out in multiple rows, then ordering will also have to consider Y component.
EDIT:
You could just use Linq for sorting, specifically OrderBy().
Example usage for case when screens are in one row and collection will be iterated once.
Change
for (int i = 0; i < Screen.AllScreens.Length; i++)
{
var screen = Screen.AllScreens[i];
to
foreach(var screen in Screen.AllScreens.OrderBy(i => i.Bounds.X))
{

How to add tooltips to a Box plot in TeeChart?

As the title says, I am using TeeChart to draw several boxplots within one chart object. Since, the number of boxplots can be quite big, I want to be able to click on a box and have information pertaining to that series to show up as a tooltip.
I am currently trying to do this with a MarksTip but for some reason, when I try to hover over the box, MarksTip will sometimes open and then immediately close (basically being visible for a split second). I have already tried setting the hide delay but it seems to be ignoring that.
Code snippet below:
seriesIndex = 0;
foreach (var seriesData in seriesDataList)
{
var series = new Box()
series.UseCustomValues = true;
series.Box.HorizSize = 5;
series.Box.Style = PointerStyles.Rectangle;
series.MildOut.Visible = true;
series.MildOut.HorizSize = 2;
series.MildOut.VertSize = 2;
series.ExtrOut.Visible = true;
series.ExtrOut.HorizSize = 2;
series.ExtrOut.VertSize = 2;
series.LinePen.Visible = _isLineVisible;
series.Pointer.Pen.Visible = true;
series.ShowInLegend = false;
series.Add(seriesIndex, seriesData);
series.Title = "tooltip text";
var tooltip = new MarksTip(Chart.Chart)
{
Series = series,
Style = MarksStyles.SeriesTitle,
HideDelay = 31000,
};
Chart.Series.Add(series);
seriesIndex++;
}
Credit to this other question for pointing me in the right direction.
I ended up using the GetSeriesMark event to modify the text of a single chart-bound MarksTip instead of creating multiple series-bound `MarksTip
seriesIndex = 0;
Chart.Tools.Add(new MarksTip());
foreach (var seriesData in seriesDataList)
{
var series = new Box()
series.UseCustomValues = true;
//Other series appearance stuff
series.Add(seriesIndex, seriesData);
series.Title = "tooltip text";
series.GetSeriesMark += (s, args) =>
{
args.MarkText = s.Title;
};
series.Marks.Visible = false;
Chart.Series.Add(series);
seriesIndex++;
}
One note about this method. The tooltip will only appear when hovering over actual datapoints and not the whole box. Not ideal but at least I can read the tooltip now.

WinForms how to show specific panel from FlowDirectionPanel

i have the following FlowDirectionPanel http://prntscr.com/aao6lt as you can see those are achievements for a poker game.When you get a new achievement a yes/no messageBox pops up and it ask's you if you want to go and check out your new acquirement if you press yes I want to be able to automatically navigate to the newly unlocked achievement which are contained in the FlowDirectionPanel which looks like in the image above. Also each achievement is contained in another panel which is children of the flowPanel if you look closer you can see that they have some outline border. I create and add the panels dynamically but they do have names which can help me to navigate to the desired panel. This is how i create them:
public void PanelForAchievements(Form currentForm, FlowLayoutPanel flp, AchivementRequirements achivement)
{
FlowLayoutPanel retFlp = flp;
string pGetAchivementName = #"pGet" + achivement.Name;
string lbAchivementName = #"lb" + achivement.Name;
string lbAchivementRewardName = #"lb" + achivement.Name + #"Reward";
string cbGetAchivementName = #"cbGet" + achivement.Name;
string pbAchivementName = #"pb" + achivement.Name;
var pGetAchivement = new Panel
{
Name = pGetAchivementName,
Size = new Size(retFlp.Width, 100),
BorderStyle = BorderStyle.FixedSingle,
};
currentForm.Controls.Add(pGetAchivement);
var lbAchivement = new Label
{
Name = lbAchivementName,
Location = new Point(pGetAchivement.Location.X, pGetAchivement.Location.Y),
Size = new Size(135, 30),
AutoSize = false,
BorderStyle = BorderStyle.FixedSingle,
Font = new Font("Microsoft Sans Serif", 10F, FontStyle.Regular, GraphicsUnit.Point, (byte)0),
Text = achivement.TitleText,
};
var lbAchivementReward = new Label
{
Name = lbAchivementRewardName,
AutoSize = true,
Top = (pGetAchivement.Height - pGetAchivement.Height) / 2,
Text = achivement.RewardLabelText,
TabIndex = 2,
BorderStyle = BorderStyle.FixedSingle,
Location = new Point(lbAchivement.Location.X, lbAchivement.Location.Y + lbAchivement.Height + 5)
};
var cbGetAchivement = new CheckBox
{
Name = cbGetAchivementName,
AutoCheck = false,
AutoSize = true,
Location = new Point(lbAchivement.Location.X + lbAchivement.Width + 10, lbAchivement.Location.Y),
TabIndex = 1,
UseVisualStyleBackColor = true
};
achivement.IsUnlocked(MainPoker.AllAchievements[achivement.EnumCasted], achivement.Requirement,cbGetAchivement);
var pbAchivement = new PictureBox
{
BorderStyle = BorderStyle.Fixed3D,
Name = pbAchivementName,
Dock = DockStyle.Right,
BackgroundImageLayout = achivement.PictureBoxImageLayout,
//Location = new Point(pGetAchivement.Right, pGetAchivement.Location.Y),
Size = new Size(145, 90),
SizeMode = PictureBoxSizeMode.Zoom,
TabIndex = 9,
TabStop = false,
Image = achivement.PackPreview,
};
pGetAchivement.Controls.Add(lbAchivement);
pGetAchivement.Controls.Add(lbAchivementReward);
pGetAchivement.Controls.Add(cbGetAchivement);
pGetAchivement.Controls.Add(pbAchivement);
retFlp.Controls.Add(pGetAchivement);
achivement.Title = lbAchivement;
achivement.RewardLabel = lbAchivementReward;
achivement.Unlocked = cbGetAchivement;
achivement.Preview = pbAchivement;
}
It's just simple code and this is how i initialize them :
public static RoyalFlush RoyalFlush = new RoyalFlush(1, new Tuple<string, int?>("Royal Card Pack", 100000));
private readonly CreatePanels _createAchivementPanels = new CreatePanels();
foreach (var achi in AchivementRequirements.AchivementList)
{
_createAchivementPanels.PanelForAchievements(this, pAchievementsCards, achi);
//im also doing other stuff here..
//pAchivementsCards is the name of the FlowDirectionPanel
}
Now by the time the yes/no messageBox is shown i already know which achievement is unlocked and also my achievements have classes as seen above public static RoyalFlush RoyalFlush and those classes have property - Name which obviously contains the name of the achievement im using this name to create respective names for each control i create from public static RoyalFlush RoyalFlushfor example :
string pGetAchivementName = #"pGet" + achivement.Name;
p stands for panel and i simply just get the current achievement name using the property achivement.Name and we end up with something like this : pRoyalFlush as a name for our panel. Now that i know the name of the panel and which achievement is being unlocked i need to navigate through my FlowDirectionPanel and find the specific panel and leave the focus there. I have no idea how to do that i will show an example with pictures of what i want if it's not clear by now :
First we unlock new achievement and we get the yes/no mbox : http://prnt.sc/aaodyr
Now we press the Yes button which will redirect us to my new form and show us the achievement FlowDirectionPanel : h.t.t.p.:/./.prntscr.com/aaofft here the program see's that the achievement for Full House is completed and it should show it in the middle of the screen with a nice border just like so : h.t.t.p.:././.prntscr.com/aaoh40
I dont have reputation to post more than 2 links so i had to put some dots in them ..
It's my first question and my native language is not English so excuse me for any mistakes I made.
you may make some foreahc loop, where you compare names, and then, when you find the control you need, scroll your flowlayoutpanel to it. For example, my flowlayoutpanel has 4 buttons, and only 2 of them are visible, and I want to scroll to button4:
foreach (Control c in flowLayoutPanel1.Controls)
{
if ((c as Button).Name == "button4")
{
(c as Button).Focus();
flowLayoutPanel1.ScrollControlIntoView(c);
break;
}
}
Hope I andrestand your question correctly.

Add annotations in MS chart (e.g. (10, 20) (20, 39) etc) and horizontal Scroll Bar

I want to add text(say, annotations) in MS chart(winforms) like (10, 20) , (30, 40) where I already have a scroll bar.
I can able to draw strings(graphics.drawstring) in Chart, but on scrolling the horizontal scroll bar, the text which I have drawn remains static and immovable.
On scrolling the scrollbar, the text which I have drawn also should move along with my horizontal scrolling.
My code follows:
chart2.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
chart2.BorderlineColor = System.Drawing.Color.FromArgb(26, 59, 105);
chart2.BorderlineWidth = 3;
chart2.BackColor = Color.White;
chart2.ChartAreas.Add("chtArea");
chart2.ChartAreas[0].AxisX.Title = "Category Name";
chart2.ChartAreas[0].AxisX.TitleFont =
new System.Drawing.Font("Verdana", 11, System.Drawing.FontStyle.Bold);
chart2.ChartAreas[0].AxisY.Title = "UnitPrice";
chart2.ChartAreas[0].AxisY.TitleFont =
new System.Drawing.Font("Verdana", 11, System.Drawing.FontStyle.Bold);
chart2.ChartAreas[0].BorderDashStyle = ChartDashStyle.Solid;
chart2.ChartAreas[0].BorderWidth = 2;
chart2.ChartAreas["chtArea"].AxisX.ScrollBar.Enabled = true;
chart2.ChartAreas["chtArea"].CursorX.IsUserEnabled = true;
chart2.ChartAreas["chtArea"].CursorX.IsUserSelectionEnabled = true;
chart2.ChartAreas["chtArea"].AxisX.ScaleView.Zoomable = false;
chart2.ChartAreas["chtArea"].AxisX.ScrollBar.IsPositionedInside = true;
chart2.ChartAreas["chtArea"].AxisX.ScaleView.Size = 20;
chart2.ChartAreas[0].AxisX.ScaleView.SmallScrollSizeType = DateTimeIntervalType.Seconds;
chart2.ChartAreas[0].AxisX.ScaleView.SmallScrollSize = 1;
chart2.Legends.Add("UnitPrice");
chart2.Series.Add("UnitPrice");
chart2.Series[0].ChartType = SeriesChartType.Line;
Random rand = new Random();
var valuesArray = Enumerable.Range(0, 500).Select(x => rand.Next(0, 100)).ToArray();
for (int i = 0; i < 500; i++)
{
chart2.Series["UnitPrice"].Points.AddXY(i+10, valuesArray[i]);
}
I tried TextAnnotaions, Line annotations, etc Nothing helped me.
Then I tried drawing dynamic labels inside MS chart also. Labels remain immovable while scrolling horizontal scroll bar.
This code works perfectly in your machine also.
Sounds a lot as if you want to add TextAnnotations.
If you want them to stick with your data points you should anchor them to the points they shall stay with.
Here are a few examples:
// directly anchored to a point
TextAnnotation TA1 = new TextAnnotation();
TA1.Text = "DataPoint 222";
TA1.SetAnchor(chart2.Series["UnitPrice"].Points[222]);
chart2.Annotations.Add(TA1);
// anchored to a point but shifted down
TextAnnotation TA2 = new TextAnnotation();
TA2.Text = "DataPoint 111";
TA2.AnchorDataPoint = chart2.Series["UnitPrice"].Points[111];
TA2.AnchorY = 0;
chart2.Annotations.Add(TA2);
// this one is not anchored on a point:
TextAnnotation TA3 = new TextAnnotation();
TA3.Text = "At 50% width BC";
TA3.AnchorX = 50; // 50% of chart width
TA3.AnchorY = 20; // 20% of chart height, from top!
TA3.Alignment = ContentAlignment.BottomCenter; // try a few!
chart2.Annotations.Add(TA3);
By default they either anchor to DataPoints or are positioned in % of the chart size.
It is also possible to set the positions according to pixel coordinates, but for this you need to calculate the positions each time the chart changes its view!
See here for an example how to transform chart data positions to chart control coordinates and vice versa.. (Not really recommended, though)

Categories

Resources