How to build an xml with data retrieved from usercontrols? - c#

I'm trying to make a little software in which I could modify a pre-existing xml file. I would have the chance to add some values (via usercontrols) to the alphabet letters, and send these values back to the xml, saving it.
I already call back the xml file, so that I can load the list of letters I need.
anyway, first things first, I'd need to retrieve the data from the usercontrols.
Here's the code in the form that contains quite everything:
public partial class SettingLetterInput : UserControl {
Dictionary<char, Actions> ListaLettere;
public SettingLetterInput() {
InitializeComponent();
}
private void SettingLetterInput_Load(object sender, EventArgs e) {
string path = Path.GetFullPath("Config.xml");
ListaLettere = Actions.GetFromXML(path);
listLetterBox1.Items.AddRange(
ListaLettere.Select(l => (object)l.Key).ToArray());
foreach (var a in Enum.GetValues(typeof(Actions.acts))) {
actionsBox.Items.Add(a);
}
}
private void AddActionBtn_Click(object sender, EventArgs e) {
if (actionsBox.Text != "") {
Actions.acts act = (Actions.acts)Enum.Parse(typeof(Actions.acts), actionsBox.Text);
AddRow(act, null);
} else {
MessageBox.Show("no action selected");
}
}
public void AddRow(Actions.acts act, object values) {
ActionRowCtrl arc;
#region switch per controls
switch (act) {
case Actions.acts.addPosition:
arc = new ActionRow_SizeCtrl(values);
arc.actNameLbl.Text = this.actionsBox.Text;
break;
case Actions.acts.changeColor:
arc = new ActionRow_ColorCtrl(values);
arc.actNameLbl.Text = this.actionsBox.Text;
break;
case Actions.acts.addCircle:
arc = new ActionRow_FormCtrl();
arc.actNameLbl.Text = this.actionsBox.Text;
break;
default:
arc = new ActionRowCtrl();
break;
}
#endregion
this.panel1.Controls.Add(arc);
positionRow();
}
public void positionRow() {
int lastTop = -1;
int lastHeight=0;
int rowN = 0;
foreach (ActionRowCtrl c in panel1.Controls) {
rowN++;
c.rowLbl.Text = rowN.ToString();
if (( rowN % 2 ) == 0)
c.BackColor = Color.White;
else
c.BackColor = Color.Bisque;
c.Top = 0;
if (lastTop > -1) {
c.Top = lastTop + lastHeight;
}
lastTop = c.Top;
lastHeight = c.Height;
}
}
public void deleteRow(ActionRowCtrl arc) {
this.panel1.Controls.Remove(arc);
arc.Dispose();
positionRow();
}

Related

How to make a scoring system in c# (Converting int to string and string to bool in c#)

I am trying to make a scoring system where if the user inputs 1 into the text box it outputs 4 into the listbox and so on. i've tried using Convert.Int32 and .ToString and bool.Parse Nothing has worked so far.
private void PlaceBox_TextChanged(object sender, EventArgs e)
{
BadForm.place = Convert.ToInt32(points);
points = Convert.ToInt32(PlaceBox.Text);
bool.Parse(PlaceBox.Text = 1.ToString());
if (PlaceBox.Text = 1.ToString())
{
PlaceBox.Text = 4;
}
else if (PlaceBox.Text = 2)
{
PlaceBox.Text = 3;
}
else if (PlaceBox.Text = 3)
{
PlaceBox.Text = 2;
}
else if (PlaceBox.Text = 4)
{
PlaceBox.Text = 1;
}
else
{
MessageBox.Show("ERROR");
}
}
Thats the code it's very basic but and that's just for one form

C# How do I set a column in a multidimensional array with a switch statement?

So in this class, I'm trying to use a switch statement to determine which radio button is checked in the form.
This is the part of the code that I'm focused on
private void lstTransactions_SelectedIndexChanged(object sender, EventArgs e)
{
int index = lstTransactions.SelectedIndex;
if (index != -1)
{
txtAmount.Text = entries[index, 0];
txtDate.Text = entries[index, 1];
chkCleared.Checked = bool.Parse(entries[index, 3]);
// entries[index, 2] is transaction type
//make switch comparison work correctly
// to determine which radio button should be checked
switch(entries[index, 2])
{
case TransactionTypes.Deposit.ToString():
rbDeposit.Checked = true;
break;
case TransactionTypes.Withdrawal.ToString():
rbWithdrawal.Checked = true;
break;
default:
rbServiceFee.Checked = true;
break;
}
}
}
Typing case TransactionTypes.Deposit.ToString(): and case TransactionTypes.Withdrawal.ToString(): gives me an error that says "CS0150 A constant value is expected". I tried looking up the error and I still can't figure out how to make the switch comparison work.
Here's the full Forms Code for Reference
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 Transaction3
{
public partial class CheckbookForm : Form
{
public CheckbookForm()
{
InitializeComponent();
}
enum TransactionTypes { Deposit, ServiceFee, Withdrawal }
TransactionTypes transactionType;
private string[,] entries = new string[20, 4];
decimal balance = 0m;
decimal bankBalance = 0m;
private void SetError(Control whichControl, string message)
{
errorProvider1.SetError(whichControl, message);
}
private decimal IsValid()
{
bool flag=true;
decimal amount = 0m;
DateTime date;
if (DateTime.TryParse(txtDate.Text,out date))
{
if (date>DateTime.Today)
{
flag=false;
SetError(txtDate,"Date must be on or before today");
}
}
else
{
flag=false;
SetError(txtDate,"Date must be entered");
}
if (decimal.TryParse(txtAmount.Text, out amount))
{
if (amount<=0)
{
flag=false;
SetError(txtAmount,"Amount must be more than zero");
}
}
else
{
flag = false;
SetError(txtAmount,"Amount must be a number more than zero");
}
if (flag)
{
if (transactionType == TransactionTypes.Withdrawal)
{
if (balance >= amount)
{
amount *= -1;
}
else
{
flag = false;
SetError(txtAmount, "Insufficient funds");
}
}
else if (transactionType == TransactionTypes.ServiceFee)
amount *= -1;
}
if (!flag)
amount = 0;
return amount;
}
private void ShowBalance()
{
lblBalance.Text = balance.ToString("c");
lblBankBalance.Text = bankBalance.ToString("c");
}
private void ClearForm()
{
txtAmount.Clear();
txtAmount.Focus();
txtDate.Clear();
rbWithdrawal.Checked = true;
}
private void CheckbookForm_Load(object sender, EventArgs e)
{
rbDeposit.Tag = TransactionTypes.Deposit;
rbWithdrawal.Tag = TransactionTypes.Withdrawal;
rbServiceFee.Tag = TransactionTypes.ServiceFee;
ClearForm();
ShowBalance();
}
private void btnProcess_Click(object sender, EventArgs e)
{
errorProvider1.Clear();
decimal amount = IsValid();
if (amount != 0)
{
string entry;
string process = "Not Processed";
balance += amount;
if (chkCleared.Checked)
{
bankBalance += amount;
process = "Processed";
}
ShowBalance();
int row = lstTransactions.Items.Count;
entries[row, 0] = amount.ToString();
entries[row, 1] = txtDate.Text;
entries[row, 2] = transactionType.ToString();
entries[row, 3] = chkCleared.Checked.ToString();
entry = string.Format("{0} {1}: {2} ({3})", transactionType.ToString(),
txtDate.Text, amount.ToString("c"), process);
lstTransactions.Items.Add(entry);
}
}
private void btnClear_Click(object sender, EventArgs e)
{
ClearForm();
}
private void btnReset_Click(object sender, EventArgs e)
{
errorProvider1.Clear();
DialogResult button;
button = MessageBox.Show("Clear all prior entries and set balance to $0?\nThis CANNOT be undone.",
"Reset Account", MessageBoxButtons.YesNo);
if (button == System.Windows.Forms.DialogResult.Yes) {
balance = 0;
bankBalance = 0;
ClearForm();
ShowBalance();
lstTransactions.Items.Clear();
}
}
private void btnExit_Click(object sender, EventArgs e)
{
this.Close();
}
private void rb_CheckedChanged(object sender, EventArgs e)
{
RadioButton rb = (RadioButton)sender;
if (rb.Checked)
transactionType = (TransactionTypes) rb.Tag;
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
btnExit_Click(sender, e);
}
private void CheckbookForm_FormClosing(object sender, FormClosingEventArgs e)
{
DialogResult button;
button = MessageBox.Show("Close and exit? All entries will be lost.",
"Exit?", MessageBoxButtons.OKCancel, MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2);
if (button == System.Windows.Forms.DialogResult.Cancel)
e.Cancel = true;
}
private void lstTransactions_SelectedIndexChanged(object sender, EventArgs e)
{
int index = lstTransactions.SelectedIndex;
if (index != -1)
{
txtAmount.Text = entries[index, 0];
txtDate.Text = entries[index, 1];
chkCleared.Checked = bool.Parse(entries[index, 3]);
// entries[index, 2] is transaction type
//make switch comparison work correctly
// to determine which radio button should be checked
switch(entries[index, 2])
{
case TransactionTypes.Deposit.ToString():
rbDeposit.Checked = true;
break;
case TransactionTypes.Withdrawal.ToString():
rbWithdrawal.Checked = true;
break;
default:
rbServiceFee.Checked = true;
break;
}
}
}
}
}
You should ditch the multidimensional array and go with a typed List or Array.
Multidimensional arrays are useful when the data is of the same type, yours it a bit of everything. so we in turn would make a class.
public class Entry
{
public Entry(decimal amount, DateTime date, TransactionType transactionType, bool cleared)
{
Amount = amount;
Date = date;
TransactionType = transactionType;
Cleared = cleared;
}
public decimal Amount { get; set; }
public DateTime Date { get; set; }
public TransactionType TransactionType { get; set; }
public bool Cleared { get; set; }
}
Given
// now an array of Entry
private Entry[] entries = new Entry[20];
Usage
To update update an entry
// add / update and array slot
entries[row] = new Entry(amount, DateTime.Parse(txtDate),transactionType,chkCleared.Checked);
Your switch would look like this
switch (entries[index].TransactionType)
{
case TransactionTypes.Deposit:
rbDeposit.Checked = true;
break;
case TransactionTypes.Withdrawal:
rbWithdrawal.Checked = true;
break;
default:
rbServiceFee.Checked = true;
break;
}
Note : This is not meant to be a working example, or to make your code work, it was merely showing you how you could used a typed array to make your life easier and show a potential for the switch without having to parse your string back into an enum
Alternatively you could just use Enum.Parse
Converts the string representation of the name or numeric value of one
or more enumerated constants to an equivalent enumerated object.
var tranType = (TransactionTypes)Enum.Parse(typeof(TransactionTypes), entries[index, 2]);
switch(tranType)
The error you are facing, has nothing to do with multidimensional arrays. It is due to using a runtime value inside a case logic.
You cannot use case TransactionTypes.Withdrawal.ToString() as a case statement. The last part ToString() is a function call. Even though it will always return the same value, the compiler does not know that. It will always think that the value of case TransactionTypes.Withdrawal.ToString() is not known at compile time, and thus is a potential risk for runtime error and thus will not compile.
You might wanna think what is the harm in having a runtime value. Consider the following code -
switch (entries[index].TransactionType)
{
case TransactionTypes.Withdrawal.ToString()://only known at wexecution not before that
throw a;
break;
case TransactionTypes.Withdrawal.ToString()://only known at wexecution not before that
throw b;
break;
default:
break;
}
Both the cases have same values. But the compiler cannot determine them unless it executes the code and when it will try to do that, there will be a clash in the decision. To save the code for such failure, having dynamic value in a switch-case is prohibited and that is the reason for that exception.
In future the compilers might be more intelligent to determine proper branching based on codes, but for now we are stuck with constant values for cases. So remove the function call or use Enum.Parse to get the enum values and do your check. Something like this -
if(Enum.TryParse<TransactionTypes>(entries[index, 2], true, out TransactionTypes parsed){
switch (parsed)
{
case TransactionTypes.Deposit://constant value, no issue
rbDeposit.Checked = true;
break;
case TransactionTypes.Withdrawal://constant value, no issue
rbWithdrawal.Checked = true;
break;
default:
rbServiceFee.Checked = true;
break;
}
}
else{
throw new Exception("Unknonw enum string");
}
get more details about Enum.Parse or Enum.TryParse here https://learn.microsoft.com/en-us/dotnet/api/system.enum.tryparse?view=netframework-4.8

С# Form buttons do not update; when they are updated

Together with a friend, we are trying to create a sea battle for playing on a local network, we made a field of buttons, created islands on it, you can set up ships and move them. But when we connected the form to our server, after requesting the package, the form stops drawing buttons, they store the correct data, but their visual update does not occur
Here are the pieces of code
private void button1_Click(object sender, EventArgs e)
{
comboBox1.Visible = false;
comboBox2.Visible = false;
button1.Visible = false;
comboBox3.Visible = false;
button2.Visible = true;
SendPacket(PacketInfo.SendGameInfo);
Thread thread = new Thread(FightIsDone);
thread.Start();
State = GameState.Waiting;
}
void FightIsDone()
{
ReceivePacket();
this.Invoke(new Action(() => GameUpdate()));
}
public void GameUpdate()
{
Console.WriteLine("!");
State = GameState.Planing;
foreach (Ship ship in Ships)
{
ship.UseModuls();
}
SetShip(Ships);
UpdateSight();
Draw();
}
The problem is here, this method changes the image of the button or its color, it works fine until the packet is sent to the server, but after receiving it, only the information is updated, and the appearance of the form does not change
public void Draw()
{
foreach (CellButton Butt in Field)
{
if (Butt.Sigth)
{
Butt.Image = null;
SetImage(Butt);
}
else
{
Butt.BackColor = Color.Gray;
Butt.Image = null;
}
}
}
public void SetImage(CellButton Butt)
{
switch (Butt.Entety)
{
case GameObject.Water: Butt.BackColor = Color.DodgerBlue; break;
case GameObject.Island: Butt.Image = GetObjectImage(GameObject.Island); break;
case GameObject.Ship:
{
Butt.Image = Butt.modul.ModulImage;
break;
}
case GameObject.Torpedo: Butt.BackColor = Color.DarkBlue; break;
case GameObject.Fire: Butt.Image = GetObjectImage(GameObject.Fire); break;
}
}
static void SendPacket(PacketInfo info)
{
ms.Position = 0;
switch (info)
{
case PacketInfo.Connect:
writer.Write(0);
break;
case PacketInfo.SendCoordinate:
writer.Write(1);
formatter.Serialize(ms, new Coordinate(5, 0));
break;
case PacketInfo.LaunchSearch:
writer.Write(2);
writer.Write(Me.ID);
break;
case PacketInfo.SendGameInfo:
writer.Write(3);
writer.Write(Me.ID);
List<Ship> shpooks = new List<Ship>();
foreach (Ship s in Ships)
if (s.PlayerID == Me.ID) shpooks.Add(s);
formatter.Serialize(ms, shpooks);
break;
}
socket.Send(ms.GetBuffer());
}
static int ReceivePacket()
{
ms.Position = 0;
socket.Receive(ms.GetBuffer());
int code = reader.ReadInt32();
switch (code)
{
case 0:
{
Console.WriteLine("Catch");
Console.WriteLine(ms.ToString());
bool IsConected = reader.ReadBoolean();
if (IsConected)
{
Me.ID = reader.ReadInt32();
return 1;
}
break;
}
case 2:
{
return 1;
break;
}
case 3:
{
Cell[,] cells = (Cell[,])formatter.Deserialize(ms);
for (int i = 0; i < Width; i++)
for (int j = 0; j < Hight; j++)
{
Field[i, j] = new CellButton(new Coordinate(i, j));
Field[i, j].SetCell(cells[i, j]);
}
Ships.Clear();
Ships.AddRange((List<Ship>)formatter.Deserialize(ms));
break;
}
}
return -1;
}
This is the form before calling Draw():
This is how the form looks after calling Draw():
And so, after a request to the server and method Draw():
I spent a lot of time finding solutions, methods like Form.Update() and Form.Refresh() don't help

Customize TextBox autocomplete

How do I change the autocomplete on a TextBox? I want that when I type a string, the box suggest items containing that string instead of starting with.
My code is:
class MyClass
{
private AutoCompleteStringCollection autoCompleteList = new AutoCompleteStringCollection();
public MyClass()
{
InitializeComponent();
autoCompleteList.AddRange(ListNames.Select(x=>x.Name).ToArray());
textBoxName.AutoCompleteCustomSource = autoCompleteList;
textBoxName.AutoCompleteSource = AutoCompleteSource.CustomSource;
textBoxName.AutoCompleteMode = AutoCompleteMode.Suggest;
textBoxName.KeyDown += TextBoxtextName_KeyDown;
}
private void TextBoxClient_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Enter)
{
this.Name = (sender as TextBox).Text;
}
}
}
What I want:
Looking at your code you have everything you need but 1 line of code. That line is:
This will only work if the start of a string is entered
//Suggestion only
textBoxName.AutoCompleteMode = AutoCompleteMode.Suggest;
//Suggest and autocomplete
textBoxName.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
This will work as a contains method but works with a custom control
You can also make a custom textbox control that fits your needs.
Custom textbox class:
public class AutoCompleteTextBox : TextBox
{
private ListBox _listBox;
private bool _isAdded;
private String[] _values;
private String _formerValue = String.Empty;
public AutoCompleteTextBox()
{
InitializeComponent();
ResetListBox();
}
private void InitializeComponent()
{
_listBox = new ListBox();
this.KeyDown += this_KeyDown;
this.KeyUp += this_KeyUp;
}
private void ShowListBox()
{
if (!_isAdded)
{
Parent.Controls.Add(_listBox);
_listBox.Left = Left;
_listBox.Top = Top + Height;
_isAdded = true;
}
_listBox.Visible = true;
_listBox.BringToFront();
}
private void ResetListBox()
{
_listBox.Visible = false;
}
private void this_KeyUp(object sender, KeyEventArgs e)
{
UpdateListBox();
}
private void this_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Enter:
case Keys.Tab:
{
if (_listBox.Visible)
{
Text = _listBox.SelectedItem.ToString();
ResetListBox();
_formerValue = Text;
this.Select(this.Text.Length, 0);
e.Handled = true;
}
break;
}
case Keys.Down:
{
if ((_listBox.Visible) && (_listBox.SelectedIndex < _listBox.Items.Count - 1))
_listBox.SelectedIndex++;
e.Handled = true;
break;
}
case Keys.Up:
{
if ((_listBox.Visible) && (_listBox.SelectedIndex > 0))
_listBox.SelectedIndex--;
e.Handled = true;
break;
}
}
}
protected override bool IsInputKey(Keys keyData)
{
switch (keyData)
{
case Keys.Tab:
if (_listBox.Visible)
return true;
else
return false;
default:
return base.IsInputKey(keyData);
}
}
private void UpdateListBox()
{
if (Text == _formerValue)
return;
_formerValue = this.Text;
string word = this.Text;
if (_values != null && word.Length > 0)
{
string[] matches = Array.FindAll(_values,
x => (x.ToLower().Contains(word.ToLower())));
if (matches.Length > 0)
{
ShowListBox();
_listBox.BeginUpdate();
_listBox.Items.Clear();
Array.ForEach(matches, x => _listBox.Items.Add(x));
_listBox.SelectedIndex = 0;
_listBox.Height = 0;
_listBox.Width = 0;
Focus();
using (Graphics graphics = _listBox.CreateGraphics())
{
for (int i = 0; i < _listBox.Items.Count; i++)
{
if (i < 20)
_listBox.Height += _listBox.GetItemHeight(i);
// it item width is larger than the current one
// set it to the new max item width
// GetItemRectangle does not work for me
// we add a little extra space by using '_'
int itemWidth = (int)graphics.MeasureString(((string)_listBox.Items[i]) + "_", _listBox.Font).Width;
_listBox.Width = (_listBox.Width < itemWidth) ? itemWidth : this.Width; ;
}
}
_listBox.EndUpdate();
}
else
{
ResetListBox();
}
}
else
{
ResetListBox();
}
}
public String[] Values
{
get
{
return _values;
}
set
{
_values = value;
}
}
public List<String> SelectedValues
{
get
{
String[] result = Text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
return new List<String>(result);
}
}
}
Usage:
string[] nameArray = { "name1", "name2", "name3", "bla name" };
AutoCompleteTextBox tb = new AutoCompleteTextBox();
tb.Values = nameArray;
tb.Location = new Point(10,10);
tb.Size = new Size(25,75);
this.Controls.Add( tb );
I got the code for the custom control from: SO Question - Autocomplete contains

C# : Mini Application Structural Design (Classes/Interfaces/etc.)

I've been creating a small application that allows a user to convert images to various sizes and formats. I've been struggling on getting a good solid design with this application. I have the application up and running, but it does integrate good Object-Oriented design. Since this is a personal project, I've been wanting to learn more about integrating interfaces, good class inheritance, object composition, and other elements of OO design.
However, I've been struggling to do so. Don't get me wrong, I know about OO design and what it is, I just don't know how to implement good OO design in projects. Of course its easy to look at class Examples that you read in books, or online. Examples may have simple scenarios such as the following.
Interface IPerson has member functions Walk(), Run() . Abstract Class Person uses IPerson Interface. Class Man and Class Female inherit from Abstract Class Person.
but when it comes to Real Projects I struggle to implement good design. I was hoping for some insight. Here is what I currently have.
Interface:
interface IPicture
{
Bitmap ReturnImage(string path, int width, int height);
}
Main Class that Holds Picture Information. This class basically stores information about the image passed, and information about the new values the user wants (i.e. new size, new file location, new pic format, etc.)
public class MyPictures : IPicture
{
//All Private variables below are properties. Property get/set's have been removed
//for the sake of space
private int _NewWidth;
private int _NewHeight;
private string _NewImgName;
private string _NewImgPath;
private string _NewImgFullPath;
private ImageFormat _NewImgFormat;
//Declare variables to hold values that have been determined
private int _OldWidth;
private int _OldHeight;
private string _OldImgName;
private string _OldImgPath;
//Old Image Format is in String format because of certain extension scenarios.
private string _OldImgFormat;
public MyPictures(Image img, string file)
{
ClearProperties();
//...set properties based on passed variables in constructor...
}
public void ClearProperties()
{
_NewWidth = 0;
_NewHeight = 0;
_NewImgName = "";
_NewImgPath = "";
_NewImgFullPath = "";
_NewImgFormat = null;
_OldWidth = 0;
_OldHeight = 0;
_OldImgName = "";
_OldImgPath = "";
_OldImgFormat = null;
}
public override string ToString()
{
return _OldImgPath;
}
public void ImageSave()
{
Bitmap tempBmp = new Bitmap(_OldImgPath);
Bitmap bmp = new Bitmap(tempBmp, _NewWidth, _NewHeight);
bmp.Save(_NewImgPath + #"\" + _NewImgName + "." + _NewImgFormat.ToString().ToLower(), _NewImgFormat);
}
public Bitmap ImageClone()
{
Bitmap bmp = new Bitmap(_OldImgPath);
return bmp;
}
Bitmap IPicture.ReturnImage(string path, int width, int height)
{
return new Bitmap(new Bitmap(path), width, height);
}
}
Main Class; Starting point of application. This definatly needs some work...
public partial class Form1 : Form
{
static bool hasThreadBeenStopped = false;
static bool imageProcessingComplete = false;
static bool imgConstrained = false;
//Default text when user selects 'All' checkbox for new image name
static string newNameDefault = "'Name' + #";
Utility.Validation.Validate valid = new Utility.Validation.Validate();
public Form1()
{
InitializeComponent();
//Populate Combo Box With Possible Image Formats...
//Conditionally show Image Properties...
ImgPropertiesEnabled();
//Set static progress bar properties...
progressBar1.Minimum = 0;
progressBar1.Step = 1;
}
private void Form1_Load(object sender, EventArgs e)
{
lblImgProcessed.Text = "";
lblFile.Text = "";
txtContentFolder.Text = "";
}
//Delegate declarations. Used for multi-thread processing
public delegate void PopulateTextboxDelegate(Label lbl, string text);
public delegate void ThreadWorkDelegate(Label lbl, string text);
public delegate void ImageDisplayDelegate(Image i);
public delegate void ProgressBarDelegate(ProgressBar p, int step, int value);
//Populate textbox fields with image processed, and image path being processed
public void PopulateTextbox(Label lbl, string text)
{
lbl.Text = "";
lbl.Text = text;
}
public void ThreadWork(Label lbl, string text)
{
this.Invoke(new PopulateTextboxDelegate(PopulateTextbox),
new object[] { lbl, text });
}
//Display Currently Processed Image
public void ImageDisplay(Image i)
{
pbMain.Image = null;
pbMain.Image = i;
}
public void ThreadWorkImg(Image i)
{
this.Invoke(new ImageDisplayDelegate(ImageDisplay),
new object[] {i});
}
//Increment Progress Bar
public void ProgressBarDisplay(ProgressBar pg, int max, int value)
{
//Dynamically set the Progress Bar properties
pg.Maximum = max;
pg.Value = value;
}
public void ThreadProgress(ProgressBar p, int max, int value)
{
this.Invoke(new ProgressBarDelegate(ProgressBarDisplay),
new object[] { p, max, value });
}
private void btnStart_Click(object sender, EventArgs e)
{
string IsValidResult = IsValid();
//If string is empty, Utility passed
if (IsValidResult == "")
{
Thread t = new Thread(new ThreadStart(ProcessFiles));
t.Start();
}
else
{
MessageBox.Show(IsValidResult);
}
}
public void ProcessFiles()
{
int count = 0;
ThreadWorkDelegate w = ThreadWork;
ImageDisplayDelegate im = ThreadWorkImg;
ProgressBarDelegate pb = ThreadProgress;
try
{
foreach (MyPictures mp in lstHold.Items)
{
try
{
if (hasThreadBeenStopped == false)
{
//Disable certain controls during process. We will use the generic
//MethodInvoker, which Represents a delegate that can execute any method
//in managed code that is declared void and takes no parameters.
//Using the MethodInvoker is good when simple delegates are needed. Ironically,
//this way of multi-thread delegation was used because the traditional way as used
//by the rest of the delegates in this method, was not working.
btnApply.Invoke(new MethodInvoker(delegate { btnApply.Enabled = false; }));
btnStart.Invoke(new MethodInvoker(delegate { btnStart.Enabled = false; }));
//Call delegate to show current picture being processed
im.BeginInvoke(mp.ImageClone(), null, null);
mp.ImageSave();
//Increment Count; Image has been processed
count++;
//Invoke Img Proceessed Output
w.BeginInvoke(lblImgProcessed, count.ToString() +
" of " + lstHold.Items.Count.ToString() + " processed",
null, null);
//Invoke File Process Output
w.BeginInvoke(lblFile, mp.NewImgPath, null, null);
//Invoke Progressbar output. Delegate is passed The count of images,
//which will be set as the progressbar max value. the 'count' variable is
//passed to determine the current value.
pb.BeginInvoke(progressBar1, lstHold.Items.Count, count, null, null);
}
else //Thread has been called to stop
{
MessageBox.Show("Image Processing Stopped: " + count + "of " +
lstHold.Items.Count + " processed");
//Enable controls after process
btnApply.Invoke(new MethodInvoker(delegate { btnApply.Enabled = true; }));
btnStart.Invoke(new MethodInvoker(delegate { btnStart.Enabled = true; }));
break;
}
}
catch (Exception ex)
{
MessageBox.Show("Error while processing pictures");
break;
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error while attempting to execute pictures: " + ex.ToString());
}
finally
{
//Loop has ended:
//In finally statement, re-enable disabled controls
//Enable certain controls during process
btnApply.Invoke(new MethodInvoker(delegate { btnApply.Enabled = true; }));
btnStart.Invoke(new MethodInvoker(delegate { btnStart.Enabled = true; }));
//Reset class variables
hasThreadBeenStopped = false;
imageProcessingComplete = false;
}
}
private void btnContent_Click(object sender, EventArgs e)
{
string selection = null;
string[] files = null;
lstAll.Items.Clear();
contentBrowser.ShowDialog();
selection = contentBrowser.SelectedPath;
txtContentFolder.Text = selection;
if (selection != "" || selection != null)
{
try
{
files = System.IO.Directory.GetFiles(selection.Trim());
foreach (string file in files)
{
lstAll.Items.Add(file);
}
}
catch (Exception ex)
{
// MessageBox.Show(ex.ToString());
}
}
}
private void btnGo_Click(object sender, EventArgs e)
{
//Grab files from folder based on user input in the textbox.
string selection = txtContentFolder.Text.Trim();
string[] files = null;
lstAll.Items.Clear();
if (valid.IsNull(selection) == false || valid.IsEmpty(selection) == false)
{
try
{
files = System.IO.Directory.GetFiles(selection);
foreach (string file in files)
{
lstAll.Items.Add(file);
}
}
catch (Exception ex)
{
MessageBox.Show("Invalid Directory");
}
}
txtContentFolder.Text = selection;
}
private void btnDestination_Click(object sender, EventArgs e)
{
string selection = null;
destinationBrowser.ShowDialog();
selection = destinationBrowser.SelectedPath;
txtNewImgPath.Text = selection;
}
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
private void btnStop_Click(object sender, EventArgs e)
{
//Flag variable that the stop button has been called. This variable is checked
//conditionally when looping over each picture.
hasThreadBeenStopped = true;
}
public string IsValid()
{
StringBuilder sb = new StringBuilder("");
if (lstHold.Items.Count <= 0)
{
return "No items exist to process";
}
//Validate that there is a value in each field for every object in lstHold. All the fields will be
//validated. Note: If there is one invalid field, the rest do not need to be considered.
foreach (MyPictures mp in lstHold.Items)
{
if (mp.NewImgName == "")
{
sb.Append(mp.OldImgPath + ", ");
}
else if (mp.NewImgPath == "")
{
sb.Append(mp.OldImgPath + ", ");
}
else if (mp.NewImgFormat == null)
{
sb.Append(mp.OldImgPath + ", ");
}
else if (mp.NewWidth == 0)
{
sb.Append(mp.OldImgPath + ", ");
}
else if (mp.NewHeight == 0)
{
sb.Append(mp.OldImgPath + ", ");
}
}
//If the returned string is empty, the image is valid. The check for the listbox's count
//will return a string immediatly if false. Because of this, we know that the returning
//string at this level will either be empty (validation passed) or filled with image paths
//of images missing required values. If image is not valid, return this concatenated string of image paths
//that are missing values, and insert a prefixed string literal to this list.
if (sb.ToString() != "")
{
sb.Insert(0, "The following images are missing required values: ");
return sb.ToString();
}
else //String is empty and has passed validation
{
return sb.ToString();
}
}
private void btnMoveOne_Click(object sender, EventArgs e)
{
//Loop through All strings in the lstAll list box. Then use each picture path to convert
//each picture into their own class
foreach (string file in lstAll.SelectedItems)
{
//isImgExistFlag is a flag indicating wheter the image coming from lstAll already exists
//in lstHold. By default, the variable is false. It is set to true if an image does exist
//This variable must be re-created within the scope of the main foreach loop to ensure a proper
//reset of the variable for each image comparison.
bool isImgExistFlag = false;
try
{
Image img;
img = Image.FromFile(file);
MyPictures mp = new MyPictures(img,file);
//If lstHold contains no items, add the item with no validation check.
if (lstHold.Items.Count == 0)
{
lstHold.Items.Add(mp);
}
else
{
//Run through each object in the lstHold to determine if the newly created object
//already exists in list box lstHold.
for (int i = 0; i < lstHold.Items.Count; i++)
{
MyPictures p = (MyPictures)lstHold.Items[i];
//Unique objects will be identified by their Original Image Path, because
//this value will be unique
if (p.OldImgPath == mp.OldImgPath)
{
isImgExistFlag = true;
}
}
//If isImgExistFlag is false, the current Image object doesnt currently exist
//in list box. Therefore, add it to the list.
if (isImgExistFlag == false)
{
lstHold.Items.Add(mp);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
private void btnMoveAll_Click(object sender, EventArgs e)
{
//This event has the same functionality as btnMoveOne_Click, except the main foreach loop
//is based on all of lstAll's items, rather than just the selected items.
foreach (string file in lstAll.Items)
{
bool isImgExistFlag = false;
try
{
Image img;
img = Image.FromFile(file);
MyPictures mp = new MyPictures(img, file);
if (lstHold.Items.Count == 0)
{
lstHold.Items.Add(mp);
}
else
{
for (int i = 0; i < lstHold.Items.Count; i++)
{
MyPictures p = (MyPictures)lstHold.Items[i];
if (p.OldImgPath == mp.OldImgPath)
{
isImgExistFlag = true;
}
}
if (isImgExistFlag == false)
{
lstHold.Items.Add(mp);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
private void btnRemoveOne_Click(object sender, EventArgs e)
{
/*
Create a seperate List to populate:
This is necessary because if you explicitly remove an item from the listbox
you will get the following error:
"List that this enumerator is bound to has been modified. An enumerator can
only be used if the list does not change."
*/
//This variable will keep track of the first index processed.
int first_index = 0;
int count = 0;
List<MyPictures> TempMp = new List<MyPictures>();
if (lstHold.Items.Count >= 1)
{
try
{
foreach (MyPictures mp in lstHold.SelectedItems)
{
if (count == 0)
{
first_index = lstHold.SelectedIndex;
}
//Add objects to be removed
TempMp.Add(mp);
}
foreach (MyPictures mp2 in TempMp)
{
lstHold.Items.Remove(mp2);
}
}
catch (Exception ex)
{
//Hide Error: MessageBox.Show(ex.ToString());
}
//Select new item in list if possible, as long as there is a item in the list
if (lstHold.Items.Count >= 1)
{
//If the first_index variable = the amount of items in the list, the new selected index
//should be the first index -1. This is because the variable first_index would be the
//index of the now deleted item in the list. Therefore we must subtract the variable by 1
//before assigning it to the selected value. Otherwise, we'll be assigning a selected index that
//no longer exists.
//There is also a check to make sure there is more than one item in the list. Otherwise, we could
//potentially assign a selected index of -1.
if (first_index == lstHold.Items.Count && lstHold.Items.Count != 1)
{
lstHold.SelectedIndex = first_index - 1;
}
else if (lstHold.Items.Count == 1)
{
lstHold.SelectedIndex = 0;
}
else
{
lstHold.SelectedIndex = first_index;
}
}
else
{
ClearTextBoxes();
}
}
}
private void btnRemoveAll_Click(object sender, EventArgs e)
{
lstHold.Items.Clear();
ClearTextBoxes();
ImgPropertiesEnabled();
}
private void lstHold_SelectedIndexChanged(object sender, EventArgs e)
{
//This prevents trying to access a negative index. This can happen when a item is removed.
if (lstHold.SelectedIndex >= 0)
{
try
{
MyPictures mp = (MyPictures)lstHold.Items[lstHold.SelectedIndex];
txtOldName.Text = mp.OldImgName;
txtOldImgPath.Text = mp.OldImgPath;
txtOldImgFormat.Text = mp.OldImgFormat.ToString();
txtOldWidth.Text = mp.OldWidth.ToString();
txtOldHeight.Text = mp.OldHeight.ToString();
txtNewName.Text = mp.NewImgName;
cbFormat.SelectedItem = mp.NewImgFormat;
txtNewWidth.Text = mp.NewWidth.ToString();
txtNewHeight.Text = mp.NewHeight.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
//Call function to determine which controls should be enabled/disabled
ImgPropertiesEnabled();
}
private void btnApply_Click(object sender, EventArgs e)
{
//Reset color. It could be grey depending on if user changed default name.
txtNewName.ForeColor = Color.Black;
if (lstHold.SelectedIndex == -1)
{
MessageBox.Show("Picture not selected. Select picture to apply properties to.");
}
else if (lstHold.SelectedIndex >= 0)
{
MyPictures mp = (MyPictures)lstHold.Items[lstHold.SelectedIndex];
//User wants to apply a generated name to all pictures within the list
if (chkNewPicName.Checked == true)
{
int count = 0;
foreach (MyPictures pic in lstHold.Items)
{
pic.NewImgName = txtNewName.Text + count.ToString();
++count;
}
txtNewName.Text = mp.NewImgName;
}
//User wants to apply a custom name to this picture only
else
{
mp.NewImgName = txtNewName.Text;
}
//User wants to apply this path to all pictures within the list
if (chkNewPicPath.Checked == true)
{
foreach (MyPictures pic in lstHold.Items)
{
pic.NewImgPath = txtNewImgPath.Text;
}
txtNewImgPath.Text = mp.NewImgPath;
}
//User wants to apply this path to this picture only
else
{
mp.NewImgPath = txtNewImgPath.Text;
}
//User wants to apply this image format to all pictures within the list
if (chkNewPicFormat.Checked == true)
{
foreach (MyPictures pic in lstHold.Items)
{
pic.NewImgFormat = (ImageFormat)cbFormat.SelectedItem;
}
}
//User wants to apply this image format to this picture only
else
{
mp.NewImgFormat = (ImageFormat)cbFormat.SelectedItem;
}
//User wants to apply this size to all pictures
if (chkNewSize.Checked == true)
{
foreach (MyPictures pic in lstHold.Items)
{
pic.NewWidth = Convert.ToInt32(txtNewWidth.Text);
pic.NewHeight = Convert.ToInt32(txtNewHeight.Text);
}
txtNewWidth.Text = mp.NewWidth.ToString();
txtNewHeight.Text = mp.NewHeight.ToString();
}
//User wants to apply this size to this picture only
else
{
mp.NewWidth = Convert.ToInt32(txtNewWidth.Text);
mp.NewHeight = Convert.ToInt32(txtNewHeight.Text);
}
mp.NewImgName = txtNewName.Text;
mp.NewImgFormat = (ImageFormat)cbFormat.SelectedItem;
mp.NewWidth = Convert.ToInt32(txtNewWidth.Text);
mp.NewHeight = Convert.ToInt32(txtNewHeight.Text);
}
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (chkSelectAll.Checked)
{
chkNewPicName.Checked = true;
chkNewPicPath.Checked = true;
chkNewPicFormat.Checked = true;
chkNewSize.Checked = true;
}
else
{
chkNewPicName.Checked = false;
chkNewPicPath.Checked = false;
chkNewPicFormat.Checked = false;
chkNewSize.Checked = false;
}
}
private void previewToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("hi there!");
}
private void btnPreview_Click(object sender, EventArgs e)
{
try
{
if (lstHold.Items.Count <= 0)
{
MessageBox.Show("No pictures are available to preview");
}
else if (lstHold.SelectedItem == null)
{
MessageBox.Show("No picture is selected to preview");
}
else
{
MyPictures mp = (MyPictures)lstHold.SelectedItem;
//Bitmap bmp = new Bitmap(mp.OldImgPath);
Form2 frm = new Form2(mp);
frm.Show();
}
}
catch (Exception ex)
{
MessageBox.Show("An Error has occured:\n " + ex.ToString());
}
}
public void ImgPropertiesEnabled()
{
//Enable Image properties when an image is selected
if (lstHold.SelectedIndex >= 0)
{
gbCheckAll.Enabled = true;
gbImgProperties.Enabled = true;
}
else
{
//Disable Image properties when an image is not selected
gbCheckAll.Enabled = false;
gbImgProperties.Enabled = false;
}
//Preview buttons enablement will depend on the same conditions
btnPreview.Enabled = gbImgProperties.Enabled;
}
public void ClearTextBoxes()
{
txtNewImgPath.Text = "";
txtNewName.Text = "";
txtNewHeight.Text = Convert.ToString(0);
txtNewWidth.Text = Convert.ToString(0);
cbFormat.SelectedItem = null;
chkSelectAll.Checked = false;
}
}
Having scanned through the code, yes it is eleborate... maybe a little to much ;)
One thing that i noticed was your naming conventions. Even though it does not change anything in runtime it does make an API/code-maintenance easier.
So, instead of having an IPicture, i would make it something like `IResizableImage´ (reading your spec, thats what it is. Not just a picture, but a resizable one)
Instead of ´ReturnImage()´ i would use something like ´Scale()´. 'ImageSave()' to 'Save()'
Your code will start to read (Which added symantical information by naming convention)
IResizableImage myImg = new ResizableImage( orignalBitmap );
Image rescaledImg = myImg.Scale( "new path", 320,240 );
resccaledImg.Save();
instead of:
IPicture myImg = new MyPictures();
Image rescaled = myImg.ReturnImage( "newpath", 320, 240 );
rescaledImg.ImageSave();
So, Generally classes are nouns, methods are verbs, adjetives are properties/fields. Try to minimize duplication or redancy. "ImageSave" is a method on your Image. Isn't "Image.Save()" clearer than "Image.ImageSave()"?
Just some of my thoughts;
In coding guidelines there is no absolute right or wrong.
Think of being another person when USING the API versus WRITING the API. Jump out of the box of "i know what it does" and imagine being a user never having seen this API before. Does it feel natural and easy accesible?
Hope this helps,
Here are some improvements for you code and design. This tips are not all OO related but you should be aware that good design is not just OO design.
1.Avoid commenting what is obvious.
//Declare variables to hold values that have been determined
private int _OldWidth;
This comment is superfluous because any programmers will understand that is a declaration.
2.Avoid giving wrong name. For example the class "MyPictures" is not really correct because:
Is holds just one picture, while the name suggests many pictures.
It contains "My" which, in my opinion is not correct since if I read your code is not my class. It is yours ;)
3.Avoid concatenating strings. Use string.Format or, for paths, Path.Combine
bmp.Save(_NewImgPath + #"\" + _NewImgName + "." + _NewImgFormat.ToString().ToLower(), _NewImgFormat);
4.Keep methods short. It is hard to keep all methods to 5 lines of code but 30 lines (if my count is correct - without comments and empty lines) for ProcessFiles is a little bit too much.
5.Don't use design elements just because you want to have them. I see no reason to use the interface in your code. In your case it just increases the complexity of your code. Even more, you haven't used it(!!!). You just implemented it and that's all. Use interfaces when you have multiple types that share common functionality (the ones in interface) and you want to treat them all similar without being aware of the actual implementation.
interface IImage
{
void DrawLine(Point startPoint, Point endPoint);
}
class MonochromeImage:IImage
{
void DrawLine(Point startPoint, Point endPoint)
{
//Draw a monochrome line on images with one channel
}
}
class ColorImage:IImage
{
void DrawLine(Point startPoint, Point endPoint)
{
//Draw a red line on images with three channels
}
}
...
void DrawLineOnImage()
{
List<IImage> images = new List<IImage>();
images.Add(new ColorImage());
images.Add(new MonochromeImage());
//I am not aware of what kind of images actually have
//all it matters is to have a draw line method
foreach(IImage image in images)
{
image.DrawLine(p1,p2)
}
}
6.As others already mentioned, try to separate the presentation (graphical user interface - GUI) from the logic. Make it in such a way that you can replace the GUI without changing logic code.
7.Make single responsibility functions. btnMoveOne_Click has more than one responsibility: it checks if file exists and it handles elements on user interface.
8.You image class is coupled to the file system. What happens if I want to store images created in memory? What is the path then? Here is where you can improve the design of the class. Make it in such a way it doesn't matter if files are from disk (HINT: in a FileStream) or from memory (HINT: in a MemoryStream) or any other place.
That's all for now. Hope this information will help you.
To achieve good design you need to apply TDD (Test Driven Design).
You will soon find then testability requires separating the project to layers, such as presentation and business logic.
Start covering your project with tests, and you won't believe how fast you will find design inconsistences with it.
Things will just stand up and scream: "No way you will test me!"
The worst anemy nere is the code buried in the WinForms.
What you can do is making a view "humble". http://codebetter.com/blogs/jeremy.miller/archive/2007/05/23/build-your-own-cab-part-2-the-humble-dialog-box.aspx
As for the project samples, you have to look at architectural patterns, not the OOP samples.
The keywords you will be lookign for are MVC, MVP, MVVM.
Well, here's what I'd do. It's probably different than what many people would do, but I think it's a pretty good, flexible design.
public abstract class AbstractConverter : IImageHandler
{
public AbstractConverter(IImageHandler nextHandler)
{
output = nextHandler;
}
public void HandleImage(Bitmap b)
{
var outBitmap = Convert(b);
output.HandleImage(outBitmap);
}
protected abstract Bitmap Convert(Bitmap input);
private IImageHandler output;
}
public interface IImageHandler
{
void HandleImage(Bitmap b);
}
Now, the rest of your app is:
Creating implementations of AbstractConverter to handle the individual transformations you want
Creating something that can build and wire converters together
Getting the initial Bitmap, and writing the final result out.

Categories

Resources