I have a button in my user control which should be used to remove the user control from its parent container. This is the way I have coded it today.
private void RemoveRoleButton_Click(object sender, RoutedEventArgs e)
{
if (ConfirmRoleRemoval())
{
Panel parentPanel = (Panel)this.Parent;
parentPanel.Children.Remove(this);
}
}
private bool ConfirmRoleRemoval()
{
return MessageBox.Show("Are you sure [...]
}
Is it normal to do it this way in WPF?
Yes, it looks fine to me. As Mike Hillberg writes in his blog:
An element doesn’t actually pick its logical parent; instead, a parent “adopts” children.
Thus, it makes sense that "removing" a child is also done through the object model of the parent.
As a side note: You might want to consider throwing a "nice" exception (or even disabling the button) when the parent is not a Panel (rather than waiting for the InvalidCastException).
Related
I'd like to programmatically emulate a click on a node in a TreeView control. There's no clickable method as far I can see (something corresponding to other controls) and I guess that I need to go for the currently selected node.
So I've tried the following:
christmasTreeView.SelectedNode. ???
However, intellisense gave me no hint on what to call in order to fire a clickety-click on the node. How can it be done?
You can do something like:
// find the node you want to select and make it the SelectedNode
christmasTreeView.SelectedNode = christmasTreeView.Nodes[1]; // <-- the index you need
// Now trigger a select
christmasTreeView.Select();
// or
//christmasTreeView.Focus();
This will fire:
private void christmasTreeView_AfterSelect(object sender, TreeViewEventArgs e) {
// awesome
}
Possible approach (not very smooth, though).
TreeNode preSelected = ChristmasTreeView.SelectedNode;
ChristmasTreeView.SelectedNode = null;
ChristmasTreeView.SelectedNode = preSelected;
ChristmasTreeView.Select();
Your main issue is that a Windows Forms TreeNode does not derive from a Control like a TreeView does (or, for example, a Button). It's much closer to a "model" class, meaning that it's primarily concerned with the hierarchical organization of your data. Although some of the presentational abstraction is leaked in properties like Color, Bounds, Handle and similar, a TreeNode doesn't know how to paint itself, nor how to handle click events.
On the other hand, a TreeView is an actual Control, meaning you can derive from it and be able to override its protected OnClick method, like shown in the example you linked.
If you want to follow that path, you could create your derived TreeView class from it and override the protected OnNodeMouseClick method. This method is specific to the TreeView and called by its WndProc method when a certain node is clicked.
But having read your comments to other answers, it seems that this is not what you really need to do to accomplish your goal.
You need to use event handler for TreeView.NodeMouseClick.
This Event got parameter which You can call in Your EventHandler like below:
void MyTreeview_NodeMouseClick(object sender,
TreeNodeMouseClickEventArgs e)
{
// do something with e.Node
}
I am using c#.
Why does the CausesValidation property does not propagate from containers to child controls?
I have a TabControl and i need to perform validation of user input on all tabs.
But each tab is independent. I found out that if i have a container like a TableLayoutPanel and i set CausesValidation to false, the child componentes still perform validation.
So this code would not work:
Foreach(Control c in Container.Controls)
{
c.CausesValidation = False;
}
If you do some DEBUG output you will see that the only found control is the TableLayoutPanel or any other container like a GroupBox that will be found and set to False. But containers are not propagating that value to the child level.
A lot of people asked how we could solve the problem. I found a lot of methods, but i have created a static class that allows me to select wich tab on the TabControl that i want to perform validation, and it will set CausesValidation to False on ALL controls of that TabControl, including child controls with a deepness of 10 layers. If you want that library just ask for it!
My real question is, should not a container propagate that property to its child controls, and that child controls to any child controls?!
It would save us a lot of work from creating a very crazy code that is very specific for something that should work from scratch? Why is this not implied?
This just isn't a constructive way to deal with your requirement. The feature just wasn't implemented the way you like it to work and that's never going to change. It isn't a problem, you can easily add it yourself with a wee bit of code:
public static void SetCausesValidation(Control.ControlCollection ctls, bool enable) {
foreach (Control ctl in ctls) {
ctl.CausesValidation = enable;
SetCausesValidation(ctl.Controls, enable);
}
}
And use it in your form constructor, something like:
public Form1() {
InitializeComponent();
SetCausesValidation(panel1.Controls, false);
}
Note the use of recursion in the method to set the property through the entire tree of controls inside the container.
I've looked and looked and can't find an answer.
I have a TreeView. It has Drag and Drop to allow moving of Nodes within the tree.
I want to limit the drag and drop to only work within that one control, within a single instance of the application (the application itself can run more than one instance).
I've tried the following:
private void SubFolderTreeView_DragEnter(object sender, DragEventArgs e)
{
TreeView source = sender as TreeView; // also tried = (TreeView) sender;
if (source == this.SubFolderTreeView && e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false))
e.Effect = DragDropEffects.Move; // Okay, set the visual effect
else
e.Effect = DragDropEffects.None; // Unknown data, ignore it
}
Unfortunately, a second instance of the same application will still be able to drag from its TreeView to the first TreeView: (source == this.SubFolderTreeView) is true
I have not tested if a totally different treeview could drag to mine, though I doubt it, but the above behaviour is already a fail.
I tried some other things - comparing the form or the control's handle also didn't work
bool isSameForm = ((MyForm) source.TopLevelControl == this); // still true
bool isSameHandle = (((Control)source).Handle == ((Control)this.SubFolderTreeView).Handle); // still true
The only other things I can think of, off the top of my head, is a random number stored in the TreeView or Form (probably won't work), and checking the absolute screen position of the control (not the best method).
I could of course stick a mutex in the application and so only allow one instance to run, but I'd rather not.
Can anyone suggest a good way of doing this?
To flesh out Hans Passant's solution (which worked perfectly, thanks Hans) for future reference and other searchers into this problem, I used the code:
// prevents dragging from other instances of this form - thanks to Hans Passant
private bool DragDropFromThisForm = false;
private void SubFolderTreeView_ItemDrag(object sender, ItemDragEventArgs e)
{
// Initiate drag/drop
DragDropFromThisForm = true;
DoDragDrop(e.Item, DragDropEffects.Move);
DragDropFromThisForm = false;
}
private void SubFolderTreeView_DragEnter(object sender, DragEventArgs e)
{
MyForm form = (MyForm) (sender as TreeView).TopLevelControl;
if (form.DragDropFromThisForm && e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false))
e.Effect = DragDropEffects.Move; // Okay, set the visual effect
else
e.Effect = DragDropEffects.None; // Unknown data, ignore it
}
It may well be that DJ Kraze's answer would also work, and perhaps be a tad more elegant, but Hans' solution is lightweight and effective.
I'm not really following the restrictions, seems like flawed logic with the information you've given (all identical instances, but only one can have drag and drop - what??), but some suggestions:
Have a property that determines whether nodes can 'drag and drop' and only set it in the one instance.
Only subscribe to the event on the one instance that you want to be able to 'drag and drop' on.
Create a separate TreeView class that supports dragging and dropping, and instantiate the base TreeView everywhere else.
I am embedding usercontrols in a panel and using DevExpress Navigator control to navigate from one to the other. What I am concered about is any implications to this method?
I would give examples of what I am concerned about but then I wouldn't need to ask this question...
I have a primary form, ShellForm that has a docked Navigator Control on the left and a docked Panel Control for the rest. I then dock a User Control, say ucSearchPage, in the Panel when the link is clicked.
public partial class ShellForm : XtraForm
{
private ucSearch searchPage = new ucSearch();
private ucEnrollments enrollmentPage = new ucEnrollments();
private ucGeneral generalInfoPage = new ucGeneral();
private ucContacts contactPage = new ucContacts();
public ShellForm()
{
InitializeComponent();
}
private void ShellForm_Load(object sender, EventArgs e)
{
this.pnlShellHost.DockControl(this.searchPage);
}
private void navSearch_LinkClicked(object sender, DevExpress.XtraNavBar.NavBarLinkEventArgs e)
{
this.pnlShellHost.DockControl(this.searchPage);
}
private void navEnrollment_LinkClicked(object sender, DevExpress.XtraNavBar.NavBarLinkEventArgs e)
{
this.pnlShellHost.DockControl(this.enrollmentPage);
}
The code for DockControl() is as follows -->
public static void DockControl(this Control control, UserControl userControl)
{
userControl.Dock = DockStyle.Fill;
control.Controls.Clear();
control.Controls.Add(userControl);
}
Are there any implications to this approach? Is it just plan stupid?
I am one of those programmers that had to learn to run before walking so I have a tendency to fall flat on my face!
There will be about 30 User Controls in all.
Any insight is welcomed and appreciated!
IMO it is not a bad idea at all to embed user controls. In fact, that is exactly what they were meant for. Because every control inherits from the same base class you can build a tree structure of controls using the Composite pattern. This will allow you to create just about anything you would like.
If you think of a basic web page, this is actually what you are doing anyways: placing one element in another, or embedding them. You can have multiple divs in other divs etc. This is essentially what you are doing when you embed user controls as the user controls render to basic HTML.
Hope this helps.
EDIT: To address the concerns in your comment... I don't think you will have a problem from the data entry standpoint. The reason why is because you are using different user controls for your enrollment control and search control. I'm assuming you are overriding the OnLoad event in each of those user controls right? What happens on post back is that the Search's OnLoad will be hit if the search control was loaded, while the enrollment's OnLoad will be hit if that was loaded.
Because of the polymorphism of the user controls, you can handle the data for those controls separately.
How do I implement a Copy menu item in a Windows application written in C#/.NET 2.0?
I want to let the user to mark some text in a control and then select the Copy menu item from an Edit menu in the menubar of the application and then do a Paste in for example Excel.
What makes my head spin is how to first determine which child form is active and then how to find the control that contains the marked text that should be copied to the clipboard.
Help, please.
With the aid of some heavy pair programming a colleague of mine and I came up with this, feel free to refactor.
The code is placed in the main form. The copyToolStripMenuItem_Click method handles the Click event on the Copy menu item in the Edit menu.
/// <summary>
/// Recursively traverse a tree of controls to find the control that has focus, if any
/// </summary>
/// <param name="c">The control to search, might be a control container</param>
/// <returns>The control that either has focus or contains the control that has focus</returns>
private Control FindFocus(Control c)
{
foreach (Control k in c.Controls)
{
if (k.Focused)
{
return k;
}
else if (k.ContainsFocus)
{
return FindFocus(k);
}
}
return null;
}
private void copyToolStripMenuItem_Click(object sender, EventArgs e)
{
Form f = this.ActiveMdiChild;
// Find the control that has focus
Control focusedControl = FindFocus(f.ActiveControl);
// See if focusedControl is of a type that can select text/data
if (focusedControl is TextBox)
{
TextBox tb = focusedControl as TextBox;
Clipboard.SetDataObject(tb.SelectedText);
}
else if (focusedControl is DataGridView)
{
DataGridView dgv = focusedControl as DataGridView;
Clipboard.SetDataObject(dgv.GetClipboardContent());
}
else if (...more?...)
{
}
}
Why not extending the control, so the control itself provides the data which should be copied into the clipboard.
Take a look at ApplicationCommands documentation.
To determine which window is open, you can query the Form.ActiveMDIChild property to get a reference to the currently active window. From there, you can do one of two things:
1) If you create your own custom Form class (FormFoo for example) that has a new public member function GetCopiedData(), then inherit all of your application's child forms from that class, you can just do something like this:
((FormFoo)this.ActiveMDIChild).GetCopiedData();
Assuming the GetCopiedData function will have the form-specific implementation to detect what text should be copied to the clipboard.
or
2) You can use inheritance to detect the type of form that is active, and then do something to get the copied data depending on the type of form:
Form f = this.ActiveMDIChild;
if(f is FormGrid)
{
((FormGrid)f).GetGridCopiedData();
} else if(f is FormText) {
((FormText)f).GetTextCopiedData();
}
etc.
That should get you started with finding the active window and how to implement a copy function. If you need more help copying out of a GridView, it may be best to post another question.
If the form is tabbed and the target control is a DataGridView, it's sometimes possible for the Form's TabControl to be returned as the active control, using the above method, when the DataGridView is right clicked upon.
I got around this by implementing the following handler for my DataGridView:-
private void dataGridView_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
dataGridView.Focus();
dataGridView.CurrentCell = dataGridView[e.ColumnIndex, e.RowIndex];
}
}
It seems to me that you might be better off breaking this into smaller tasks/questions.
You have a few issues you are stuck on from the way it sounds.
You have multiple 'child' windows open. Is this an MDI application?
When an action is performed on one of those child windows, it should fire an event in that window's event handlers. That is your first thing to set up. If this is a datagridview I would suggest a simple test to start. Try trapping the DataGridView.SelectionChanged event. Just throw in something like MessageBox.Show("I copied your datas!"); for now.
This should get you started where you will at least understand how this event will be raised to you.
From here, we will need to know a little more about your datagrid, and the rows and child controls in those rows. Then we can likely create events in the render events that will be raised at the appropriate times, with the appropriate scope.