|
So why have you posted it in the C# forum?
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
I'm trying to make a ToDo app but I'm stuck. I did a UserControl named Item that consists of a panel which includes a checkbox, a textbox and a button: [^]
The problem is that I don't know how to manage those dinamically generated items. For instance, if I delete one item by pressing that X button (I call the method Dispose on DeleteButton_Click ), how could I arrange the remaining items in order to be one after another? See this screenshot: [^]
Any advice?
modified 11-Feb-18 19:32pm.
|
|
|
|
|
How about trying the FlowLayoutPanel in the Toolbox.
|
|
|
|
|
Calling Dispose doesn't remove anything from anywhere, it just closes down and releases any resources the object used - which means that just calling Dispose on objects in the display will not only not work, but is very liable to throw an ObjectDisposedException[^] when it gets subsequently looked at.
Start by looking at how you add new objects, because that is where you need to delete them: an object can't delete itself, because it doesn't (or shouldn't) know what container it is held in.
If you add them to a panel:
Item item = new Item();
myPanel.Controls.Add(item); Then you need to remove them from the panel:
myPanel.Controls.Remove(item);
item.Dispose(); But it's going to be similar for all other containers.
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Hi, Griff,
Calling 'Dispose on a WinForm Control will remove it from its container.
However, as you state, that is not best practice.
cheers, Bill
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
Hi Bill!
Thanks for that. It does if you call the bool version (which I didn't know), but it doesn't if you call "vanilla" Dispose :
void IDisposable.Dispose() {
if (hBitmapDC.Handle == IntPtr.Zero || hMetafileDC.Handle == IntPtr.Zero || hBitmap.Handle == IntPtr.Zero) {
return;
}
bool success;
try {
success = DICopy(hMetafileDC, hBitmapDC, destRect, true);
Debug.Assert(success, "DICopy() failed.");
SafeNativeMethods.SelectObject(hBitmapDC, hOriginalBmp);
success = SafeNativeMethods.DeleteObject(hBitmap);
Debug.Assert(success, "DeleteObject() failed.");
success = UnsafeNativeMethods.DeleteCompatibleDC(hBitmapDC);
Debug.Assert(success, "DeleteObject() failed.");
}
finally {
hBitmapDC = NativeMethods.NullHandleRef;
hBitmap = NativeMethods.NullHandleRef;
hOriginalBmp = NativeMethods.NullHandleRef;
GC.SuppressFinalize(this);
}
}
Reference Source[^]
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Hi, Griff,
Strange, I just checked in VS 2017, FrameWork 4.7.1, and the vanilla no-param version did remove the Control.
TextBox tbx = new TextBox { Text = "wtf" };
this.Controls.Add(tbx);
var c1 = this.Controls.Count;
tbx.Dispose();
var c2 = this.Controls.Count;
bool isnull = tbx == null;
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
So the reference sources aren't the source code to .NET? Ouch!
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Hi, this is what I see:
=public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize((object) this);
} Maybe source is different for Thailand ?
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
Or I misread it all ... equally possible!
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
Hypothesis: the 'Dispose source you looked at is different from the 'Dispose source in the 'ComponentModel class.
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
In the last ten years, I think I've written about ten variations on this type of control
Key point: Controls added to a ControlCollection and docked 'Top followed by the use of 'BringToFront on the Control will be added beneath other controls. When one of these controls is removed/deleted, the other Controls will rearrange so that the order is maintained. I suggest:
0. in the 'ToDo UserControl: expose only the UI elements through public properties initialized in the constructor.
1. make a second UserControl called 'ToDoContainer:
a. put a Panel, docked 'Top, in it to hold UI elements that affect instances of the 'ToDo controls, like a Button to add another ToDo.
b. add a second Panel to 'ToDoContainer, docked 'Fill, with 'AutoScroll 'true.
When the add Button on the top Panel is clicked:
a. create the ToDo instance:
a.1. set event handlers for the ToDo close-button Click, and checkbox CheckStateChanged, events.
a.2. add the new ToDo to the controls of the second Panel.
a.3. call the 'BringToFront method on the new ToDo: this will add it to the bottom of any other ToDos displayed in the Panel.
Of course, you will probably want to expose Events in the container for external consumers of your code, like: ToDo added, ToDo deleted, ToDo activated/de-activated, etc.
And, some nice features to add are:
1. highlight the current/selected/active ToDo visually
2. use color in some way to indicate priority
3. implement keyboard handling to navigate in ToDo collection.
4. implement sorting of ToDos by criteria like date, priority, etc.
Suggestion: by making add and close/delete ToDo a result of the user using the top panel controls, you can keep the ToDo control itself simpler.
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
modified 12-Feb-18 8:02am.
|
|
|
|
|
Thanks for this useful post. However I don't understand this:
Quote: a.1. set event handlers for the ToDo close-button Click, and checkbox CheckStateChanged, events.
Where should I put these event handlers: in the ToDoContainer or in the Item usercontrol? On a side note, I'm confused about how could I transmit the events from a usercontrol back to the MainForm (e.g. if I click a Delete button from usercontrol how could I notify the MainForm about this?).
PS: I uploaded the VC solution so far if you want to take a look: download[^].
modified 12-Feb-18 10:42am.
|
|
|
|
|
I think you're doing fine. When a Control manages/hosts a set of other Controls, my preference is to keep the hosted controls (your 'Item) as "dumb" as possible, and to centralize their management by code in the container Control. So the 'Item might look like this:
public ToDo()
{
InitializeComponent();
CbxDone = cbxDone;
LblID = lblId;
BtnClose = btnClose;
TxtBx = textBox1;
}
public CheckBox CbxDone { set; get; }
public Label LblID { set; get; }
public Button BtnClose { set; get; }
public TextBox TxtBx { set; get; } In the host container the 'Add button handler might look like this:
private void btnAdd_Click(object sender, EventArgs e)
{
ToDo todo = new ToDo();
currentToDos.Add(todo);
currentToDo = todo;
todo.LblID.Text = (idCount).ToString();
idCount++;
todo.Enter += TodoOnEnter;
todo.Leave += TodoOnLeave;
todo.BtnClose.Click += BtnCloseOnClick;
todo.CbxDone.CheckStateChanged += CbxDoneOnCheckStateChanged;
todopanel.Controls.Add(todo);
todo.Dock = DockStyle.Top;
todo.BringToFront();
this.ActiveControl = todo;
} When you close one of the hosted controls"
currentToDos.Remove(currentToDo);
this.todopanel.Controls.Remove(currentToDo);
currentToDo.Dispose();
this.Invalidate(); I would keep track of the hosted controls in several ways: I might keep separate lists for completed tasks, incomplete tasks, selected tasks, and, I might even not destroy tasks, but either disable them while leaving them visible), or keep the ones removed from the display list in a list for future use. Your choices will depend on what you want your app to do, how you want to preserve the state of the controls and their data when the app closes, etc.
In production code, I would be concerned about making the hosted controls only accessible to the host, through design-time attributes like:
[DesignTimeVisible(false)]
public partial class ToDo : UserControl
And other techniques.
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
Thanks again. This is what I did so far:
Item.cs
using System.Windows.Forms;
namespace ToDo
{
public partial class Item : UserControl
{
public Panel ItemPanel { set; get; }
public CheckBox DoneCheckBox { set; get; }
public Button DeleteButton { set; get; }
public TextBox UserTextBox { set; get; }
public Item()
{
InitializeComponent();
ItemPanel = itemPanel;
DoneCheckBox = doneCheckBox;
DeleteButton = deleteButton;
UserTextBox = userTextBox;
}
}
}
ItemContainer.cs
List<Item> itemList = new List<Item>();
Item crtItem;
void AddItem()
{
if (inputTextBox.Text != "")
{
Item item = new Item();
itemList.Add(item);
crtItem = item;
item.Dock = DockStyle.Top;
item.UserTextBox.Text = inputTextBox.Text;
item.Enter += Item_Enter;
item.Leave += Item_Leave;
item.DoneCheckBox.CheckedChanged += DoneCheckBox_CheckedChanged;
item.DeleteButton.Click += DeleteButton_Click;
itemPanel.Controls.Add(item);
item.BringToFront();
inputTextBox.Text = "";
itemsLabel.Text = itemList.Count + " item(s)";
}
inputTextBox.Focus();
}
private void Item_Enter(object sender, EventArgs e)
{
crtItem.ItemPanel.BackColor = Color.FromArgb(200, 233, 253);
crtItem.UserTextBox.BackColor = Color.FromArgb(200, 233, 253);
}
private void Item_Leave(object sender, EventArgs e)
{
crtItem.ItemPanel.BackColor = Color.FromArgb(240, 240, 240);
crtItem.UserTextBox.BackColor = Color.FromArgb(240, 240, 240);
}
private void DoneCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (crtItem.DoneCheckBox.Checked)
{
crtItem.UserTextBox.ForeColor = Color.FromArgb(150, 150, 150);
crtItem.UserTextBox.Font = new Font("Segoe UI", 18F, FontStyle.Strikeout, GraphicsUnit.Point, 0);
}
else
{
crtItem.UserTextBox.ForeColor = Color.FromArgb(0, 0, 0);
crtItem.UserTextBox.Font = new Font("Segoe UI", 18F, FontStyle.Regular, GraphicsUnit.Point, 0);
}
}
private void DeleteButton_Click(object sender, EventArgs e)
{
itemList.Remove(crtItem);
itemPanel.Controls.Remove(crtItem);
crtItem.Dispose();
Invalidate();
}
The problem is with this crtItem (in your example: currentToDo ). How do I update its value when I click a random Item usercontrol? Here's the VC solution: download[^] Feel free to modify it if you want.
|
|
|
|
|
Your making good progress. My goal here is to provide just enough of a hint for you to finish the rest of this yourself, and, hopefully, increase your future skills. So:
alin1 wrote: How do I update its value when I click a random Item usercontrol? Hint : consider the Enter and Leave Events as shown in the code, and think about what is available in those Event Handlers when they are called as the user changes focus at run-time. What is the 'sender parameter ?
Whenever you are problem-solving with Controls and Events, put breakpoints in the Event Handlers so you can see ehen the Event is triggered, and examine the 'sender and the event arguments. If you make a habit of this, you will make quick progress.
Examine the effect of this code in the 'ctor in my example: this.ActiveControl = todo; What Events does that trigger, and how can you use them.
What other user actions could you use to keep track of the current 'Item ?
best, Bill
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
Meanwhile (before seeing your latest reply) I tried this and it worked:
private void Item_Enter(object sender, EventArgs e)
{
crtItem = (Item)sender;
crtItem.ItemPanel.BackColor = Color.FromArgb(200, 233, 253);
crtItem.UserTextBox.BackColor = Color.FromArgb(200, 233, 253);
}
private void Item_Leave(object sender, EventArgs e)
{
crtItem = (Item)sender;
crtItem.ItemPanel.BackColor = Color.FromArgb(240, 240, 240);
crtItem.UserTextBox.BackColor = Color.FromArgb(240, 240, 240);
}
Is it the correct approach or it's just a hack?
On a side note, I already implemented the filtering (All, Active, Done), the items count label and Clear done items.
modified 12-Feb-18 15:15pm.
|
|
|
|
|
You are rolling now
Think about what is always 'true when you/user Leave one 'Item, and then later Enter another, or the same, 'Item.
Are there any circumstances where you would want to keep track of not only the current active 'Item, but, the previous one ?
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
Quote: Are there any circumstances where you would want to keep track of not only the current active 'Item, but, the previous one ?
So far, not.
---
What I'm trying to do next is to trigger an autosave function in 2 scenarios: 5 seconds after the user last pressed a key in a TextBox from an Item and when the app is closed. Regarding autosave, would you recommend reading the TextBox + CheckBox properties of each Item or to store these in a separate data structure in ItemContainer and save it?
Thanks to your advice I improved the app a lot: download[^].
|
|
|
|
|
My preference is to not have an auto-save feature, but, to leave it up to the user when to save state, but, as you mention, saving state/data when the app exits can be important. I like the idea of always giving the user choices. What if I make a big mess, and I don't want to over-write the saved state ?
Of course, if your app has an undo/redo/history feature, that's another, more complex, story.
For saving/restoring UI state in WinForms: research 'Settings, 'Configuration techniques. For data, I suggest you implement a 'Data class and use 'DataContract and 'DataMember WCF facilities: lots of good examples of that here.
I recommend you don't post links to your code; in general, none of us here have time to read, and most of us are reluctant to download anything for security reasons.
keep going, Bill
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
hi
i want check my password is correct or not
if its ok
extract zip file
|
|
|
|
|
You don't "check" to see if the password is OK because 7Zip, or any other zip library, won't tell you. Either the password you attempt is good or it just doesn't work.
|
|
|
|
|
fyi: SevenZipSharp has a 'Check function for this.
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
|
|
|
|
|
Hmmm, learn something new every day.
|
|
|
|
|
SevenZipSharp is long out-of-date: I suggest you research other, newer, actively supported, compression libraries: [^]
But, if you must use it, Look at the SevenZipSharp source code, for the 'Check function, or look on the web for use examples.
And, look into .Net's cryptographic facilities, if you can separate out password stuff from compression stuff.
«... thank the gods that they have made you superior to those events which they have not placed within your own control, rendered you accountable for that only which is within you own control For what, then, have they made you responsible? For that which is alone in your own power—a right use of things as they appear.» Discourses of Epictetus Book I:12
modified 12-Feb-18 8:54am.
|
|
|
|
|