customizing treeview in winforms - c#

Is it possible to create a treeview in visual studio which resembles the following figure :
The ROOT , CHILD and Sub-Child , all three would be LinkLabels , and on clicking them a new Form would be opened.

You could also try embed WPF user control into WinForm. Customizing WinForms isn't an easy task. In WPF you can do it much easier.

You also can activate Hot tracking for the tree view and then handle the NodeMouseClick event.

This is not practical in Winforms, every Control has a native Windows window associated with it. A window is a very expensive operating system object, create more than 50 of them and your user interface will noticeably start to drag because of the amount of overhead involved in drawing the controls. You very quickly reach that practical upper limit by nesting controls like you are intending to do.
You can customize the appearance of a TreeView by using its DrawMode property and the DrawNode event. The MSDN library article for TreeView.DrawNode has a decent example. It is also a popular component type in 3rd party component vendor collections. They add lots of bells and whistles to their version.

So, people don't like doing it.
The answer, however, is Yes, you can.
TreeView treeView1;
void Initialize_It() {
treeView1 = new TreeView();
treeView1.AfterSelect += new TreeViewEventHandler(treeView1_AfterSelect);
TreeNode Root = treeView1.Nodes.Add("ROOT");
TreeNode Child = Root.Nodes.Add("CHILD");
TreeNode SubChild = Child.Nodes.Add("Sub-Child");
}
void treeView1_AfterSelect(object sender, TreeViewEventArgs e) {
const string FORMAT = "{0} Node Selected. Call your Windows Form from here.";
if (e.Node.Level == 0) {
MessageBox.Show(string.Format(FORMAT, e.Node.Text), e.Node.Text);
} else if (e.Node.Level == 1) {
MessageBox.Show(string.Format(FORMAT, e.Node.Text), e.Node.Text);
} else if (e.Node.Level == 2) {
MessageBox.Show(string.Format(FORMAT, e.Node.Text), e.Node.Text);
}
}

Related

preselect checkboxes in radtreeview silverlight)

I have a silverlight app where there is a telerik radtreeview with checkboxes. The user selects stuff and when the user wants to edit it's selection i need to pre-populate the tree with the previously saved selection.
I found out that I can bind the checkboxes to my viewmodel. But if I choose that scenario I don't use the "built in" checkboxes and lose the tristate logic (autoselecting siblings when selecting a parent and such)
So I am experimenting with trying to get the radtreeviewitem objects from the radtreeview.items collection
http://www.telerik.com/help/silverlight/radtreeview-how-to-iterate-through-treeviewitems.html
The problem is that the radtreeviewitems are only generated when a node is expanded by a user in the ui. So not all items I want to iterate through are present after the control is databound.
I have not found a good way to force the ui to build all the radtreeviewitems so I can iterate through them and set my preselection. I found the links below but it only seems to work with the root node, not the siblings.
WPF: control.ItemContainerGenerator.Status is NotStarted. How do I tell it to start now?
Would you guys also consider rebuilding the "tristate-mode" into your viewmodel logic "dirty"?
How would you go about preselecting checkboxitems in the radtreeview?
This is how I do it :
public static void CheckAllTreeItemsAuto(RadTreeView tree)
{
tree.ItemContainerGenerator.StatusChanged += (s, e) =>
{
if ((s as Telerik.Windows.Controls.ItemContainerGenerator).Status == Telerik.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
RadTreeViewItem item = (RadTreeViewItem)tree.ItemContainerGenerator.ContainerFromIndex(0);
while (item != null)
{
item.IsChecked = true;
item = item.NextItem;
}
}
};
}
I didn't experience your problem with the items not generated at the start. (I don't know how you generate your RadTreeView).
When working with the RadTreeView control you need to have in mind that the built-in tri-state logic is designed to work with declaratively defined control and items, only. This means that using this feature in MVVM scenarios will not work as expected.
Since Telerik is aware of that limitation they provided the community with an article demonstrating how developers can use the tri-state logic of a native CheckBox control in MVVM scenarios. You can find the article in their documentation. Also, at the end of the article you can find a link leading to their CodeLibrary where you can download ready to run project demonstrating the described approach.
I hope this information will help you.

Add overlay to Window at runtime

I'm writing a simple "tutorial" library that will allow developers to easily add step-by-step tutorials to their existing WPF applications. The tutorials will help first time users of the application find their way around by adding an overlay that highlights a control and explains its purpose. The end result will look something like this:
The regular application:
The overlay explaining the purpose of a control:
My question is this: What's the most reliable and unobtrusive way to inject the overlay view into the current window? The best I've come up with so far is to require the developer to add an attached property to whatever window will be hosting the overlay, and then add the necessary elements on the window's Initialized callback:
public static void IsTutorialOverlayCompatibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((Boolean)e.NewValue == true)
{
if (sender as Window != null)
{
Window window = (Window)sender;
window.Loaded += new RoutedEventHandler((o, eargs) =>
{
Grid newRootElement = new Grid();
newRootElement.Name = "HelpOverlayRoot";
if (window.Content as UIElement != null)
{
UIElement currentContent = (UIElement)window.Content;
window.Content = null;
newRootElement.Children.Add(currentContent);
newRootElement.Children.Add(new HelpOverlayControl());
window.Content = newRootElement;
}
});
}
}
}
This feels like a hack, however, and I'm not sure that there isn't some edge case where this method will break the layout of the application. In addition, it requires that the window's Content property be an instance of type UIElement.
I'd like to avoid forcing developers to change their XAML (i.e, adding a custom overlay UserControl to every window) in order to use my library. What's the best way to add this kind of functionality to an existing WPF application?

Get all controls of the current form at design-time

I have a question about design-time things:
I've made a component with an property "Links".
Those links are Controls. Now I want to make a UI-Dialog (for editing this property in the property grid).
How can I get all controls of the current form? I think the component has an connection to it, but where? I can't find anything.
Thanks :)
To get all of the controls of the current form then use the following code to get a collection of all of the controls on that form:
MyForm.Controls
See this MSDN help
Edit:
Perhaps these will help?
Design-time editor support for controls collection
http://social.msdn.microsoft.com/Forums/en-US/winformsdesigner/thread/64df27e7-8502-42ac-8634-cf8a8937d922/
Adding design-time support for a nested container in a custom/usercontrol (Winforms)
This is quite untrivial to do, I don't know of any examples of .NET components that do this. You can get to the form at design time with the Site property but there are problems. What's hard to deal with is the user deleting controls, ones that you have already added to your controls collection. I don't know of any good trigger to keep your collection valid, beyond also having to use a custom designer for the form or user control.
There's a better mousetrap for this, you see it being used by the HelpProvider and ErrorProvider components for example. Note how they add properties to all other controls on the form. This is done by implementing the IExtenderProvider interface. There's an excellent example of this in the MSDN library article.
You can get IDesignerHost service at design-time. This service has a property called Container which has Components. Then for each component, get INestedContainer service and then get all components from that service.
This is how Document Outline window works. I've changed their method to use List<IComponent> as return value:
List<IComponent> GetSelectableComponents(IDesignerHost host)
{
var components = host.Container.Components;
var list = new List<IComponent>();
foreach (IComponent c in components)
list.Add(c);
for (var i = 0; i < list.Count; ++i)
{
var component1 = list[i];
if (component1.Site != null)
{
var service = (INestedContainer)component1.Site.GetService(
typeof(INestedContainer));
if (service != null && service.Components.Count > 0)
{
foreach (IComponent component2 in service.Components)
{
if (!list.Contains(component2))
list.Add(component2);
}
}
}
}
return list;
}
To filter the result to contain just controls, you can call result.TypeOf<Control>().
Not sure if this is what you want.
I "lost" a label control by accidentally removing it's text property.
After looking here at this discussion I finally realized that by accessing ANY control property at design time I could use the drop - down at the top of the properties window to locate the control name. Selecting the name revealed the location of the control on the form and exposed it's properties in the properties editor.

.NET: is there a Click-and-drag "Desktop-Like" control?

OK, first for context look at the Windows desktop; You can take items (folders, files) on the desktop and drag them around to different places and they "stay" where you dragged them. This seems to be a pretty useful feature to offer users so as to allow them to create their own "groupings" of items.
My question is thus:
Is there a control in .NET that approximates this behavior with a collection of items?
I'm thinking something like a listview in "LargeIcon" mode, but it allows you to drag the icons around to different places inside the control.
You can do this with a standard ListView control by implementing drag-and-drop. Here's a sample control that does this:
using System;
using System.Drawing;
using System.Windows.Forms;
public class MyListView : ListView {
private Point mItemStartPos;
private Point mMouseStartPos;
public MyListView() {
this.AllowDrop = true;
this.View = View.LargeIcon;
this.AutoArrange = false;
this.DoubleBuffered = true;
}
protected override void OnDragEnter(DragEventArgs e) {
if (e.Data.GetData(typeof(ListViewItem)) != null) e.Effect = DragDropEffects.Move;
}
protected override void OnItemDrag(ItemDragEventArgs e) {
// Start dragging
ListViewItem item = e.Item as ListViewItem;
mItemStartPos = item.Position;
mMouseStartPos = Control.MousePosition;
this.DoDragDrop(item, DragDropEffects.Move);
}
protected override void OnDragOver(DragEventArgs e) {
// Move icon
ListViewItem item = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
if (item != null) {
Point mousePos = Control.MousePosition;
item.Position = new Point(mItemStartPos.X + mousePos.X - mMouseStartPos.X,
mItemStartPos.Y + mousePos.Y - mMouseStartPos.Y);
}
}
}
I think the closest would the ListView control, but even that is more like an explorer window. You might be able to create your own view that does what you want, but you'd need to manually persist icon locations somewhere.
If you are not opposed to using WPF, Josh Smith has created a pretty neat canvas that I am currently using for a project. It allows you to add controls and drag them around the canvas. You would have to handle what is loaded on the canvas and where on the next load of the program, but that is pretty simple.
http://www.codeproject.com/KB/WPF/DraggingElementsInCanvas.aspx
This depends on whether this is a windows application or a web browser based application. In either case you need to have some sort of container to manage the locations of controls. You can manage the position of controls inside of a container with their X and Y coordinates.
You would handle the actual movement using the drag events. So you have drag start, while dragging (you might show a place holder graphic or change the cursor), and finally a drag end (set the control's x and y to the new position). Obviously these aren't the actual event names, but a search for "how to handle drag events" should get you started.
In a web environment, I know jquery has dragging capability built in. So you might want to look at that. The one big thing you'll have to be careful of is maintaining the positions of your controls between postbacks. I'm not sure what would happen in this case.
Windows uses ListView32, an internal control with drag n' drop placeholder features, custom borders...
The icon location can be stored in a XML file, or in the application settings (by putting the XML as string and converting it to file when needed).
You can do, for example:
<icons>
<icon1>
<name>Icon1</name>
<text>My PC</text>
<imageIndex>16</imageIndex>
</icon1>
<icon2>
.....
</icon2>
.....
</icons>
Lorenzo

How to implement the Edit -> Copy menu in c#/.net

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.

Categories

Resources