I have a panel where I create Labels and NumericUpDown fields dinamically like:
List<string> Labels = new List<string>();
public List<Label> DeliveryBreakdownLabelsModel = new List<Label>();
public List<NumericUpDown> DeliveryBreakdownNumericUpDownModel = new List<NumericUpDown>();
private void SetDeliveryBreakdownAmountForm_Load(object sender, EventArgs e)
{
var rModel = //List data from database
AddRow(rModel);
Arrange();
}
private void AddRow(IList<DeliveryBreakdownGetViewModel> rModel)
{
for (int i = 0; i < rModel.Count; i++)
{
Labels.Add(rModel[i].DesignGroupName);
var label = new Label
{
AutoSize = true, // make sure to enable AutoSize
Name = "label" + Labels.Count,
Text = rModel[i].DesignGroupName,
Location = new Point(12, YPos)
};
this.Controls.Add(label);
pnlDeliveryBreakdown.Controls.Add(label);
DeliveryBreakdownLabelsModel.Add(label);
var numericUpDown = new NumericUpDown
{
Name = "numericUpDown" + Labels.Count,
Text = rModel[i].ContractedAmount.ToString(),
Location = new Point(12, YPos),
Size = new Size(60, 19),
DecimalPlaces = 2,
Maximum = decimal.MaxValue
};
this.Controls.Add(numericUpDown);
this.Controls.Add(numericUpDown);
pnlDeliveryBreakdown.Controls.Add(numericUpDown);
DeliveryBreakdownNumericUpDownModel.Add(numericUpDown);
YPos += 25;
}
}
void Arrange()
{
// Determine the widest label sized by the AutoSize
var maxLabelX = 0;
for (int i = 0; i < Labels.Count; i++)
{
maxLabelX = Math.Max(maxLabelX, DeliveryBreakdownLabelsModel[i].Location.X + DeliveryBreakdownLabelsModel[i].Size.Width);
}
// Move all the text boxes a little to the right of the widest label
var maxNumericX = 0;
for (int i = 0; i < Labels.Count; i++)
{
maxNumericX = Math.Max(maxNumericX, DeliveryBreakdownNumericUpDownModel[i].Location.X + DeliveryBreakdownNumericUpDownModel[i].Size.Width);
DeliveryBreakdownNumericUpDownModel[i].Location = new Point(maxLabelX + 10, DeliveryBreakdownNumericUpDownModel[i].Location.Y);
}
//Set total wi
this.Width = maxNumericX + maxLabelX + 60;
}
So it looks like:
My question is, how can I modify my code in order to create more than one column. I want to do that because sometimes I can have alot of data so shows only in vertical may be a problem in future. Expected result: I.E
I suggest using a TableLayoutPanel with a UserControl that contains exactly one label and one numeric box.
Even in the designer, this looks neat.
All you have to do is expose the properties you want from the numeric box in your user control.
In the example above, I am using the following code:
// Fixed height user control
// Some code taken from: https://stackoverflow.com/a/4388922/380384
[Designer(typeof(MyControlDesigner))]
public partial class LabelNumeric : UserControl
{
public LabelNumeric()
{
InitializeComponent();
}
protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)
{
base.SetBoundsCore(x, y, width, 24, specified);
}
[DefaultValue("Label")]
public string Caption
{
get => label1.Text;
set => label1.Text = value;
}
[DefaultValue(0)]
public decimal Value
{
get => numericUpDown1.Value;
set => numericUpDown1.Value =value;
}
[DefaultValue(0)]
public int DecimalPlaces
{
get => numericUpDown1.DecimalPlaces;
set => numericUpDown1.DecimalPlaces = value;
}
[DefaultValue(100)]
public decimal MaxValue
{
get => numericUpDown1.Maximum;
}
[DefaultValue(0)]
public decimal MinValue
{
get => numericUpDown1.Minimum;
}
}
internal class MyControlDesigner : ControlDesigner
{
MyControlDesigner()
{
base.AutoResizeHandles = true;
}
public override SelectionRules SelectionRules
{
get
{
return SelectionRules.LeftSizeable | SelectionRules.RightSizeable | SelectionRules.Moveable;
}
}
}
You can access all the controls from the tableLayoutPanel1.Controls property and check their position in the table like this:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
foreach (var item in tableLayoutPanel1.Controls.OfType<LabelNumeric>())
{
var pos = tableLayoutPanel1.GetCellPosition(item);
item.Caption = $"Row{pos.Row}Col{pos.Column}";
}
}
}
So at runtime it is as follows:
The trick is to have one extra row on the bottom autosized, and all the other rows that contain the controls be of fixed height of 24pt.
Related
I have a parent form and four child forms,
In each child form, there are 28 labels to fill,
The parent form computes the value in the array
UInt16[,] _array = new UInt16[4, 28];
then send the value for each child
_array[0, 0] ~[0, 27] to Child1
_array[1, 0] ~[1, 27] to Child2
_array[2, 0] ~[2, 27] to Child3
_array[3, 0] ~[3, 27] to Child4
How do I send the values
ChildForm
I have set the TabIndex of 28 labels to(0~27) in property
public partial class ChildTest : Form
{
public List<Control> Cell_Volt1 = new List<Control>();
public ChildTest()
{
InitializeComponent();
}
private void ChildTest_Load(object sender, EventArgs e)
{
for (int i = 0; i < tableLayoutPanel1.Controls.Count; i++)
{
if (tableLayoutPanel1.Controls[i].TabIndex < 28)
{
Cell_Volt1.Add(tableLayoutPanel1.Controls[i]);
tableLayoutPanel1.Controls[i].Text = "001x";
}
}
}
}
Parent Form
UInt16[,] _array = new UInt16[4, 28]{//Some Values}
if (Application.OpenForms["ChildTest"] is ChildTest childForm)
{
childForm.Focus();
return;
}
childForm = new ChildTest();
Your question is how to pass a multidimensional array to your ChildTest forms. My suggestion is to pass it in the form of data bindings. This way, your MainForm can set values using a statement like _array[1,5] = 0xff and this will automatically display the value at the correct position on the right form. This walk-through will demonstrate how to connect these in a one-time setup. First a couple of usage examples in the MainForm:
private void buttonClear2_Click(object sender, EventArgs e)
{
for (int cell = 0; cell < 28; cell++) _array[1, cell] = 0x00;
}
private void buttonTest1_Click(object sender, EventArgs e) => _array[0,0] = 0xFF;
Observable UInt
The bindings are based on a uint value that sends a notification whenever its value changes. This class works behind the scenes to keep everything synced.
public class ObservableUInt : INotifyPropertyChanged
{
uint _Value = 0;
public uint Value
{
get => _Value;
set
{
if (!Equals(_Value, value))
{
_Value = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Formatted)));
}
}
}
public string Formatted => $"0x{_Value.ToString("X2")}";
public event PropertyChangedEventHandler PropertyChanged;
public static implicit operator uint(ObservableUInt #this) => #this._Value;
}
Multidimensional array class
The multidimensional array provides a standard array indexer that gets and sets a uint value using _array[bank,index] = 0xnn. Under the hood are arrays made from these observable uints but it makes no difference in terms of interacting with the multidimensional array.
public class MultidimensionalArray
{
public MultidimensionalArray()
{
for (uint i = 0; i < 112; i++)
{
_array[i / 28].Add(new ObservableUInt());
}
}
public uint this[int bank, int index]
{
get => _array[bank][index];
set => _array[bank][index].Value = value;
}
public uint this[uint bank, uint index]
{
get => _array[bank][(int)index];
set => _array[bank][(int)index].Value = value;
}
public ObservableUInt[] this[int bank] => _array[bank].ToArray();
ObservableCollection<ObservableUInt>[] _array =
Enumerable.Range(0, 4)
.Select(_ => new ObservableCollection<ObservableUInt>())
.ToArray();
}
ChildTest form class
This is a Form with a TableLayoutPanel on it that is unremarkable except for the addition of a DataSource property. It is here that the observable uints are bound to the textbox values.
public partial class ChildTest : Form
{
public ObservableUInt[] DataSource
{
set
{
if (value != null)
{
for (int i = 0; i < 28; i++)
{
var row = (i / 7) * 2;
var column = i % 7;
// Add Label
var label = new Label
{
Size = new Size(width: 150, height: 50),
Text = $"{i + 1}",
TextAlign = ContentAlignment.MiddleCenter,
BackColor = Color.DimGray,
ForeColor = Color.White,
Anchor = (AnchorStyles)0xf,
};
tableLayoutPanel.Controls.Add(label, column, row);
row++;
// Add Textbox and bind the Formatted property to it
var textbox = new TextBox
{
Size = new Size(width: 150, height: 50),
TextAlign = HorizontalAlignment.Center,
};
tableLayoutPanel.Controls.Add(textbox, column, row);
textbox.DataBindings.Add(
nameof(Label.Text),
value[i],
nameof(ObservableUInt.Formatted),
false,
DataSourceUpdateMode.OnPropertyChanged
);
}
}
}
}
}
MainForm Constructor
public MainForm()
{
InitializeComponent();
// Initialize 4 x 28 with respective index
for (uint i = 0; i < 112; i++)
{
var bank = i / 28;
var index = i % 28;
_array[bank, index] = i + 1;
}
_childTest1 = new ChildTest { DataSource = _array[0] };
_childTest2 = new ChildTest { DataSource = _array[1] };
_childTest3 = new ChildTest { DataSource = _array[2] };
_childTest4 = new ChildTest { DataSource = _array[3] };
}
private readonly MultidimensionalArray _array = new MultidimensionalArray();
Good afternoon.
I started discussing this issue here, but I thought that the topic is worthy of a separate question, since I could not find the answer “from the swipe”.
Panee was discussed at StaskOverflov
Thanks to this decision, changes were made to the class (new full code).
Now the changed type of the class is as follows:
class Seasonality_ProgressBar : Control
#region --События--
public delegate void OnValueChangedEvent(int value);
public event OnValueChangedEvent OnValueChanged;
#endregion
Stopwatch st = new Stopwatch();
MouseButtons mb = MouseButtons.None;
public int Value
{
get => _value;
set
{
if (value >= ValueMinimum && value <= ValueMaximum)
{
_value = value;
Invalidate();
}
else
{
value = _value;
Invalidate();
}
OnValueChanged?.Invoke(_value);
}
}
public Seasonality_ProgressBar()
{
}
protected override void OnMouseDown(MouseEventArgs e)
{
// base.OnMouseDown(e);
if (!st.IsRunning)
{
mb = e.Button;
st.Start();
if (e.Button == mb)
{
x = e.X;
y = e.Y;
timer.Elapsed += OnTimedEvent;
timer.Start();
}
}
base.OnMouseDown(e);
}
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
timer.Stop();
//MessageBox.Show("Сработало");
float reultation = x - y;
if (reultation > 0)
{
Value = "A";
}
else
{
Value = "B";
}
}
Now on the form you can subscribe to the event:
label2.Text = (-seasonality_ProgressBar1.Value).ToString();
seasonality_ProgressBar1.OnValueChanged += SomeEvent;
And the kind of SomeEvent method is:
private void SomeEvent(int value)
{
//Use Invoke here because your event is called from another thread
Invoke(new Action(() =>
{
label2.Text = (-value).ToString();
}));
//File.AppendAllText("log.txt", "seasonality_ProgressBar1_MouseUp\r\n");
}
#M. Artem, Thanks for the help.
My question is this.
On the form of elements of this class a lot (12 pieces). Each of them should display its own value on a separate label. I'm trying to learn how to create an array of elements of the same type on a form, while they are of their own class or standard (which, as I understand it, matters), i.e. you need 2 solutions, assign values to them, read values from fields that are not standard (my class has "author" fields).
Friends, help me learn, pliz.
Ready to answer all additional questions.
ready to send any add-ons.
I’m new here, criticize, lower, dominate.
You could include this task in your custom control to show the value whenever it changes in a selected control.
➤ Add a new property of Control type, say:
class Seasonality_ProgressBar : Control
{
//...
public Control ValueControl { get; set; }
//...
}
➤ In the setter of the Value property, check whether the ValueControl has a value and assign the Value property to it's Text property. You need to use the Invoke method here since you are using the System.Timers.Timer instead of the System.Windows.Forms.Timer!
public int Value
{
get => _value;
set
{
if (value >= ValueMinimum && value <= ValueMaximum)
{
_value = value;
Invalidate();
}
else
{
value = _value;
Invalidate();
}
if (ValueControl != null) Invoke(new Action(() => ValueControl.Text = value.ToString()));
OnValueChanged?.Invoke(_value);
}
}
➤ Rebuild.
➤ In the Form, select an instance of the Seasonality_ProgressBar and switch to the Properties window, you will see the new property listed as ValueControl.
➤ Select from the drop down the control that you want to connect it with this instance to show it's value. Say label1. Alternatively, you can set the ControlValue property in code:
seasonality_ProgressBar1.ValueControl = lable1; //or textBox1 ...etc.
➤ Run and try.
Try something like this
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
const int ProgressBar_Width = 100;
const int ProgressBar_Height = 200;
const int Label_Width = 10;
const int Label_Height = 20;
const int Number_Bars = 10;
const int MARGIN = 20;
List<Seasonality_ProgressBar> bars = new List<Seasonality_ProgressBar>();
public Form1()
{
InitializeComponent();
for(int i = 0; i < Number_Bars; i++)
{
Seasonality_ProgressBar bar = new Seasonality_ProgressBar();
bar.Left = MARGIN + i * (ProgressBar_Width + MARGIN);
bar.Top = MARGIN + MARGIN + Label_Height;
bar.Width = ProgressBar_Width;
bar.Height = ProgressBar_Height;
bar.Name = "Bar_" + i.ToString();
this.Controls.Add(bar);
bars.Add(bar);
bar.highLabel = new Label();
bar.highLabel.Left = MARGIN + i * (ProgressBar_Width + MARGIN) + (ProgressBar_Width / 2);
bar.highLabel.Top = MARGIN;
bar.highLabel.Width = Label_Width;
bar.highLabel.Height = Label_Height;
bar.highLabel.Text = i.ToString();
bar.highLabel.BackColor = Color.Red;
this.Controls.Add(bar.highLabel);
bar.lowLabel = new Label();
bar.lowLabel.Left = MARGIN + i * (ProgressBar_Width + MARGIN) + (ProgressBar_Width / 2);
bar.lowLabel.Top = MARGIN + MARGIN + ProgressBar_Height + Label_Height + MARGIN;
bar.lowLabel.Width = Label_Width;
bar.lowLabel.Height = Label_Height;
bar.lowLabel.Text = i.ToString();
bar.lowLabel.BackColor = Color.LightBlue;
this.Controls.Add(bar.lowLabel);
}
}
}
public class Seasonality_ProgressBar : ProgressBar
{
public Label highLabel { get; set; }
public Label lowLabel { get; set; }
public string name { get; set; }
}
}
After I created a userControl and established it in a Windows form, this form could no longer be saved. When I press Ctrl + S or the Save button nothing happens, the small * on the form file name suggests that it was not saved. Also, my form contains a property for a color. When I change this, the color doesn't change and the property window doesn't seem to respond anymore.
Code of custom userControl
namespace FlowGui
{
public partial class FlowProgressBar: UserControl
{
// Public Variables with Setters
private int maxValue;
[Bindable(true), Category("Behavior")]
public int Maximum
{
get
{
return maxValue;
}
set
{
maxValue = value;
DrawBar();
}
}
private int minValue;
[Bindable(true), Category("Behavior")]
public int Minimum
{
get
{
return minValue;
}
set
{
minValue = value;
DrawBar();
}
}
private int currentValue;
[Bindable(true), Category("Behavior")]
public int Value
{
get
{
return currentValue;
}
set
{
currentValue = value;
DrawBar();
}
}
private Color barColor;
[Bindable(true), Category("Appearance")]
public Color BarColor
{
get
{
return barColor;
}
set
{
barColor = value;
CreateBarImage();
DrawBar();
}
}
// private Values for Drawing
private Image canvas;
private Graphics gfx;
private Bitmap barImage;
public FlowProgressBar()
{
InitializeComponent();
}
// Load
private void FlowProgressBar_Load(object sender, EventArgs e)
{
maxValue = 100;
minValue = 0;
currentValue = 0;
barColor = Color.LightBlue;
// Init Graphic
InitGraphics();
CreateBarImage();
DrawBar();
}
private void CreateBarImage()
{
barImage = new Bitmap(10, 50);
Bitmap currentBarImage = Properties.Resources.BarImage_Basic;
for(int x=0; x<10; x++)
{
for (int y = 0; y < 50; y++)
{
Color barPreColor = currentBarImage.GetPixel(x, y);
int r = barPreColor.R * barColor.R / 255;
int g = barPreColor.G * barColor.G / 255;
int b = barPreColor.B * barColor.B / 255;
Color pixelColor = Color.FromArgb(r,g,b);
currentBarImage.SetPixel(x, y, pixelColor);
}
}
}
private void InitGraphics()
{
canvas = new Bitmap(pictureBoxBar.Width, pictureBoxBar.Height);
gfx = Graphics.FromImage(canvas);
}
private void DrawBar()
{
int barWidth = pictureBoxBar.Width;
int barHeight = pictureBoxBar.Height;
Image bgImage = Properties.Resources.BarBG_Raised_Grey;
gfx.DrawImage(bgImage,-200,0,barWidth+400,barHeight);
gfx.DrawImage(barImage, 0, 0, 100, barHeight);
pictureBoxBar.Image = canvas;
}
// Behaviour
private void FlowProgressBar_SizeChanged(object sender, EventArgs e)
{
InitGraphics();
DrawBar();
}
private void pictureBoxBar_Paint(object sender, PaintEventArgs e)
{
InitGraphics();
DrawBar();
}
}
}
My first thought was that it could be because the userControll is in another project and only linked. But even after I implemented the userControll in the main project, my described problems occurred. So I undo this.
I have no idea what I'm doing wrong. Thank you very much for your help.
Solved!
The problem was the pictureBoxBar_Paint() event. After I removed it from the userControl everything runs.
I want to make an application in which the user could add rectangle with customizable text inside it. The rectangle also can have another rectangles inside. Just as you can see on these picture:
I read about DrawingVisual, Shapes etc. So far I did it using DrawingVisual + Host, which derivies from FrameworkElement. DrawingVisual has FormattedText field, and list of Children elements; Host maintain drawing all elements.
The main problem is that, everytime user changes text in any child element I need to calculate new coordinates, width, height of all child elements. Maybe there is any method to do that automatically?
Also, DrawingVisual doesn't have any mouse events. So how to make all elements selectable / hoverable? Or should I derive from some other class?
Later I will post some code...
EDIT:
public class VisualHost: FrameworkElement
{
private VisualCollection _children;
private List<MyElement> _list;
public VisualHost(List<MyElement> list)
{
_children = new VisualCollection(this);
_list = list;
}
protected override int VisualChildrenCount
{
get { return _children.Count; }
}
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index >= _children.Count)
{
throw new ArgumentOutOfRangeException();
}
return _children[index];
}
private void CheckSize(MyElement element)
{
double sw = 0;
double mh = 0;
if (element.GetChildCount() > 0)
{
for (int i = 0; i < element.GetChildCount(); i++)
{
CheckSize(element.GetChild(i));
sw += element.GetChild(i).Width;
mh = Math.Max(mh, element.GetChild(i).Height);
}
}
element.Width = Math.Max(element.Formatted.Width, sw);
element.Height = element.Formatted.Height + mh;
}
private void DrawElement(double top, double left, MyElement element)
{
CheckSize(element);
var context = element.RenderOpen();
context.DrawRectangle( null, new Pen(Brushes.Black, 2d), new Rect(new Point(left, top), new Size(element.Width, element.Height)));
context.DrawText(element.Formatted, new Point(left, top));
top += element.Formatted.Height;
if (element.GetChildCount() > 0)
{
for (int i = 0; i < element.GetChildCount(); i++)
{
context.DrawRectangle(null, new Pen(Brushes.Black, 2d), new Rect(new Point(left, top), new Size(element.GetChild(i).Width, element.GetChild(i).Height)));
context.DrawText(element.GetChild(i).Formatted, new Point(left, top));
left += element.GetChild(i).Width;
}
}
context.Close();
_children.Add(element);
}
public void Redraw()
{
if (_list != null)
{
double top = 0, left = 0;
foreach (MyElement element in _list)
{
DrawElement(top, left, element);
top += element.Height + 10d;
}
}
}
}
public class MyElement: DrawingVisual
{
private string _text;
public string Text
{
get { return _text; }
set {
if (_text != value)
{
Typeface typeface = new Typeface(new FontFamily("Arial"), FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
Formatted = new FormattedText(value, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, typeface, 12, Brushes.Red);
_text = value;
}
}
}
public FormattedText Formatted { get; private set; }
public double Height { get; set; }
public double Width { get; set; }
private List<MyElement> _children;
public MyElement GetChild(int i)
{
if (i < 0 || i >= _children.Count)
{
throw new ArgumentOutOfRangeException();
}
return _children[i];
}
public int GetChildCount()
{
return _children.Count;
}
public void AddChild(MyElement child)
{
_children.Add(child);
}
public MyElement(string Text)
{
this.Text = Text;
this._children = new List<MyElement>();
}
}
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
_list = new List<MyElement>();
_list.Add(new MyElement("text"));
var e = new MyElement("text 2");
e.AddChild(new MyElement("a"));
e.AddChild(new MyElement("b"));
e.AddChild(new MyElement("c"));
_list.Add(e);
_host = new VisualHost(_list);
MyCanvas.Children.Add(_host);
_host.Redraw();
}
This is my code for now. I wrote it only to check if idea is correct.
well I'm not sure if you would like this approach but you can actually do it very simple... I'm thinking maybe you can use blend to create a user control and design a label and a listbox in a stackpanel and set 'em all on autosizing.
or design 2 stack panels set 1 to do vertical orientation and the other one to do horizontal and add textblocks or something to the horizontal one.
The scenario:
I want to tap on a textblock and run a method to add that item to the cart.No, I don't prefer buttons to textblocks, thank you :)
The code (shoppingcart.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading.Tasks;
namespace m_POS
{
public class shoppingcart
{
int cartnum;
int duplicate=0;
int num_of_items;
int counter=1;
List<item> items = new List<item>();
//constructor
public shoppingcart()
{
this.cartnum = counter;
counter += 1;
this.num_of_items = 0;
this.items = new List<item>();
init_items();
}
//return the item list of tapped/purchased items
public List<item> getitems(){
return this.items;
}
//returns the number of items tapped/purchased
public int get_num_of_items() { return this.num_of_items; }
// the method that adds a tapped-on item to the items list
public void additem(String itemx,String qty) {
for (int i = 0; i < item.pick_item.Count; i++)
{
if (itemx.Equals(item.pick_item[i].getname()))
{
item itm = new item(item.pick_item[i].getname(),
item.pick_item[i].getprice());
itm.addqty(Convert.ToInt16(qty));
this.items.Add(itm);
Debug.WriteLine("added to cart!!");
}
}
this.num_of_items += Convert.ToInt16(qty);
}
//used to test the additem() works. Everytime the class is run, this Rolex item will
//be the first to be added to the cart. ALWAYS. Funny thing is, it doesnt get
// duplicated.
private void init_items()
{
item itm12 = new item("Rolex", 4000);
//additem(itm12);
this.items.Add(itm12);
}
}
////////////////////////////////////////////////////////////////////////////////////////
public class item
{
String itemname;
int itemamount;
int itemqty = 0;
public static List<item> pick_item = new List<item>();
public static List<String> menu_food = new List<string> {
"Single Beef Burger",
"Double Beef Burger",
"Triple Beef Burger",
"Single Chicken Burger",
"Double Chicken Burger",
"Single Veggie Burger",
"1/2 Fries",
"Full Fries",
"Beef Steak",
"Mushroom",
"Steamed Rice",
"Rolex"};
public static List<String> menu_price = new List<String>{
"8000",
"17000",
"25000",
"12000",
"26500",
"7500",
"4000",
"6000",
"20000",
"25000",
"17500",
"4000"};
public item(string name, int amount)
{
this.itemamount = amount;
this.itemname = name;
this.itemqty = 1;
}
public static void init_menu()
{
for (int i = 0; i < get_menu().Count; i++)
{
item itm = new item(menu_food[i], Convert.ToInt32(menu_price[i]));
pick_item.Add(itm);
}
}
public void addqty(int qty) { this.itemqty = qty; }
public string getname() { return this.itemname; }
public int getprice() { return this.itemamount; }
public static int getpxbyname(string itemname) {
int ans=0;
for (int y = 0; y < pick_item.Count; y++) {
if (pick_item[y].itemname.ToString().Equals(itemname)) {
ans = pick_item[y].itemamount;
}
}
return ans;
}
public static List<String> get_menu() { return menu_food; }
public static List<String> get_price() { return menu_price; }
}
}
Where are the duplicates happening?
I'm getting the additem(string itemname,int itemqty) being run twice on every tap. Everything else is perfect, though.
What have I done before posting?
- Tested the Tap event and made sure it was only being fired ONCE per tap? Check.
- Tested the additem() method to make sure it works to being with? Check. I add a single item to the cart everytime the app is started. That item never gets duplicated.
Console Debug.WriteLine() shows
added to cart!!
added to cart!!
called method with System.Windows.Controls.TextBlock
The first two added to carts are from the method being called twice.
The next called method with System.Windows.Controls.TextBlock is from the debug i inserted just after calling this method from the Food.xaml.cs
Food.xaml.cs [part of it]
public Foods()
{
InitializeComponent();
item.init_menu();
populatemenu();
}
public void populatemenu()
{
List<String> display = item.get_menu();
for (int i = 0; i < display.Count; i++)
{
string tname = "tb" + i;
TextBlock tb = new TextBlock();
tb.Tap += new EventHandler<System.Windows.Input.GestureEventArgs>(tb_Click);
tb.Style = (Style)Resources["textblocker"];
tb.FontSize = 36;
tb.Text = display[i];
tb.Name = tname;
sp_lunch.Children.Add(tb);
}
}
private void tb_Click(object sender, RoutedEventArgs e)
{
tapped += 1;
selectqty(sender);
}
private void selectqty(object sender) {
Popup popup = new Popup();
popup.Height = 300;
popup.Width = 400;
popup.VerticalOffset = 100;
PopUpControl control = new PopUpControl();
popup.Child = control;
popup.IsOpen = true;
string qty="";
control.btnOK.Click += (s, args) =>
{
popup.IsOpen = false;
//pick input from the popup's textbox.
qty = control.tbx.Text;
if (qty == null||qty=="") { qty = "0"; }
//send clicked item to cart for addition
TextBlock clicked = ((TextBlock)sender);
string temp = clicked.Text;
Cart.cart_new.additem(temp, qty);
Debug.WriteLine("called method with "+sender.ToString());
tb_pamount_lunch.Text = Convert.ToString(Cart.cart_new.get_num_of_items());
//tb_pamount_lunch.Text = tapped.ToString();
MessageBox.Show(temp);
//update the dinner stackpanel to display the selected items
sp_dinner.Children.Clear();
List<item> display = Cart.cart_new.getitems();
for (int i = 0; i < display.Count; i++)
{
TextBlock tb1 = new TextBlock();
tb1.FontSize = 36;
tb1.Text = display[i].getname().ToString();
sp_dinner.Children.Add(tb1);
}
};
control.btnCancel.Click += (s, args) =>
{
//close popup when cancel is clicked
popup.IsOpen = false;
};
}
Any more info??
If there's some other class you'd want to take a look at, I'll gladly copy/paste it here or upload the whole project.zip :)
I think when you tab the thrid time, 3 items will be added.
(s, args) =>
{
popup.IsOpen = false;
//pick input from the popup's textbox.
qty = control.tbx.Text;
if (qty == null||qty=="") { qty = "0"; }
//send clicked item to cart for addition
TextBlock clicked = ((TextBlock)sender);
string temp = clicked.Text;
Cart.cart_new.additem(temp, qty);
Debug.WriteLine("called method with "+sender.ToString());
tb_pamount_lunch.Text = Convert.ToString(Cart.cart_new.get_num_of_items());
//tb_pamount_lunch.Text = tapped.ToString();
MessageBox.Show(temp);
//update the dinner stackpanel to display the selected items
sp_dinner.Children.Clear();
List<item> display = Cart.cart_new.getitems();
for (int i = 0; i < display.Count; i++)
{
TextBlock tb1 = new TextBlock();
tb1.FontSize = 36;
tb1.Text = display[i].getname().ToString();
sp_dinner.Children.Add(tb1);
}
};
control.btnCancel.Click += (s, args) =>
{
//close popup when cancel is clicked
popup.IsOpen = false;
};
This is because the control still exists and every time you call selectqty another action is added to the eventhandler list. And will be executed more than ones. You should only register the event ones. Like in your constructor.
Personal: You should register events to a button that within a subcontrol. Instead create a new event on the control and raise it there. This will give you the advantage to change the buttons or other controls (maybee you want short-cut keys in future) on that controls without altering you mainform.
example:
public class Form1 : Form
{
public Form1()
{
InitializeComponent();
control.btnOKClicked += Control_buttonOk;
control.btnCancelClicked += Control_buttonCancel;
}
private void Control_buttonOk(object sender, eventArgs e)
{
// implement code
}
private void Control_buttonCancel(object sender, eventArgs e)
{
// implement code
}
}
public class UserControl1: UserControl
{
public UserControl1()
{
InitializeComponent();
btnOK.Click += (sender, e) =>
{
if(btnOKClicked != null)
btnOKClicked(this, EventArgs.Empty);
};
btnCancel.Click += (sender, e) =>
{
if(btnCancelClicked!= null)
btnCancelClicked(this, EventArgs.Empty);
};
}
public event EventHandler btnOKClicked;
public event EventHandler btnCancelClicked;
}
This way you separate the functionallity/dependency of the layout of the control.
As i was writing this, i think you might look over here:
Form.ShowDialog Method http://msdn.microsoft.com/en-us/library/c7ykbedk.aspx This will handle the popup.IsOpen and will block your MainForm until it's closed. With a DialogResult you can read if the user pressed Ok or cancel.