C# WPF - Custom Control drag and drop (Visual Studio style) - c#

How do I make my Custom controls draggable and droppable on a grid let's say?
I want to drag a panel(custom control) and drop it somewhere on my screen, in the best case in a grid, for example how it's done in Visual Studio, you can grab the solution explorer let's say and drop it somewhere, but how exactly do I do that?

You need to build your project and then it will be automatically available in the Toolbox when you are in the XAML designer. Just like the common controls.
For Drag and Drop at runtime look and the official WPF documentation. Also I suggest you look at the GongSolutions.WPF.DragDrop library in GitHub it is open source so you can see how they implemented it if the functionality it provides does not do what you want.

I found this post with a very similar conclusion to this one. the difference being scale.
the following example will swap the parent container
int i = 0;
void swapLocations()
{
foreach(var formObject in objList) //objList == a list or array on all objects you want to move from one container to another
{
if (i % 2 == 0)
{
// catch current position
Point moveLocation = new Point(formObject.Location.X + formObject.Parent.Location.X,formObject.Location.Y + formObject.Parent.Location.Y);
// remove this object
formObject.Parent.Controls.Remove(formObject);
// add this object to the form
this.Controls.Add(formObject);
// set location
formObject.Location = moveLocation;
formObject.SendToBack();
}
else
{
formObject.BringToFront();
}
}
++i;
}

Related

Unable to dispose and remove controls from control list

thanks for reading.
I have a C#.NET form with buttons that switch controls in a main panel. I didn't have any issues until I upgraded to Visual Studio 2012 and Advanced Installer. Target Framework is 4.0, not 4.5.
When I change controls, I dispose and remove the previous before adding the new one, but I'm getting an error when there aren't any controls yet (ie, when the first one loads).
The original loop crashed with something about iterating while modifying the collection, so now I'm trying to just remove one control after ensuring it's there.
This errors with: Index 0 is out of range.
This all works fine on the dev machine, and it wasn't an issue using the old built-in VS installer.
Any ideas? 4.0 framework issue? Missing reference not being deployed?
Thanks!
panelMain.SuspendLayout();
int control_count = panelMain.Controls.Count;
if (control_count > 1) {
Log.Write("More than one control found in main panel.", ErrorLevel.Error);
}
if (control_count > 0) {
Control current_ctrl = panelMain.Controls[0];
current_ctrl.Dispose();
panelMain.Controls.Remove(current_ctrl);
}
//foreach (Control ctrl in panelMain.Controls) {
// ctrl.Dispose();
// panelMain.Controls.Remove(ctrl);
//}
The issue with the foreach loop that you've commented out is that you cannot add items to or remove items from a collection that you are currently enumerating. That means that if you want to loop through a collection and remove items then you must use a for loop. If you want to remove multiple items then you must loop backwards.
The issue with the second if statement is that disposing a control automatically removes it from its parent's Controls collection. That means that, as soon as you call Dispose on the control, there is no longer an item in the Controls collection so the Remove call fails.
So, the moral of the story is that you should use a for loop, loop backwards and use just Dispose to destroy and remove.
Here is a simple recursive method to dispose of controls if anyone is interested. Using the advice of jmcilhinney above.
NOTE: make sure you read all comments about Visible property and setting it back to true.
// Start by calling a parent control containing the controls you want to
// destroy such as a form, groupbox or panel
private void DisposeControls(Control ctrl)
{
// Make control disappear first to avoid seeing each child control
// disappear. This is optional (if you use - make sure you set parent
// control's Visible property back to true if you create controls
// again)
ctrl.Visible = false;
for (int i = ctrl.Controls.Count - 1; i >= 0; i--)
{
Control innerCtrl = ctrl.Controls[i];
if (innerCtrl.Controls.Count > 0)
{
// Recurse to drill down through all controls
this.DisposeControls(innerCtrl);
}
innerCtrl.Dispose();
}
}

How to create new components in Visual Studio 2010

Can you tell me how to create new visual component for .net 4.0 in Visual Studio 2010,
i have a book about component creation in Russian Language (Pavel Agurov -Razrabotka Komponentov v MS Visual Studio 2005/2008), but methods in this book covers VS 2005 and 2008. And methods gives some errors in VS 2010.
EDIT:
There is my code
public partial class exComboBox : ComboBox
{
/*public exComboBox()
{
InitializeComponent();
}*/
private System.Collections.Specialized.StringCollection _itemValues = new System.Collections.Specialized.StringCollection();
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Editor("System.Windows.Forms.Design.StringCollectionEditor,System.Editor","System.Drawing.Design.UITypeEditor,System.Drawing")]
public System.Collections.Specialized.StringCollection ItemValues
{
get
{
return _itemValues;
}
set
{
_itemValues = value;
}
}
public object SelectedItemValue
{
get
{
return _itemValues[SelectedIndex];
}
}
}
And when I try to add this component to new form and add values for ItemValues it says
Constructor on type 'System.String' not found.
Right click your project, choose to add a new item. Go to the C# section and select User Control.
This will create a windows control that will appear at the top of the Toolbox when you use the forms designer. Just drag and drop it into your form like any other control.
If you wish to derive it from Combo Box, then just edit the class that is created and change it's base class from a UserControl to a combo box. You might also need to change your constructor top call three base class constructor rather than doing InitialiseComponent for itself.
If you want to use this control in many applications, then you can put the code into a Class Library project, which will make a .dll assembly that other projects can reference to gain access to the control.
Create a Windows Application and then go to Add New Item menu by right clicking on Project in Solution Explorer and you can see there the Component AFAIR.
Assuming you're talking about Winforms...
Deriving from existing controls is probably not something you'll want to do. Especially if you want to change their default behavior or replace their existing properties. Sooner or later you will be facing severe limitations. Believe me, I've been there.
If you want an easy way to repeat some kind of pattern for a ComboBox, I suggest you take a look at IExtenderProvider interface. You can implement it on a Component-derived class that you can drag on the design surface. In this component, you can declare properties that will be attached as extra properties to any comboboxes (or anything else, it's all up to you).
Since the component will know about whatever is attached to it, it will be able to declare event handlers over any attached control / component. For example, if you want your combobox to have items already present, you could handle it's Load event and add the items there.
To learn more about IExtenderProvider, go here.

How do I change a display using TreeView?

I'm trying to change a panel to a specific form (as this is the only way I can fathom it) based on the selected TreeView node. For example, in Visual Studio, if you right-click on "Solution 'solutionname' (1 Project)", click 'Properties', it comes up with a tree list on the left side. When you click on a selection, the right pane changes.
I've searched continuously for hours the previous few days and only found a tutorial showing how it can affect a webBrowser control.
This is a farfetched example that I can understand:
private void tree_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
treeNode nName = e.Node;
//For testing:
string pg = "";
pg = nName.Tag;
if (pg == "Form2") display = Form2;
}
Display is a panel. I know this is absolutely wrong, but I couldn't find any proper method using my search terms.
You will need to set Visible on all your panels to false, except for the one you want to display which will be set to true.
WinForms does not have any particularly nice way of setting this up. You could set the Tag property of each node to be a reference to the panel (this has to be done programmatically - the designer won't let you do it), then iterate through the entire tree view to set ((Panel)node.Tag).Visible = false followed by ((Panel)e.Node.Tag).Visible = true or you can maintain the list separately. If you don't have many panels, a switch/if-else block may be okay, too.

What is the standard way to add controls to a TabPage at Design Time?

I am creating my first Windows Forms application, to be deployed on Windows Mobile and I am having some trouble designing a Tabbed Interface.
I had assumed that I could Create a TabControl, then Add some TabPages and then drag Controls on to each Tab Page in turn. This does not appear to be possible and most of the information I see on the web seems to suggest that the controls should be added dynamically at run-time.
Am I missing something obvious here or is this indeed correct?
If you do have to add the controls at runtime then how do people generally manage the design process. Do they create a Custom UserControl for each tab and then add that at runtime?
Design environment (C# Visual Studio 2005, .net 2.0)
Runtime environment (Windows Mobile 6.1)
Update 1
The actual steps taken within visual studio were as follows :-
Select New Project -> SmartDevice -> Windows Mobile 6 Professional -> Device Application
Added a TabControl to Form1. This automatically adds tabPage1 and tabPage2
Update 2
The solution to this is embarrassingly noobish. The TabControl puts the tabs at the bottom of the page, the first thing I was doing was resizing the tab control to a single line which was then hiding the TabPage control.
Currently i don't use Windows Mobile, but i think it works quite the same.
After adding a TabControl to your form you should take a look into the properties and search for TabPages. Here you can add and delete new TabPages to your Control and design it as you like in the designer.
To your question about using UserControls on each TabPage i would definitely say Yes. It makes easier to separate between each page and what will happen on each one.
Also at a last step i am going to move the needed code out of the Designer.cs into my own function (e.g. var tabControl = CreateTabControl() where all of my properties are set. Then i put all my UserControls into an
private IEnumerable<Type> GetAllTypes()
{
yield return typeof(MyFirstControl);
yield return typeof(MySecondControl);
}
and make an
private void CreateTabPages(TabControl tabControl, IEnumerable<Type> types)
{
foreach(var type in types)
{
var control = Activator.CreateInstance(type);
var tabPage = new TabPage();
tabPage.Controls.Add(control);
tabControl.TabPages.Add(tabPage);
}
}
this will then be called by
CreateTabPages(tabControl, GetAllTypes());
With this approach i can easily add another Tab Page with a single line of code and design it in its own scope.
I just opened vs2008 and created a tabcontrol, then I added controls inside using drag and drop in the designer and I didn't found any problem.
The way I use to do it is to create a usercontrol for each tab, But I add the usercontrol to the tab in the designer. (note that the usercontrol will not appear in the toolbox until you generate your solution).
I didn't know why your method are not working. Did you stop your application before try to add the controls?
Good Luck.

Design-Time drag and drop

I'm currently working of a project where my part is to design a Design Surface where the end user can add/remove/move controls at run-time.
I followed this tutorial "Hosting Windows Forms Designers, by Tim Dawson", and almost have implemented all the features I need.
Short story for those who don't want to read the tutorial : I implemented IDesignerHost, IContainer, ISelectionService, IToolboxService and some other interface, to create my design surface. I didn't use the System.ComponentModel.Design.DesignSurface already in the framework, mainly beacause I need a really custom design surface.
Question :
I want to allow user to drag & drop new Control from the IToolboxService to the IDesignerHost/IContainer. In this tutorial, you clic on a Control in the toolbox, the click on the design surface to add the control.
What i've found :
There is a built-in feature that
automagically does drag & drop from
IToolboxService to
System.ComponentModel.Design.DesignSurface but it is clearly not working if you implement IDesignerHost from nothing.
When you use the
Control.DoDragDrop(ToolboxItem)
method, to initiate a drag & drop :
IToolboxService.SerializeToolboxItem(ToolboxItem)
is called to serialize the item
IToolboxService.IsToolboxItem(object)
and
IToolboxService.IsSupported(object)
are called to evaluate if the
serialized ToolboxItem can be allowed
to be droped on the design surface
When you drop the control : IToolboxService.DeserializeToolboxItem(object serializedObject) is called by the design surface to deserialize the controldropped.
IToolboxService.SetCursor() is called to know if you define a custom cursor, or let the standard windows cursor.
Problem :
I implemented all mentionned above, in the "What i've found", but the drag & drop is buggy :
I drag, everything's fine, but when
hovering the design surface, my
cursor blink a little between
standard cursor and the
"DragDropEffects.Copy" style.
When I drop, nothing happens, and when my cursor leave the design surface (after I dropped), then the new Control is created and added where I dropped it.
Has anyone ever tried to do what I'm doing, and if so, how did you manage it ?
Is there anyone that has any pointer/link/good advices ?
Thank you =)
Is there anyone that has any
pointer/link/good advices?
Your issue seems to be addressed by the Microsoft Connect issue Mouse Events Consumed / Not Raised in 2.0 Designers. This issue is all but easy to grasp though, but apparently the upgrade from .NET Framework 1.1 (2003) to 2.0 (2006) implied a change in drag & drop behavior - given that the article you mentioned stems from 2003 this could well be related. Specifically you should scroll ahead to section Issue 2, citation:
You will now notice constant
flickering when dragging over the
form. This is partially caused by the
new drag-drop behavior in 2.0.
I'm unable to dig deeper into this right now myself but you might also be able to figure something from Customize Drag & Drop in the Designer, which addresses changed behavior regarding drag & drop between Visual Studio versions 2003 and 2005; quite likely this might be related and one of the accepted answers does in fact specifically reference the Microsoft Connect issue above, plus it offers more hints towards possible solutions too.
Me to worked on this questions for one day ago. This is my solution that found for my project like yours.
I drag, everything's fine, but when hovering the design surface, my cursor blink a little between standard cursor and the "DragDropEffects.Copy" style.
This is my toolboxservice.
class EasyAccordionToolboxService : AccordionControl, IToolboxService
{
EasyAccordionControlElement _SelectedElement;
public Control DesignPanel { get; set; }
public EasyAccordionToolboxService()
{
AddAccordions();
AllowItemSelection = true;
base.ElementClick += EasyAccordionToolboxService_ElementClick;
}
private void EasyAccordionToolboxService_ElementClick(object sender, ElementClickEventArgs e)
{
if (e.Element.Level == 1) this._SelectedElement = e.Element as EasyAccordionControlElement;
}
public ToolboxItem GetSelectedToolboxItem()
{
if(_SelectedElement != null)
{
var selectedToolboxItem = this._SelectedElement.ToolBoxItem;
this._SelectedElement = null;
return selectedToolboxItem;
}
else
{
return null;
}
}
public bool SetCursor()
{
if(this._SelectedElement == null)
{
this.DesignPanel.Cursor = Cursors.Default;
}
else
{
this.DesignPanel.Cursor = Cursors.Hand;
}
return true;
}
There are "DesignPanel" property and "SetCursor" methods. DesignPanel property is control referred to view of root designer.
When I drop, nothing happens, and when my cursor leave the design surface (after I dropped), then the new Control is created and added where I dropped it.
There is GetSelectedToolboxItem method in EasyAccordionToolBoxService implemented from IToolboxService. GetSelectedToolboxItem is your answer.
Finally,
designer = new EasyDesignSurface(propertyGrid);
var rootForm = (Form)designer.CreateComponent(typeof(Form));
rootForm.Name = "rootForm";
rootForm.Text = "BELGE";
rootForm.TopLevel = false;
rootForm.Size = new Size(600, 600);
designer.ComponentContainer.Add(rootForm);
var view = (Control)designer.View;
view.Dock = DockStyle.Fill;
designPanel.Controls.Add(view);
easyAccordionToolboxService1.DesignPanel = view;
designer.AddService(typeof(IToolboxService), easyAccordionToolboxService1);

Categories