I want to detect the rectangles in the red zone of the picture below, I defined the picture center and drew a line and compared my center to the rectangles center and that way I am able to find the centres.
My approach didn't take Y into consideration but the left range requires that. So I think Points would be appropriate to use here but I don't know how to do that,
QUESTION How to define this range(demonstrated by the redlines), I just want to know which objects are on the left line,right line,center line(gray lines), So by defining lines,spaces, anything would work for me,
// Rectangles am interested in, have left, right, top, bottom pixel position
var rectangleCenter =(left + right) / 2;
if (rectangleCenter >= (CenterRef - 50) && rectangleCenter <= (CenterRef + 50))
{
}
// assuming 5 is the curve
for(int i=0; i<somelimit; i+5)
{
var rectangleCenter = (left + right) / 2;
// assuming its a 1000 pixel image, Mycenter is 500,
leftRef = MyCenter + 250;
leftRef + i;
if (rectangleCenter >= (leftRef - 50) && rectangleCenter <= (leftRef + 50))
{
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
double[,] allPoints = new double[5, 3]; //Each rectangle is defined by 4 X/Y Points (left-top, left-bottom, right-top and right-bottom)
//The four points of each rectangle have to be inside the given area (inside the given couple of red lines)
int foundInArea = 0;
int areaCount = 0;
do
{
areaCount = areaCount + 1; //There are three areas; what is inside each couple of two red lines
foundInArea = areaCount;
int count1 = 0;
do
{
count1 = count1 + 1;
if (!isInArea(areaCount, new double[] { 0, allPoints[count1, 1], allPoints[count1, 2] }))
{
foundInArea = 0;
break;
}
} while (count1 < 4);
if (foundInArea > 0)
{
break;
}
} while (areaCount < 3);
if (foundInArea > 0)
{
//Rectangle inside are foundInArea
}
}
private bool isInArea(int areaNo, double[] pointToTest)
{
bool isThere = false;
double alpha = returnAngles(areaNo); //Inclination of the red lines
double[] startPoint1 = returnStartEndPoints(areaNo, true, true); //Initial point of the red line on the left
double[] endPoint1 = returnStartEndPoints(areaNo, true, false); //End point of the red line on the left
double[] startPoint2 = returnStartEndPoints(areaNo, false, true); //Initial point of the red line on the right
double[] endPoint2 = returnStartEndPoints(areaNo, false, false); //End point of the red line on the right
return checkPoint(pointToTest, alpha, startPoint1, endPoint1, startPoint2, endPoint2);
}
private bool checkPoint(double[] pointToTest, double alpha, double[] startPoint1, double[] endPoint1, double[] startPoint2, double[] endPoint2)
{
bool isThere = false;
//You have all the information and can perform the required trigonometrical calculculations to determine whether the two lines surround the given point or not
//I think that I have worked more than enough in this code :)
return isThere;
}
//Hardcoded angles for each red line.
//All the angles have to be taken from the same reference point (for example: middle-bottom part)
//Example: area1 (lines on the left): 240 degree, area2: 270 degree...
private double returnAngles(int areaNo)
{
double outVal = 0;
if (areaNo == 1)
{
//outVal = val;
}
else if (areaNo == 2)
{
//outVal = val;
}
else if (areaNo == 3)
{
//outVal = val;
}
return outVal;
}
//Returning the X (index 1) and Y (index 2) values under the given conditions (start/end point for each area)
//These values have to be hardcoded from a rough estimation. For example, by assuming that the start is in the upper part,
//the starting point for the left line can be assumed to be X = max_X/3 and Y = max_Y
private double[] returnStartEndPoints(int areaNo, bool isLeftLine, bool isStartPoint)
{
double[] outPoint = new double[3];
if (areaNo == 1)
{
if (isLeftLine)
{
if (isStartPoint)
{
//outPoint[1] = value; //hardcoded X for start point of line on the left of area1
//outPoint[2] = value; //hardcoded Y for start point of line on the left of area1
}
else
{
//outPoint[1] = value;
//outPoint[2] = value;
}
}
else
{
if (isStartPoint)
{
//outPoint[1] = value;
//outPoint[2] = value;
}
else
{
//outPoint[1] = value;
//outPoint[2] = value;
}
}
}
else if (areaNo == 2)
{
if (isLeftLine)
{
if (isStartPoint)
{
//outPoint[1] = value;
//outPoint[2] = value;
}
else
{
//outPoint[1] = value;
//outPoint[2] = value;
}
}
else
{
if (isStartPoint)
{
//outPoint[1] = value;
//outPoint[2] = value;
}
else
{
//outPoint[1] = value;
//outPoint[2] = value;
}
}
}
else if (areaNo == 3)
{
if (isLeftLine)
{
if (isStartPoint)
{
//outPoint[1] = value;
//outPoint[2] = value;
}
else
{
//outPoint[1] = value;
//outPoint[2] = value;
}
}
else
{
if (isStartPoint)
{
//outPoint[1] = value;
//outPoint[2] = value;
}
else
{
//outPoint[1] = value;
//outPoint[2] = value;
}
}
}
return outPoint;
}
}
Related
There is a "Custom Trackbar", which can take negative and positive values. If you set Min = -50, Max = 100, the slider moves outside the scrollbar. I need it to behave in the same way as "Standard Trackbar" (it should not go beyond the scrollbar boundaries). How to do it?
The screenshot shows 2 Trackbars for both I set (Minimum = -50, Maximum = 100, Value = -50), but after building the project I got the following picture:
If we set (Minimum = 0, Maximum = 100, Value = 25), we get the following:
[Code Custom Trackbar]
[DefaultEvent("ValueChanged")]
public class HandyHTrackbarWorked : Control {
#region Установка начальных параметров
public HandyHTrackbarWorked() {
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint
| ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true); UpdateStyles();
Size = new Size(250, 12);
ThumbSize = new Size(ThumbRect.Width = 15, ThumbRect.Height = 12);
}
#endregion
#region Основные свойства
private double _value;
public double Value {
get { return _value; }
set {
_value = value;
//if (_value < Minimum) { _value = Minimum; }
//if (_value > Maximum) { _value = Maximum; }
OnScroll(); Refresh();
}
}
private double minimum;
public double Minimum {
get { return minimum; }
set { minimum = value; Invalidate(); }
}
private double maximum = 100;
public double Maximum {
get { return maximum; }
set { maximum = value; Invalidate(); }
}
private double smallStep = 1;
public double SmallStep {
get { return smallStep; }
set {
smallStep = (value > 0) ? value : 1;
}
}
#endregion
#region Свойства, отвечающие за оформление
[Description("Размер ползунка")]
private Size thumbSize;
public Size ThumbSize {
get { return thumbSize; }
set {
thumbSize = value;
//if (thumbSize.Width % 2 == 0 && thumbSize.Width > 0) thumbSize.Width += 1;
Invalidate();
}
}
[Description("Цвет ползунка")]
private Color thumbBackColor = Color.FromArgb(255, 255, 255);
public Color ThumbBackColor {
get { return thumbBackColor; }
set { thumbBackColor = value; Invalidate(); }
}
private Color trackBackColor = Color.Transparent;
public Color TrackBackColor {
get { return trackBackColor; }
set { trackBackColor = value; Invalidate(); }
}
private Color trackBorderColor = Color.FromArgb(221, 0, 49);
public Color TrackBorderColor {
get { return trackBorderColor; }
set { trackBorderColor = value; Invalidate(); }
}
private Color trackBorderColor2 = Color.FromArgb(64, 64, 64);
public Color TrackBorderColor2 {
get { return trackBorderColor2; }
set { trackBorderColor2 = value; Invalidate(); }
}
[Description("Толщина")]
private int trackThickness = 2;
public int TrackThickness {
get { return trackThickness; }
set { trackThickness = value; Invalidate(); }
}
public new Padding Padding {
get { return base.Padding; }
set { base.Padding = value; Invalidate(); }
}
public Rectangle ThumbRect;
#endregion
#region Основные события
public event EventHandler ValueChanged;
#endregion
#region Обработчики событий
private Point startMouseClickPosition;
private Point currentMousePosition;
protected override void OnCreateControl() {
base.OnCreateControl();
this.MouseDown += (sender, e) => {
// When clicking on the ScrollBar, center the Thumb relative to the mouse cursor
if (!ThumbRect.Contains(e.Location)) {
MoveThumb(e, false);
}
// When clicking on Thumb, determine the startMouseClickPosition
if (ThumbRect.Contains(e.Location)) {
startMouseClickPosition.X = e.X - ThumbRect.Left; // OR ... - ThumbRect.X
ThumbBackColor = Color.Green;
}
};
this.MouseMove += (sender, e) => {
ThumbBackColor = ThumbRect.Contains(e.Location)
? ThumbBackColor = Color.Orange : ThumbBackColor = Color.Gray;
if (e.Button == MouseButtons.Left) {
ThumbBackColor = Color.Green; MoveThumb(e);
}
};
this.MouseLeave += (sender, e) => { ThumbBackColor = Color.Gray; };
}
int PaddingLR = 10;
// padding(left/right) must be the same,
// if the orientation of the scroll bar is HORIZONTAL
private void MoveThumb(MouseEventArgs e, bool useStartMouseClickPosition = true) {
double newValue;
if (useStartMouseClickPosition) {
currentMousePosition.X = e.X - startMouseClickPosition.X;
// works correctly
newValue = Maximum * (currentMousePosition.X - (ThumbSize.Width / 2) + (ThumbSize.Width / 2) - PaddingLR)
/ (Width - ThumbSize.Width - PaddingLR * 2);
} else {
newValue = Maximum * (e.X - ThumbSize.Width / 2 - PaddingLR)
/ (Width - ThumbSize.Width - PaddingLR * 2);
}
// does NOT work correctly (although the calculation result is the same)
//double newValue = Maximum * (newThumbLeft + (ThumbSize.Width / 2) - PaddingLR) /
// (Width - ThumbSize.Width - PaddingLR * 2);
Value = Math.Max(0, Math.Min(Maximum, newValue));
}
public void OnScroll() {
ValueChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
#region Отрисовка элементов управления
protected override void OnPaint(PaintEventArgs e) {
ThumbRect = new Rectangle(
Convert.ToInt32(Value * (Width - ThumbSize.Width - Padding.Left * 2) / Maximum + Padding.Right),
0 + Padding.Top,
ThumbSize.Width, // fixed slider width
Height - Padding.Bottom - Padding.Top // dynamic slider height
// (example) Height - 4, means to move the slider by 2 px above and below
);
// Filling the scroll bar
using (SolidBrush brush = new SolidBrush(TrackBackColor)) {
e.Graphics.FillRectangle(brush, new Rectangle(0, 0, Width, Height));
}
// The colored line in front of the slider
using (Pen pen = new Pen(TrackBorderColor2, TrackThickness)) {
e.Graphics.DrawLine(pen, Padding.Left, Height / 2, Width - Padding.Right, Height / 2);
}
// The colored line following the slider
using (Pen pen = new Pen(TrackBorderColor, TrackThickness)) {
e.Graphics.DrawLine(pen, Padding.Left, Height / 2, ThumbRect.Right, Height / 2);
}
// Filling the slider
using (SolidBrush brush2 = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) {
e.Graphics.FillRectangle(brush2, ThumbRect);
}
}
#endregion
}
Thanks to the help of user #IVSoftware, in writing auxiliary methods calcValueFromPosition() and calcXfromValue(), the following solution was obtained, which allows you to set different paddings:
[Code Custom Trackbar]
namespace Handy_UI.Controls.Trackbars {
[DefaultEvent("ValueChanged")]
public class HandyHTrackbarWorked : Control {
#region Setting the initial parameters
public HandyHTrackbarWorked() {
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint
| ControlStyles.AllPaintingInWmPaint | ControlStyles.ResizeRedraw, true); UpdateStyles();
Size = new Size(250, 12);
ThumbSize = new Size(15, 12);
}
#endregion
#region Main features
private double _value;
public double Value {
get { return _value; }
set {
_value = value;
OnScroll(); Refresh();
}
}
private double minimum;
public double Minimum {
get { return minimum; }
set { minimum = value; Invalidate(); }
}
private double maximum = 100;
public double Maximum {
get { return maximum; }
set { maximum = value; Invalidate(); }
}
private double smallStep = 1;
public double SmallStep {
get { return smallStep; }
set {
smallStep = (value > 0) ? value : 1;
}
}
#endregion
#region Properties responsible for design
private Size thumbSize;
public Size ThumbSize {
get { return thumbSize; }
set {
thumbSize = value;
Invalidate();
}
}
private Color thumbBackColor = Color.FromArgb(255, 255, 255);
public Color ThumbBackColor {
get { return thumbBackColor; }
set { thumbBackColor = value; Invalidate(); }
}
private Color trackBackColor = Color.Transparent;
public Color TrackBackColor {
get { return trackBackColor; }
set { trackBackColor = value; Invalidate(); }
}
private Color trackBorderColor = Color.FromArgb(221, 0, 49);
public Color TrackBorderColor {
get { return trackBorderColor; }
set { trackBorderColor = value; Invalidate(); }
}
private Color trackBorderColor2 = Color.FromArgb(64, 64, 64);
public Color TrackBorderColor2 {
get { return trackBorderColor2; }
set { trackBorderColor2 = value; Invalidate(); }
}
private int trackThickness = 2;
public int TrackThickness {
get { return trackThickness; }
set { trackThickness = value; Invalidate(); }
}
public new Padding Padding {
get { return base.Padding; }
set { base.Padding = value; Invalidate(); }
}
#endregion
#region Key Events
public event EventHandler ValueChanged;
#endregion
#region Обработчики событий
private Point startMouseClickPosition;
private Point currentMousePosition;
protected override void OnCreateControl() {
base.OnCreateControl();
this.MouseDown += (sender, e) => {
// When clicking on the ScrollBar, center the Thumb relative to the mouse cursor
if (!ThumbRect.Contains(e.Location)) {
MoveThumb(e, false);
}
// When clicking on Thumb, determine the startMouseClickPosition
if (ThumbRect.Contains(e.Location)) {
startMouseClickPosition.X = e.X - ThumbRect.Left; // OR ... - ThumbRect.X
ThumbBackColor = Color.Green;
}
};
this.MouseMove += (sender, e) => {
ThumbBackColor = ThumbRect.Contains(e.Location)
? ThumbBackColor = Color.Orange : ThumbBackColor = Color.Gray;
if (e.Button == MouseButtons.Left) {
ThumbBackColor = Color.Green; MoveThumb(e);
}
};
this.MouseLeave += (sender, e) => { ThumbBackColor = Color.Gray; };
//this.SizeChanged += (sender, e) => { TrackThickness = Height - Padding.Bottom - Padding.Top; };
}
private void MoveThumb(MouseEventArgs e, bool useStartMouseClickPosition = true) {
Point currentMousePosition = new Point(0, 0);
if (useStartMouseClickPosition) {
currentMousePosition.X = e.X - startMouseClickPosition.X + ThumbSize.Width / 2
- Padding.Right - (Padding.Left - Padding.Right);
} else currentMousePosition.X = e.X - Padding.Left;
Value = calcValueFromPosition(currentMousePosition);
}
private double calcValueFromPosition(Point e) {
var mouseRange = Width - (Padding.Left + Padding.Right);
var pct = e.X / (double)mouseRange;
var controlRange = Maximum - Minimum;
var relative = pct * controlRange;
var value = Minimum + relative;
value = Math.Max(Minimum, value);
value = Math.Min(Maximum, value);
return value;
}
public void OnScroll() {
ValueChanged?.Invoke(this, EventArgs.Empty);
}
#endregion
#region Drawing controls
public int calcXfromValue() {
var range = Maximum - Minimum;
var relative = Value - Minimum;
var pct = relative / range;
var width = Width - (Padding.Left + Padding.Right);
var pos = pct * width;
var x = pos + Padding.Left - (ThumbSize.Width / 2);
if (x < 0 + Padding.Left) x = 0 + Padding.Left;
else if (x > Width - ThumbSize.Width - Padding.Right) x = Width - ThumbSize.Width - Padding.Right;
return (int)x;
}
public Rectangle ThumbRect => new Rectangle(
x: calcXfromValue(), y: 0 + Padding.Top,
width: ThumbSize.Width, // fixed slider width
height: Height - Padding.Bottom - Padding.Top // dynamic slider width
);
protected override void OnPaint(PaintEventArgs e) {
// Filling the scroll bar
using (SolidBrush brush = new SolidBrush(TrackBackColor)) {
e.Graphics.FillRectangle(brush, new Rectangle(0, 0, Width, Height));
}
// The colored line in front of the slider
using (Pen pen = new Pen(TrackBorderColor2, TrackThickness)) {
e.Graphics.DrawLine(pen, Padding.Left, Height / 2, Width - Padding.Right, Height / 2);
}
// The colored line following the slider
using (Pen pen = new Pen(TrackBorderColor, TrackThickness)) {
e.Graphics.DrawLine(pen, Padding.Left, Height / 2, ThumbRect.Right, Height / 2);
}
// Filling the slider
using (SolidBrush brush2 = new SolidBrush(Color.FromArgb(100, 0, 0, 0))) {
e.Graphics.FillRectangle(brush2, ThumbRect);
}
}
#endregion
}
}
I've got a User Control consisting of Panels and Labels.
MinPanel, ValuePanel, and MaxPanel.
I'm trying to allow the user to drag the panels much like you would in a TrackBar.
My problem is that when I click and attempt to drag the MinPanel, it jump around starting at 0, then to 200, then to other random values. It seems to be attempting to reset to it's default value each time I drag it.
public partial class ToolboxCustomTrackBar : UserControl
{
private int min = 0;
private int max = 1000;
private int selectedMin = 0;
private int selectedMax = 1000;
private int selectedValue = 400;
private int selectionWidth = 0;
private int labelHeight = 10;
public int Min
{
get { return min; }
set
{
min = value;
Invalidate();
}
}
public int Max
{
get { return max; }
set
{
max = value;
Invalidate();
}
}
public int SelectedMin
{
get { return selectedMin; }
set
{
selectedMin = value;
Invalidate();
}
}
public int SelectedMax
{
get { return selectedMax; }
set
{
selectedMax = value;
Invalidate();
}
}
public int SelectedValue
{
get { return selectedValue; }
set { selectedValue = value; Invalidate(); }
}
public int LabelHeight
{
get { return labelHeight; }
set { labelHeight = value; Invalidate(); }
}
public ToolboxCustomTrackBar()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
backdropPanel.Width = Width;
backdropPanel.Height = Height;
SelectedMin = Min;
SelectedMax = Max;
SelectedValue = (Max - Min) / 2;
selectionWidth = Max - Min;
Invalidate();
backdropPanel.BackColor = Color.LightBlue;
minPanel.BackColor = Color.DarkRed;
maxPanel.BackColor = Color.DarkGreen;
valuePanel.BackColor = Color.Black;
backdropPanel.Location = new Point(Min, backdropPanel.Location.Y);
}
private void ToolboxCustomTrackBar_Paint(object sender, PaintEventArgs e)
{
backdropPanel.Location = new Point(0, LabelHeight);
backdropPanel.Width = Width;
backdropPanel.BackColor = Color.AliceBlue;
minPanel.Location = new Point(SelectedMin * Width / (Max - Min), backdropPanel.Location.Y);
maxPanel.Location = new Point(SelectedMax * Width / (Max - Min), backdropPanel.Location.Y);
valuePanel.Location = new Point((SelectedValue) * Width / (Max - Min), backdropPanel.Location.Y);
minLabel.Location = new Point(SelectedMin - (minLabel.Width / 2) + (minPanel.Width / 2), backdropPanel.Location.Y - LabelHeight);
minLabel.Text = SelectedMin.ToString();
maxLabel.Location = new Point(SelectedMax - (maxLabel.Width / 2) + (maxPanel.Width / 2), backdropPanel.Location.Y - LabelHeight);
maxLabel.Text = SelectedMax.ToString();
valueLabel.Location = new Point(SelectedValue - (valueLabel.Width / 2) + (valuePanel.Width / 2), backdropPanel.Location.Y - LabelHeight);
valueLabel.Text = SelectedValue.ToString();
}
private void minPanel_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return; //ensure user left-clicked
int pointedValue = Min + e.X * (Max - Min) / Width; //selectionWidth?
SelectedMin = pointedValue;
}
}
I've cut some unrelated bits out.
I don't fully understand why, but I need to use += instead of =.
private void minPanel_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return; //ensure user left-clicked
int pointedValue = Min + e.X * (Max - Min) / Width;
SelectedMin += pointedValue;
}
So Im making tetris and I dont know how to draw the blocks( L,I,Z etc) I have one block as Texture2D and every class for the blocks look like this:
namespace Tetris
{
public class ZBlock
{
Color Color;
const int x = 4;
const int y = 4;
bool[,] vorm;
public bool[,] zblock()
{
vorm = new bool[x, y];
for(int i=0; i< x; i++)
for (int j=0; j<y; j++)
{
vorm[i, j] = false;
vorm[0, 0] = true;
vorm[1, 0] = true;
vorm[1, 1] = true;
vorm[2, 1] = true;
}
Color = Color.Purple;
return vorm;
}
}
and this is the block class:
namespace Tetris
{
public class Block
{
Texture2D block;
Vector2 BlockPosition = new Vector2(30, 30);
float FallTimer;
Random Random = new Random();
ZBlock zBlock = new ZBlock();
TBlock tBlock = new TBlock();
SBlock sBlock = new SBlock();
OBlock oBlock = new OBlock();
JBlock jBlock = new JBlock();
LBlock lBlock = new LBlock();
IBlock iblock = new IBlock();
public bool[,] blockvorm()
{
bool[,] vorm;
vorm = new bool[4, 4];
vorm[3, 3] = false;
int r = Random.Next(7);
if (r == 0)
{
ZBlock.zblock();
}
else if (r == 1)
{
TBlock.tblock();
}
else if (r == 2)
{
SBlock.sblock();
}
else if (r == 3)
{
OBlock.oblock();
}
else if (r == 4)
{
JBlock.jblock();
}
else if (r == 5)
{
LBlock.lblock();
}
else if (r == 6)
{
IBlock.iblock();
}
return vorm;
}
public TBlock TBlock
{
get { return tBlock; }
}
public ZBlock ZBlock
{
get { return zBlock; }
}
public SBlock SBlock
{
get { return sBlock; }
}
public OBlock OBlock
{
get { return oBlock; }
}
public JBlock JBlock
{
get { return jBlock; }
}
public LBlock LBlock
{
get { return lBlock; }
}
public IBlock IBlock
{
get { return iblock; }
}
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, ContentManager Content)
{
block = Content.Load<Texture2D>("Block");
int[,] Grid = Tetris.GameWorld.TetrisGrid;
spriteBatch.Begin();
spriteBatch.Draw(?????????????);
spriteBatch.End();
}
So the problem is: I dont know how to draw those blocks (I know how to draw one block but I want the complete ones). I thought maybe ZBlock.vorm or ZBLock.zblock but both give errors.
Does anyone know how to draw the blocks?
Ok so here is a partial answer. What you want to do is basically just draw each block with a certain offset from the next block equal to: blockWidth / 2 in pixels. This means that the blocks will be correctly orientated without overlap.
Here is what you should put in the draw statement:
public void Draw(int theXPosition, int theYPosition, Color theColor, SpriteBatch theSpriteBatch, Texture2D theBlockTexture)
{
int aTextureStartX = Color * Convert.ToInt32(mBlockSize);
for (int aBlock = 0; aBlock < mNumberOfBlocks; aBlock++)
{
int aXPosition = (int)(theXPosition + (CurrentShape[Rotation, aBlock, 0] * mBlockSize));
int aYPosition = (int)(theYPosition + (CurrentShape[Rotation, aBlock, 1] * mBlockSize));
theSpriteBatch.Draw(theBlockTexture, new Rectangle(aXPosition, aYPosition, mBlockSize, mBlockSize), new Rectangle(aTextureStartX, 0, mBlockSize, mBlockSize),
}
}
This is from a blog: http://www.xnadevelopment.com/tutorials/fallingblocksyoumovetomakelines/fallingblocksyoumovetomakelines.shtml
The source code is at the top of the page.
I am making a GUI that reads in serial data, and I want to have the option to choose to plot the data on either the left or right Y axis. I can plot the data on the left y axis, but I am unable to plot it on the Left axis. What is wrong with my code?
Here is the relevant Code:
GraphPane myPane2;
PointPairList inst3time = new PointPairList();
myPane2 = zedGraphControl2.GraphPane;
myPane2.Title = "Data vs Time Plots";
myPane2.XAxis.Title = "Elapsed Minutes";
private void UpdateData3(string line)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new UpdateDataDelegate(UpdateData3), new object[] { line });
}
else
{
if (chk_DISPLAY_3.Checked == true)
{
timer3.Interval = (30000);
timer3.Start();
OZ1lastdatatime = DateTime.Now;
count++;
if (count > 7)
{
count = 0;
TextBox_3.Text = "";
TextBox_3.AppendText(line);
}
else
{
TextBox_3.AppendText(line);
}
}
if (chk_SAVE_FILE_3.Checked == true)
{
StoreData3.Write(line);
StoreData3.Flush();
}
if (chk_PLOT_3.Checked == true)
{
string[] blahArray = line.Split(new char[] { ',' });
//string blaharray = Convert.ToDouble(blahArray[2]).ToString("F4");
int column_data = Convert.ToInt32(textBox5.Text);
double inst3 = Convert.ToDouble(blahArray[column_data]);
//TextBox_3.Text = Convert.ToString(oz1);
TimeSpan span = DateTime.UtcNow - startDateTimeOfProgram;
double elapsedMinutes = span.TotalMinutes;
inst3time.Add(elapsedMinutes,inst3);
if (cbo_POSITION_3.Text == "LEFT")
{
myPane2.YAxis.Title = cbo_Instrument_3.Text;
zedGraphControl2.AxisChange();
zedGraphControl2.GraphPane.AddCurve("",inst3time, Color.Blue, SymbolType.Circle);
zedGraphControl2.Refresh();
}
if (cbo_POSITION_3.Text == "RIGHT")
{
myPane2.Y2Axis.Title = cbo_Instrument_3.Text;
zedGraphControl2.AxisChange();
LineItem myCurve = zedGraphControl2.GraphPane.AddCurve("",inst3time, Color.Blue, SymbolType.Circle);
myCurve.IsY2Axis = true;
zedGraphControl2.Refresh();
}
}
}
}
I was able to figure it out: I needed to make sure that the Y2 Axis was visible:
myPane2.Y2Axis.IsVisible = true;
myPane2.Y2Axis.Title = cbo_Instrument_3.Text;
zedGraphControl2.AxisChange();
LineItem myCurve = myPane2.AddCurve("",inst3time, Color.Blue, SymbolType.Circle);
myCurve.IsY2Axis = true;
zedGraphControl2.AxisChange();
zedGraphControl2.Refresh();
What I need: listbox with textboxes inside, textboxes wraps, and last in row fills remaining space:
|word 1||word 2___|
|word 3___________|
I'm trying to implement this behaviour using that advice. My xaml:
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Tags}"
HorizontalContentAlignment="Stretch">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<controls:WrapPanelLastChildFill />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MyWrapPanel (inherits form WrapPanel) code:
protected override Size MeasureOverride(Size constraint)
{
Size curLineSize = new Size();
Size panelSize = new Size(constraint.Width, 0);
UIElementCollection children = base.InternalChildren;
for (int i = 0; i < children.Count; i++)
{
UIElement child = children[i] as UIElement;
child.Measure(constraint);
Size sz = child.DesiredSize;
if (curLineSize.Width + sz.Width > constraint.Width) // new line
{
panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
panelSize.Height += curLineSize.Height;
if (i > 0)
{
// change width of prev control here
var lastChildInRow = children[i - 1] as Control;
lastChildInRow.Width = lastChildInRow.ActualWidth + panelSize.Width - curLineSize.Width;
}
curLineSize = sz;
}
else
{
curLineSize.Width += sz.Width;
curLineSize.Height = Math.Max(sz.Height, curLineSize.Height);
}
}
panelSize.Width = Math.Max(curLineSize.Width, panelSize.Width);
panelSize.Height += curLineSize.Height;
return panelSize;
}
Thats work, but in one side only - textbox width never shrinks.
Any help appreciated.
I have done it recently.
You can see my code at: CodeProject
or use this class directly as shown:
Usage:
<TextBox MinWidth="120" wrapPanelWithFill:WrapPanelFill.UseToFill="True">*</TextBox>
Code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
namespace WrapPanelWithFill
{
public class WrapPanelFill : WrapPanel
{
// ******************************************************************
public static readonly DependencyProperty UseToFillProperty = DependencyProperty.RegisterAttached("UseToFill", typeof(Boolean),
typeof(WrapPanelFill), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender));
// ******************************************************************
public static void SetUseToFill(UIElement element, Boolean value)
{
element.SetValue(UseToFillProperty, value);
}
// ******************************************************************
public static Boolean GetUseToFill(UIElement element)
{
return (Boolean)element.GetValue(UseToFillProperty);
}
// ******************************************************************
const double DBL_EPSILON = 2.2204460492503131e-016; /* smallest such that 1.0+DBL_EPSILON != 1.0 */
// ******************************************************************
private static bool DoubleAreClose(double value1, double value2)
{
//in case they are Infinities (then epsilon check does not work)
if (value1 == value2) return true;
// This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < DBL_EPSILON
double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON;
double delta = value1 - value2;
return (-eps < delta) && (eps > delta);
}
// ******************************************************************
private static bool DoubleGreaterThan(double value1, double value2)
{
return (value1 > value2) && !DoubleAreClose(value1, value2);
}
// ******************************************************************
private bool _atLeastOneElementCanHasItsWidthExpanded = false;
// ******************************************************************
/// <summary>
/// <see cref="FrameworkElement.MeasureOverride"/>
/// </summary>
protected override Size MeasureOverride(Size constraint)
{
UVSize curLineSize = new UVSize(Orientation);
UVSize panelSize = new UVSize(Orientation);
UVSize uvConstraint = new UVSize(Orientation, constraint.Width, constraint.Height);
double itemWidth = ItemWidth;
double itemHeight = ItemHeight;
bool itemWidthSet = !Double.IsNaN(itemWidth);
bool itemHeightSet = !Double.IsNaN(itemHeight);
Size childConstraint = new Size(
(itemWidthSet ? itemWidth : constraint.Width),
(itemHeightSet ? itemHeight : constraint.Height));
UIElementCollection children = InternalChildren;
// EO
LineInfo currentLineInfo = new LineInfo(); // EO, the way it works it is always like we are on the current line
_lineInfos.Clear();
_atLeastOneElementCanHasItsWidthExpanded = false;
for (int i = 0, count = children.Count; i < count; i++)
{
UIElement child = children[i] as UIElement;
if (child == null) continue;
//Flow passes its own constrint to children
child.Measure(childConstraint);
//this is the size of the child in UV space
UVSize sz = new UVSize(
Orientation,
(itemWidthSet ? itemWidth : child.DesiredSize.Width),
(itemHeightSet ? itemHeight : child.DesiredSize.Height));
if (DoubleGreaterThan(curLineSize.U + sz.U, uvConstraint.U)) //need to switch to another line
{
// EO
currentLineInfo.Size = curLineSize;
_lineInfos.Add(currentLineInfo);
panelSize.U = Math.Max(curLineSize.U, panelSize.U);
panelSize.V += curLineSize.V;
curLineSize = sz;
// EO
currentLineInfo = new LineInfo();
var feChild = child as FrameworkElement;
if (GetUseToFill(feChild))
{
currentLineInfo.ElementsWithNoWidthSet.Add(feChild);
_atLeastOneElementCanHasItsWidthExpanded = true;
}
if (DoubleGreaterThan(sz.U, uvConstraint.U)) //the element is wider then the constrint - give it a separate line
{
currentLineInfo = new LineInfo();
panelSize.U = Math.Max(sz.U, panelSize.U);
panelSize.V += sz.V;
curLineSize = new UVSize(Orientation);
}
}
else //continue to accumulate a line
{
curLineSize.U += sz.U;
curLineSize.V = Math.Max(sz.V, curLineSize.V);
// EO
var feChild = child as FrameworkElement;
if (GetUseToFill(feChild))
{
currentLineInfo.ElementsWithNoWidthSet.Add(feChild);
_atLeastOneElementCanHasItsWidthExpanded = true;
}
}
}
if (curLineSize.U > 0)
{
currentLineInfo.Size = curLineSize;
_lineInfos.Add(currentLineInfo);
}
//the last line size, if any should be added
panelSize.U = Math.Max(curLineSize.U, panelSize.U);
panelSize.V += curLineSize.V;
// EO
if (_atLeastOneElementCanHasItsWidthExpanded)
{
return new Size(constraint.Width, panelSize.Height);
}
//go from UV space to W/H space
return new Size(panelSize.Width, panelSize.Height);
}
// ************************************************************************
private struct UVSize
{
internal UVSize(Orientation orientation, double width, double height)
{
U = V = 0d;
_orientation = orientation;
Width = width;
Height = height;
}
internal UVSize(Orientation orientation)
{
U = V = 0d;
_orientation = orientation;
}
internal double U;
internal double V;
private Orientation _orientation;
internal double Width
{
get { return (_orientation == Orientation.Horizontal ? U : V); }
set { if (_orientation == Orientation.Horizontal) U = value; else V = value; }
}
internal double Height
{
get { return (_orientation == Orientation.Horizontal ? V : U); }
set { if (_orientation == Orientation.Horizontal) V = value; else U = value; }
}
}
// ************************************************************************
private class LineInfo
{
public List<UIElement> ElementsWithNoWidthSet = new List<UIElement>();
// public double SpaceLeft = 0;
// public double WidthCorrectionPerElement = 0;
public UVSize Size;
public double Correction = 0;
}
private List<LineInfo> _lineInfos = new List<LineInfo>();
// ************************************************************************
/// <summary>
/// <see cref="FrameworkElement.ArrangeOverride"/>
/// </summary>
protected override Size ArrangeOverride(Size finalSize)
{
int lineIndex = 0;
int firstInLine = 0;
double itemWidth = ItemWidth;
double itemHeight = ItemHeight;
double accumulatedV = 0;
double itemU = (Orientation == Orientation.Horizontal ? itemWidth : itemHeight);
UVSize curLineSize = new UVSize(Orientation);
UVSize uvFinalSize = new UVSize(Orientation, finalSize.Width, finalSize.Height);
bool itemWidthSet = !Double.IsNaN(itemWidth);
bool itemHeightSet = !Double.IsNaN(itemHeight);
bool useItemU = (Orientation == Orientation.Horizontal ? itemWidthSet : itemHeightSet);
UIElementCollection children = InternalChildren;
for (int i = 0, count = children.Count; i < count; i++)
{
UIElement child = children[i] as UIElement;
if (child == null) continue;
UVSize sz = new UVSize(
Orientation,
(itemWidthSet ? itemWidth : child.DesiredSize.Width),
(itemHeightSet ? itemHeight : child.DesiredSize.Height));
if (DoubleGreaterThan(curLineSize.U + sz.U, uvFinalSize.U)) //need to switch to another line
{
arrangeLine(lineIndex, accumulatedV, curLineSize.V, firstInLine, i, useItemU, itemU, uvFinalSize);
lineIndex++;
accumulatedV += curLineSize.V;
curLineSize = sz;
if (DoubleGreaterThan(sz.U, uvFinalSize.U)) //the element is wider then the constraint - give it a separate line
{
//switch to next line which only contain one element
arrangeLine(lineIndex, accumulatedV, sz.V, i, ++i, useItemU, itemU, uvFinalSize);
accumulatedV += sz.V;
curLineSize = new UVSize(Orientation);
}
firstInLine = i;
}
else //continue to accumulate a line
{
curLineSize.U += sz.U;
curLineSize.V = Math.Max(sz.V, curLineSize.V);
}
}
//arrange the last line, if any
if (firstInLine < children.Count)
{
arrangeLine(lineIndex, accumulatedV, curLineSize.V, firstInLine, children.Count, useItemU, itemU, uvFinalSize);
}
return finalSize;
}
// ************************************************************************
private void arrangeLine(int lineIndex, double v, double lineV, int start, int end, bool useItemU, double itemU, UVSize uvFinalSize)
{
double u = 0;
bool isHorizontal = (Orientation == Orientation.Horizontal);
Debug.Assert(lineIndex < _lineInfos.Count);
LineInfo lineInfo = _lineInfos[lineIndex];
double lineSpaceAvailableForCorrection = Math.Max(uvFinalSize.U - lineInfo.Size.U, 0);
double perControlCorrection = 0;
if (lineSpaceAvailableForCorrection > 0 && lineInfo.Size.U > 0)
{
perControlCorrection = lineSpaceAvailableForCorrection / lineInfo.ElementsWithNoWidthSet.Count;
if (double.IsInfinity(perControlCorrection))
{
perControlCorrection = 0;
}
}
int indexOfControlToAdjustSizeToFill = 0;
UIElement uIElementToAdjustNext = indexOfControlToAdjustSizeToFill < lineInfo.ElementsWithNoWidthSet.Count ? lineInfo.ElementsWithNoWidthSet[indexOfControlToAdjustSizeToFill] : null;
UIElementCollection children = InternalChildren;
for (int i = start; i < end; i++)
{
UIElement child = children[i] as UIElement;
if (child != null)
{
UVSize childSize = new UVSize(Orientation, child.DesiredSize.Width, child.DesiredSize.Height);
double layoutSlotU = (useItemU ? itemU : childSize.U);
if (perControlCorrection > 0 && child == uIElementToAdjustNext)
{
layoutSlotU += perControlCorrection;
indexOfControlToAdjustSizeToFill++;
uIElementToAdjustNext = indexOfControlToAdjustSizeToFill < lineInfo.ElementsWithNoWidthSet.Count ? lineInfo.ElementsWithNoWidthSet[indexOfControlToAdjustSizeToFill] : null;
}
child.Arrange(new Rect(
(isHorizontal ? u : v),
(isHorizontal ? v : u),
(isHorizontal ? layoutSlotU : lineV),
(isHorizontal ? lineV : layoutSlotU)));
u += layoutSlotU;
}
}
}
// ************************************************************************
}
}
It was misunderstanding where to place width correction. This must be in ArrangeOverride:
private void ArrangeLine(double y, Size lineSize, double boundsWidth, int start, int end)
{
double x = 0;
UIElementCollection children = InternalChildren;
for (int i = start; i < end; i++)
{
UIElement child = children[i];
var w = child.DesiredSize.Width;
if (LastChildFill && i == end - 1) // last сhild fills remaining space
{
w = boundsWidth - x;
}
child.Arrange(new Rect(x, y, w, lineSize.Height));
x += w;
}
}