I need to make an application that shows codes that update from time to time.
Now I got the back-end part covered but I am completely new to the front end part.
I'd basicly like it took look something like this:
Now the idea is that I'll have a List that holds items that should be converted to elements in the below visual list.. The logo is something I staticly added and that is anchored to the left top corner, I'd like to create the rest of the elemnents dynamically (I have a timed event loop to check if the list has changed, etc)
So I made the following method as a starting point:
private void createOTPEntry()
{
Rectangle entry = new Rectangle();
entry.Name = "entry1";
entry.Stroke = new SolidColorBrush(Color.FromRgb(0, 111, 0));
entry.Fill = new SolidColorBrush(Color.FromRgb(243, 86, 13));
entry.Height = 65;
entry.Width = 497;
Thickness margin = entry.Margin;
margin.Left = 10;
margin.Top = 83;
entry.Margin = margin;
entry.BringIntoView();
}
Now this isn't relative yet to the logo or anything.. But it's supposed to atleast be a white rectangle with an orange border.. And it's not showing. I tried to find if my rectangle object has like a "show" or "draw" method but it doesn't.. And I'm still miles away from what I really want..
So the logic of what I want:
Loop that goes over list elements and draws the visual list elements, probably a Rectangle containing 2 TextBoxes or Labels (one of the labels will change)
These elements will be added to another Container? that has a scrollbar in case there are to many elements.
I'd like some help of possible on what control do I choose to add the elements to? (the container witht he scrollbar?). How exactly do I show my rectangle? Is there an easy way to make them kinda automaticly space when I add them to the "parent control" (the scrollable container).
Thanks in advance for giving me a jump start!
Related
I am having a problem with my layout groups and keeping my UI consistent. I have a menu that holds two empty elements Blue and Green boxes bellow that get filled at playtime depending on what the player does. The orange boxes are buttons that may add or remove both purple boxes or orange boxes depending on the button pressed. The Gray, Blue, and Green boxes all have Vertical Layout Group and Content Size fitter (vertical fit set to "Prefered Size"). So from my understanding, all children within these boxes should be stacked vertically and will expand and contract the box as needed.
The problem I am having is that the first time I press a button that adds a purple box and expands the blue box, the green box stays where it is. The second time I press a button the Green box shifts where it should be no matter what gets added to the blue box. Conversely if pressing a button for the first time removes purple boxes and contracts the blue box, the green box stays where it is until I press a button again then shifts where it should be.
If I manually change any of the Gray, Blue, or Green boxes attributes such as anchor pivot or scale the boxes rearrange themselves the way it should be.
Am I doing something wrong with Vertical Layout / Content Size fitter? I hope this is enough to go on. I can provide code if need be. I just don't know exactly what to share.
When the button is pressed call this method.
void AcceptUnlockButtonInput(Exit roomExit) {
if (PlayerHasKeyForExit(roomExit)) {
LogAction(roomExit.exitUnlockDescription);
roomExit.attemptedToExit = false;
roomExit.attemptedToUnlock = false;
} else if (roomExit.attemptedToUnlock == false) {
LogAction(roomExit.exitLockedDescription);
}
DisplayActionText();
foreach(Transform child in inputContainer.transform) {
Destroy(child.gameObject);
}
DisplayInputButtons();
Canvas.ForceUpdateCanvases();
}
LogActrion() just simply adds a string to a list then DisplayActionTest() reads off that list and creats/adds gameObjects to the container.
public void DisplayActionText() {
actionLog.ForEach(value => {
var textObject = Instantiate(displayTextPrefab) as GameObject;
var textMeshPro = textObject.GetComponentInChildren<TextMeshProUGUI>();
textMeshPro.text = value;
textObject.transform.SetParent(outputContainer.transform, false);
});
actionLog.Clear();
Canvas.ForceUpdateCanvases();
}
Simply creats a button for each exit and adds the original method to the buttons listener.
public void DisplayInputButtons() {
foreach (var exit in Exits) {
var unlockButton = Instantiate(userActionButton) as GameObject;
newButton.text = buttonText;
newButton.transform.SetParent(buttonGroup.transform, false);
unlockButton.onClick.AddListener(() => AcceptUnlockButtonInput(exit));
}
}
I have had the same problem with layout groups. From my understanding, layout groups don't update immediately when you put something in them.
Something what worked for me was:
LayoutRebuilder.ForceRebuildLayoutImmediate(recttransform of the layoutgroup);
You can also try disabling and enabling the layout group component:
IEnumerator UpdateLayoutGroup()
{
layoutGroupComponent.enabled = false;
yield return new WaitForEndOfFrame();
layoutGroupComponent.enabled = true;
}
If enabling and disabling the layoutgroup component doesn't work you can also try this:
IEnumerator UpdateLayoutGroup()
{
yield return new WaitForEndOfFrame();
layoutGroupComponent.enabled = false;
layoutGroupComponent.CalculateLayoutInputVertical();
LayoutRebuilder.ForceRebuildLayoutImmediate(recttransform of the layoutgroup);
layoutGroupComponent.enabled = true;
}
I have had similar problems and I found this to be helpful:
https://docs.unity3d.com/ScriptReference/Canvas.ForceUpdateCanvases.html
Sometimes when instantiating elements into a gameobject with a vertical layout group, the elements will only get into their right position after updating the canvas.
Update answer
In my case there were multiple layout groups nested into each other, so the rebuild of the parent layout group did not work. In the first try I rebuild all in hierarchy (old answer), but ran into performance issues for large lists. Then I found a much more robust and performant way (with help from Answer from Adam-Buckner and Auto-Layout Docs):
Only the topmost layout group of the layout group hierarchy has a content size fitter, any descendant must not have one
All vertical layout groups have checked Control Child Size (Width & Height), Use Child Scale (Only Height), Child Force Expand (Only Width)
When the layout is being affected by code, due to adding/removing elements or setting a component active, I use the following code (it uses MarkLayoutForRebuild, which prevents recomputing a node multiple times)
(starting from element with changes, first line can be omitted if element is not a LayoutGroupon its own)
LayoutRebuilder.MarkLayoutForRebuild((RectTransform)transform);
LayoutGroup[] parentLayoutGroups = gameObject.GetComponentsInParent<LayoutGroup>();
foreach (LayoutGroup group in parentLayoutGroups) {
LayoutRebuilder.MarkLayoutForRebuild((RectTransform)group.transform);
}
It works really well and I have no longer elements jumping around. As far as I understand content size fitters control a RectTransform, but competes with the parent LayoutGroup which also want to control it (therefore the warning in the editor). If only LayoutGroups are nested into each other, the algorithm can easily compute the sizes from bottom up, so the bottom most layout groups derive their size from their children (Caused by Use Child Scale), which is then applied to it by their parent layout group (caused by Control Child Size). LayoutGroups does not affect the RectTransform of the element it is placed on, only on its children. Finally the top element gets a height from the hierarchy which is then applied to itself the content size fitter.
Old and inefficient answer
In my case there were multiple layout groups nested into each other, so the rebuild of the parent layout group did not work. I had to rebuild all in hierarchy, to properly update the layout.
LayoutGroup[] parentLayoutGroups = gameObject.GetComponentsInParent<LayoutGroup>();
foreach (LayoutGroup group in parentLayoutGroups) {
LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)group.transform);
}
I know there is an issue with the VLG not updating correctly. Changing any of its value after adding/ removing elements forces its layout to resize and may fix your issue (at least that's the way I do it).
Can you try :
- Add/remove your element
- VerticalLayoutLement.spacing += Random.Range(-1,1);
To be sure it rescales properly i usually do it in a coroutine with an"yield return new WaitForNewFrame()" before the spacing part.
After trying a lot of tricks that I found on the web I felt a little frustrated because none of them worked for me. When I ran the scene all the elements in the vertical layout looked messed, but when I clicked on Pause and Continue they magically fitted perfectly in the right position, so I wrote this simple funcion to emulate it, that works fine in my app:
IEnumerator RefreshPrefab(GameObject prefabInstance)
{
prefabInstance.SetActive(false);
yield return new WaitForSeconds(0.1f);
prefabInstance.SetActive(true);
}
And call it sending as parameter the parent prefab that contains the layout group:
StartCoroutine(RefreshPrefab(instantiatedPrefab));
I'm not sure if I titled the question correctly so it would be better if I explained what I'm trying to do. I want to add some images on chart control and
around their to draw graphics.
I want to display the layout of the sensors on the coordinate plane defined by coordinates, while noting the location of geographic objects (forest, river, etc.). These objects will be images which I want to add to the chart/
How can I do it? It is possible?
If you show us an example we may be able to help to find the best way.
There are several options.:
You can place image controls like PictureBox or Panel on the Chart by adding them to the chart's Controls collection
You can draw them in the Pre- or PostPaint event
You can assemble a BackImage that contains all the Images you want to place around the chart.
You can add ImageAnnotations to the chart. (Recommended)
The latter obviously is the one that is best integrated.
Here is an example:
We start by adding the images we want to use to the Chart's Images collection:
List<string> imgFiles = new List<string>()
{ yourImageFileName1, ...};
for (int i = 0; i < imgFiles.Count; i++)
{
Image img = Bitmap.FromFile(imgFiles[i]);
chart1.Images.Add(new NamedImage("Image" + i, img));
}
Note the NamedImage class used here. It allows you to refer to the images by a string; pick better names! Maybe Path.GetFileNameWithoutExtension(imgFiles[i]) - Also note that you must not Dispose of the Images or else they will disappear!
Next make make a little room at the right side of the chart by reducing the ChartArea's size:
ChartArea ca = chart1.ChartAreas[0];
ca.Position = new ElementPosition(5,5,70,90);
Note the values are percentages of the Chart's ClientSize, so they will grow and shrink when resizing the Chart!
Finally we can add them all. You will want to add them at specific positions. I add them at some space to the right and also make them moveable:
foreach (NamedImage img in chart1.Images)
{
ImageAnnotation ia = new ImageAnnotation();
ia.Image = img.Name;
ia.AllowMoving = true;
ia.X = 77;
ia.Y = 15 * chart1.Images.IndexOf(img) + 5;
chart1.Annotations.Add(ia);
}
Now you should see the Annotions. And if you add this event:
private void chart1_AnnotationPositionChanging(object sender,
AnnotationPositionChangingEventArgs e)
{
testLabel.Text = e.Annotation.X + " " + e.Annotation.Y;
}
..you will see just what the numbers for the best position are. Eventually you will not keep them moveable, of course..
Note the the Annotations' position is also in percentages, so they will move along nicely, when the chart get resized! You may also scale the Images by setting the Width and Height; this is a little tricky, as it will also be in percent (and not as the docs falsely state in pixels). You would probably want to loop over the ImageAnnotations and rescale them in the Resize event..: ia.Height = ia.Width * chart1.Width / chart1.Height;
Also note that there are other ways to position annotations, like anchored to datapoints, but this seem the best for a static adornment.
I have a TextBlock that I create during run-time:
TextBlock firstBlock = new TextBlock();
firstBlock.Text = "Mary Joe";
firstBlock.Margin = new Thickness(0, 0, -1500, 0);
firstBlock.FontSize = 72;
firstBlock.TextAlignment = TextAlignment.Center;
firstBlock.Foreground = Brushes.Red;
myGrid.Children.Add(firstBlock);
I want to be able to move the TextBlock down the Y-axis smoothly. I've thought to use DoubleAnimation, but I couldn't find a property to alter in order to move it. I would also like to make the text smaller as it goes downwards (kind of like a reverse Stars Wars Credits, but no 3D). I know this stuff is pretty simple to make in xaml, but I want to be able to call the animation at certain times and add new text each time in the blocks. I've searched around a lot to no avail. I figured I would post here to see if smart people could help me :).
You can use a TranslateTransform on the TextBlock to move it. Use a double animation on the Y property.
For the FontSize use another double animation to shrink the value. Put them both in a Storyboard and you can run them at the same time.
You are my only hope. I have searched everywhere and just can't find anything that could help me with this one.
I've done a simple code marking plugin for Visual Studio (2010). It just finds some parts of code to highlight (by Regex), creates Spans out of the matches found and then creates Rectangle adornments for them (in the background of the text), that scroll with the text. All of this is done in view.LayoutChanged event's implementation. It works fine... but... NOT EVERY TIME! Sometimes the markers get moved by various distances (mostly up or down) and then just keep these incorrect positions while the text is scrolled. I have no idea why and WHEN this happens. I was able to discover only these few things:
you can reproduce this bug (move some markers from their correct positions) by dragging the vertical scrollbar of the code editor window very fast and agresively up and down (but sometimes it also fixes the positions...)
you cannot fix a marker's position by editing the line on which it is placed (or even the marked text)
you can fix the marker's position by deleting and restoring the ending "}" of the code block in which the marked code is placed (which causes the whole block of code to be reformatted)
the view.ViewportTop is negative when the positions are calculated incorrectly (view is a WpfTextView class) and the Geometry "g" (see below) is getting the negative Bounds.Top too. (You can test it by attaching one VS to another and set a breakpoint)
Here is the piece of my code that calculates the positions and creates the markers (LayoutChanged event):
Geometry g = textViewLines.GetMarkerGeometry(span);
if (g != null)
{
GeometryDrawing drawing = new GeometryDrawing(_brush, _pen, g);
drawing.Freeze();
DrawingImage drawingImage = new DrawingImage(drawing);
drawingImage.Freeze();
Image image = new Image();
image.Source = drawingImage;
//Align the image with the top of the bounds of the text geometry
Canvas.SetLeft(image, g.Bounds.Left);
Canvas.SetTop(image, g.Bounds.Top);
//_layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, span, null, image, null);
Rect rect = new Rect(g.Bounds.Location, g.Bounds.Size);
Rectangle marker = new Rectangle();
marker.Margin = new Thickness(rect.X - 3, rect.Y - 2, 0, 0);
marker.Width = rect.Width + 6; marker.Height = rect.Height + 4;
marker.Fill = new SolidColorBrush(mark);
marker.RadiusX = marker.RadiusY = 5;
marker.Stroke = new SolidColorBrush(color);
_layer.AddAdornment(AdornmentPositioningBehavior.TextRelative, span, null, marker, null);
}
This is basically the MSDN example for creating adornments, I'm not doing any magic here.
Please help!
I had the same problem. If you use
_layer.AddAdornment(AdornmentPositioningBehavior.TextRelative,...);
more then once you have to insert
Canvas.SetLeft(image, g.Bounds.Left);
Canvas.SetTop(image, g.Bounds.Top);
every time before.
I've just spent the whole day on similar issue.
There're a lot of undocumented corner cases besides span moving issue. Even worse, seems-to-be-proven solution tend to break in never VS version (especially, starting with roslyn and VS2015). My favorite one was the following: the adornments were removed occasionally if you press enter multiple times inside a multiline comment. Hilarious!
So, the only working approach is the following: don't try to outsmart VS editor, it'll fool you anyway.
Instead, borrow the code from the roslyn's AdornmentManager<T>. It contains a lot of hacks I had to reinvent and even more I didn't ever suspect about, but it works. All you need to do is replace the code below
// add the visual to the adornment layer.
with yours one (the part is a good candidate to refactor into overridable method).
P.S. I know that I'm slightly late :) Hope this will save some time to another poor soul.
I'm working in Visual C# 2010 Express with WPF to develop an application for a Microsoft Surface table, and cannot for the life of me find the simple solution to this problem.
When using a ScatterView control without setting a custom template for scatterview items, the control throws new ScatterViewItems onto the screen at random orientations and positions. This, I like.
I am developing my program on a vertical screen, on which scatterViewItems are randomly thrown, except they are mostly upright, and never thrown on updide-down. What I am getting at is the control knows the screen is vertical and keeps the items from starting out upside down.
However, when I transfer the program to a device with a horizontal screen, the items are now thrown out randomly and OFTEN upside down from the original app orientation. Again the ScatterView control is reading InteractiveSurface.PrimarySurfaceDevice.Tilt (or something like it) and compensating.
How do I make it stop doing this?
I just want my items to continue to appear in the ScatterView as if the screen was vertical. Can I trick ScatterView into thinking it's vertical still? Can I turn this feature off? I would like to avoid making my own template for ScatterViewItems.
Thanks!
Set the Orientation property to 0 for each item. The randomness only happens when you haven't explicitly set a value
Well, if nobody can explain how ScatterView knows the screen tilt angle, and how to change that, then the only solution is to generate your own randomness. For reference, here's what I used to do this:
ScatterViewItem item = new ScatterViewItem();
item.Content = image;
Random rand = new Random();
item.Orientation = RandOrientation(-20, 20);
item.Center = RandCenter(50);
SSScatter.Items.Add(item);
private Point RandCenter(int pad)
{
int randx = rand.Next(pad, (int)SSScatter.Width-pad);
int randy = rand.Next(pad, (int)SSScatter.Height-pad);
return new Point(randx, randy);
}
private int RandOrientation(int low, int up)
{
int randor = rand.Next(low, up);
return randor;
}
I still wish there was a way to trick ScatterView...