C# TabControl, How create custom TabPages collection editor? - c#

I have customized a TabPage, but found that there are problems as follows:
First, I Create two custom TabPage, It worked.
But there was a problem when I closed the document and then I reopened the document:
enter image description here
Look, TabPage becomes four, but I found the problem in the "designer.cs" document.
//
// blK_TabControl1
//
this.blK_TabControl1.Controls.Add(this.blK_TabPage6);
this.blK_TabControl1.Controls.Add(this.blK_TabPage7);
this.blK_TabControl1.Location = new System.Drawing.Point(4, 12);
this.blK_TabControl1.Name = "blK_TabControl1";
this.blK_TabControl1.SelectedIndex = 0;
this.blK_TabControl1.Size = new System.Drawing.Size(604, 196);
this.blK_TabControl1.TabIndex = 14;
this.blK_TabControl1.TabPages.AddRange(new System.Windows.Forms.TabPage[] {
this.blK_TabPage6,
this.blK_TabPage7});
After the normal TabControl add TabPage, there is no "TabPages.AddRange()" this code, how can I fix it?
Here is my code:
public class BLK_TabPageCollectionEditor : CollectionEditor
{
public BLK_TabPageCollectionEditor(Type type)
: base(type)
{
}
protected override bool CanSelectMultipleInstances()
{
return false;
}
protected override Type CreateCollectionItemType()
{
return typeof(BLK_TabPage);
}
protected override object CreateInstance(Type itemType)
{
BLK_TabPage tabPage = (BLK_TabPage)itemType.Assembly.CreateInstance(itemType.FullName);
IDesignerHost host = (IDesignerHost)this.GetService(typeof(IDesignerHost));
host.Container.Add(tabPage);
//this.Context.Container.Add(tabPage);
tabPage.Text = tabPage.Name;
return tabPage;
}
}
public class BLK_TabControl : TabControl
{
[EditorAttribute(typeof(BLK_TabPageCollectionEditor), typeof(UITypeEditor))]
[MergableProperty(false)]
public new TabControl.TabPageCollection TabPages
{
get
{
return base.TabPages;
}
}
}
Thanks in advance.

I have tried your code. Looks like everything is fine. But based on designer-generated code and your image description the only thing I can suggest is to hide serialization of TabPages property:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorAttribute(typeof(BLK_TabPageCollectionEditor), typeof(UITypeEditor))]
[MergableProperty(false)]
public new TabControl.TabPageCollection TabPages
{
get
{
return base.TabPages;
}
}

Related

How to Set a Collection of Custom Controls from the property GridView in c#

I have an ObservableCollection of MySearchFields, on a custom Form, lets say CustomForm I declare it as below:
private ObservableCollection<MySearchFields> _mySearchFields = new ObservableCollection<MySearchFields>();
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Category("MyFrameWork")]
public ObservableCollection<MySearchFields> MySearchFields
{
get => _mySearchFields;
}
and then on my Main form where it inherits from CustomForm,
when I'm on the designer view and go to propertygrid window I select my ControlType To be ComboBox and then I select DataSource, ValueMember And Display Member. the issue is it does not set DataSource,ValueMember or DisplayMember.
why does this happen? am I missing something?
public class MySearchFields
{
public enum MyControlTypes
{
MyTextBox,
MyDateTimePicker,
MyLbComboBox
}
private MyControlTypes _myControlType = MyControlTypes.MyTextBox;
public MyControlTypes MyControlType
{
get => _myControlType;
set
{
_myControlType = value;
switch (_myControlType)
{
case MyControlTypes.MyTextBox:
{
MyControl = new TextBox();
break;
}
case MyControlTypes.MyLbComboBox:
{
MyControl = new MyLbComboBox();
break;
}
case MyControlTypes.MyDateTimePicker:
{
MyControl = new DateTimePicker();
break;
}
}
}
}
public Control MyControl { get; set; }
public MySearchFields()
{
MyControl = new Control();
}
}
Your error is that on your case switcher you insert new MyLBcombobox instead of just ComboBox.
try initializing public Control MyControl { get; set; } with the proper setters and getters, as well as a private variable the backs it up,
Furthermore just use the fullprop snippet of Visual studio
just write "fullprop" and the press 2 times TAB, swapping around => and {} is not that nice.

Add another dropdown button to own devexpres PopupContainerEdit with same style behaviour

We've got custom PopupContainerEdit that inherits from DevExpress'es PopupContainerEdit. One of our custom features is another dropdown button (EditorButton with kind = ButtonPredefines.Glyph) that acts like the default one except, it opens different PopupContainerControl. Everything works as intended except button's style coloring. The button acts like default button - that means it doesn't support state coloring (checked/unchecked) when dropdown is visible/hidden. I couldn't find any custom draw event/method for EditorButton.
Is it possible to achieve such behaviour? If so, how?
#edit
Simple example of the above situation.
Default PopupContainerEdit looks like image A. When you click on the
button (triangle like), dropdown shows and button goes into checked
state.
Our PopupContainerEdit (that inherits from default) looks like
B.
C, D is coloring example when you hover the button.
E is checked state coloring for default button (it works like that by
DevExpress'es design).
F is our button behaviour - acts like normal button.
G is what we want - checked state coloring for our button
Our approach to create custom button:
string TheToolTipText = "The text";
string OurButtonTag = "TheButton";
Image TheIcon = new Image(); // just example ...
EditorButton customButton = new EditorButton();
customButton.Width = 16;
customButton.Image = TheIcon;
customButton.ToolTip = TheToolTipText;
customButton.Tag = OurButtonTag;
customButton.Kind = ButtonPredefines.Glyph;
this.Properties.Buttons.Add(customButton);
To be honest there's nothing more to show. We're not aware of any custom Draw event or similar things.
There are two properties in RepositoryItemPopupContainerEdit that are responsible for this behavior. Fisrt one is RepositoryItemPopupBase.ActionButtonIndex property. It's value specifying which editor button will open the editor's dropdown window. The second one is RepositoryItemPopupContainerEdit.PopupControl which sets the control to display in the popup window. So, by manipulating with this two properties, you can achieve the desired behavior.
Here is example:
0. RepositoryItemPopupContainerEdit descendant
Because you need to show two different PopupContainerControl
you can create additional properties for each of your controls in your custom RepositoryItem.
public class RepositoryItemCustomEdit1 : RepositoryItemPopupContainerEdit
{
#region Some default stuff for custom repository item (constructors, registration, etc).
static RepositoryItemCustomEdit1() { RegisterCustomEdit1(); }
public const string CustomEditName = "CustomEdit1";
public RepositoryItemCustomEdit1() { }
public override string EditorTypeName { get { return CustomEditName; } }
public static void RegisterCustomEdit1()
{
Image img = null;
EditorRegistrationInfo.Default.Editors.Add(new EditorClassInfo(
CustomEditName,
typeof(CustomEdit1),
typeof(RepositoryItemCustomEdit1),
//For v13.2 you need to use custom ViewInfo class. So, here is CustomEdit1ViewInfo.
//For v15.1 you can use the base PopupContainerEditViewInfo.
typeof(CustomEdit1ViewInfo),
new ButtonEditPainter(),
true,
img));
}
#endregion
#region Hide base PopupContainerControl properties in designer.
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override PopupContainerControl PopupControl
{
get { return base.PopupControl; }
set { base.PopupControl = value; }
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override int ActionButtonIndex
{
get { return base.ActionButtonIndex; }
set { base.ActionButtonIndex = value; }
}
#region
#region First PopupContainerControl properties
public int DefaultActionButtonIndex { get; set; }
public PopupContainerControl DefaultPopupControl { get; set; }
#endregion
#region Another PopupContainerControl properties
public int DifferentActionButtonIndex { get; set; }
public PopupContainerControl DifferentPopupControl { get; set; }
#endregion
public override void Assign(RepositoryItem item)
{
BeginUpdate();
try
{
base.Assign(item);
RepositoryItemCustomEdit1 source = item as RepositoryItemCustomEdit1;
if (source == null) return;
DefaultActionButtonIndex = source.DefaultActionButtonIndex;
DefaultPopupControl = source.DefaultPopupControl;
DifferentPopupControl = source.DifferentPopupControl;
DifferentActionButtonIndex = source.DifferentActionButtonIndex;
}
finally
{
EndUpdate();
}
}
}
You can see new properties in your designer:
1. PopupContainerEdit descendant
Now you can use this properties in your custom Edit class.
public class CustomEdit1 : PopupContainerEdit
{
#region Some default stuff for custom edit (constructors, registration, etc).
static CustomEdit1() { RepositoryItemCustomEdit1.RegisterCustomEdit1(); }
public CustomEdit1() { }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public new RepositoryItemCustomEdit1 Properties { get { return base.Properties as RepositoryItemCustomEdit1; } }
public override string EditorTypeName { get { return RepositoryItemCustomEdit1.CustomEditName; } }
#endregion
protected override bool IsActionButton(EditorButtonObjectInfoArgs buttonInfo)
{
int buttonIndex = Properties.Buttons.IndexOf(buttonInfo.Button);
if (buttonIndex == Properties.DefaultActionButtonIndex ||
buttonIndex == Properties.DifferentActionButtonIndex)
{
//Set the Properties.ActionButtonIndex value according to which button is pressed:
Properties.ActionButtonIndex = buttonIndex;
//Set the Properties.PopupControl according to which button is pressed:
if (buttonIndex == Properties.DefaultActionButtonIndex)
Properties.PopupControl = Properties.DefaultPopupControl;
else
Properties.PopupControl = Properties.DifferentPopupControl;
return true;
}
return false;
}
}
2. PopupContainerEditViewInfo descendant
For v13.2 you need to use custom ViewInfo class for your editor:
public class CustomEdit1ViewInfo : PopupContainerEditViewInfo
{
public CustomEdit1ViewInfo(RepositoryItem item) : base(item) { }
public new RepositoryItemPopupBase Item { get { return base.Item as RepositoryItemCustomEdit1; } }
//Show the pressed state when button is pressed or when popup is open.
protected override bool IsButtonPressed(EditorButtonObjectInfoArgs info)
{
var hitObject = PressedInfo.HitObject as EditorButtonObjectInfoArgs;
return
(hitObject != null && hitObject.Button == info.Button) ||
(IsPopupOpen && Item.ActionButtonIndex == info.Button.Index);
}
}
Result
In the result you will get something like this:
and

Button located under tabitem (within tabcontrol) but the handler needs to be in the main window

I have a main windows, which contains some controls including a tabcontrol. The tab control itself has multiple tabitems. Each tab item has a unique design. When the user clicks a button within one of the tab items, I want the event handler to be within the main window's .cs file. I cant figure out a way to do that, can any one enlighten me?
My syntax may not be perfect, but what about this:
public interface ITabControlWithNotifications
{
void OnButtonClicked(...);
}
public sealed class TabControlWithNotifications : TabControl, ITabControlWithNotifications
{
public void OnButtonClicked(...)
{
...
}
}
public sealed class TabPageWithNotifications : TabPage
{
private readonly ITabControlWithNotifications parentTabControl;
public TabPageWithNotifications(ITabControlWithNotifications tabControlWithNotifications)
{
this.parentTabControl = tabControlWithNotifications;
this.InitialzeComponents();
}
... ClickEventHandler(...)
{
this.parentTabControl.OnButtonClicked(...);
}
}
public sealed class TabControlFactory
{
public void Build()
{
var parentTabControl = new TabControlWithNotifications();
var tabPage1 = new TabPageWithNotifications(parentTabControl);
var tabPage2 = new TabPageWithNotifications(parentTabControl);
var tabPage3 = new TabPageWithNotifications(parentTabControl);
parentTabControl.Controls.Add(tabPage1);
parentTabControl.Controls.Add(tabPage2);
parentTabControl.Controls.Add(tabPage3);
}
}

Creating Smart Tag for Form (not other control) using c#

As the Form of System.Windows.Forms inherits from Control, I was wondering if there is a way to create a Custom Form and its Designer with some options (shortcuts) to create a title or somthings like that.
I tried this, but nothings happend, the Form I calles ManagedForm
[Designer(typeof(ManagedFormDesigner))]
public class ManagedForm : Form{
//code here
}
[PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class ManagedFormDesigner : ControlDesigner {
private DesignerActionListCollection actionLists;
public override DesignerActionListCollection ActionLists {
get {
if (actionLists == null) {
actionLists = new DesignerActionListCollection();
actionLists.Add(new ManagedFormDesignerActionList(this.Component));
}
return actionLists;
}
}
}
public class ManagedFormDesignerActionList : DesignerActionList {
private ManagedForm managedForm = null;
private DesignerActionUIService designerActionUISvc = null;
public ManagedFormDesignerActionList(IComponent component) : base(component) {
this.managedForm = component as ManagedForm;
this.designerActionUISvc =
GetService(typeof(DesignerActionUIService))
as DesignerActionUIService;
}
public override DesignerActionItemCollection GetSortedActionItems() {
DesignerActionItemCollection items = new DesignerActionItemCollection();
items.Add(new DesignerActionMethodItem(this, "CreateTitle", "Create Title", "Appearence", true));
return items;
}
public void CreateTitle() {
Panel pTitulo = new Panel();
pTitulo.Size= new Size(100,25);
pTitulo.Dock = DockStyle.Top;
(this.Component as ManagedForm).Controls.Add(pTitulo);
}
}
Action list are show when you click on the little arrow on the control inside a form (or on a component on the bottom of the designer if the object is a component).
Other things you can do is to manage verbs.
Verbs Handling is implemented on the ControlDesigner class (ManagedFormDesigner in your case).
You can see verbs clicking right mouse button or on the bottom of the properties (i.e. TabControl ha 2 verbs, add tab and remove tab).
You can implement verbs adding to ControlDesigner (or ComponentDesigner) class something like this
private DesignerVerbCollection _verbs;
public override DesignerVerbCollection Verbs
{
get
{
if (_verbs == null)
{
_verbs = new DesignerVerbCollection();
_verbs.Add(new DesignerVerb("Create Title", new EventHandler(MyCreateTitleHandler)));
}
return _verbs;
}
}
private void MyCreateTitleHandler(object sender, EventArgs e)
{
// Do here something but take care to show things via IUIService service
IUIService uiService = GetService(typeof(IUIService)) as IUIService;
}

Why overriden ToString() do not return what I want when item added to ComboBox?

public partial class TestConrol : UserControl
{
public TestConrol()
{
InitializeComponent();
}
public override string ToString()
{
return "asd";
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
TestConrol tc1 = new TestConrol();
comboBox1.Items.Add(tc1);
TestConrol tc2 = new TestConrol();
comboBox1.Items.Add(tc2);
}
}
When form loaded, I see combobox has two items with empty names, instead of "asd" :/
But this work if I override ToString() in common class, not derived from anything:
public class TestClass
{
public override string ToString()
{
return "bla bla bla";
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
TestClass tcl = new TestClass();
comboBox1.Items.Add(tcl);
}
}
After that I see in combobox "bla bla bla"
Create a property in you control and map the DisplayMember of the combobox to that property, it should work.
I tried to understand the source code(!). This is not a simple call to ToString().
There's an internal class System.Windows.Forms.Formatter doing some stuff. It eventually creates a converter. This is roughly equivalent to saying:
var conv = System.ComponentModel.TypeDescriptor.GetConverter(tc1.GetType());
where tc1 is the TestContol from your question. Now, had we used the TestClass tcl which doesn't implement any interfaces, this would have given us a converter which would eventually call ToString().
But in this example we use tc1, and it is a System.ComponentModel.IComponent. Our conv therefore becomes a System.ComponentModel.ComponentConverter. It uses the Site of the IComponent. When we say:
string result = conv.ConvertTo(tc1, typeof(string));
and the Site is null, we get the empty string "" you saw in your combo box. Had there been a Site it would have used its Name instead.
To demonstrate that, put the following into your TestControl instance constructor:
public TestConrol()
{
InitializeComponent();
Site = new DummySite(); // note: Site is public, so you can also
// write to it from outside the class.
// It is also virtual, so you can override
// its getter and setter.
}
where DummySite is something like:
class DummySite : ISite
{
public IComponent Component
{
get { throw new NotImplementedException(); }
}
public IContainer Container
{
get { throw new NotImplementedException(); }
}
public bool DesignMode
{
get { throw new NotImplementedException(); }
}
public string Name
{
get
{
return "asd"; // HERE'S YOUR TEXT
}
set
{
throw new NotImplementedException();
}
}
public object GetService(Type serviceType)
{
return null;
}
}
Use comboBox1.Items.Add(tc1.ToString()); instead of comboBox1.Items.Add(tcl);
This worked for me:
comboBox1.FormattingEnabled = false
In your UserControl, add a property, and call it FriendlyName for example, as such
namespace project
{
public partial class CustomUserControl : UserControl
{
public CustomUserControl()
{
InitializeComponent();
}
public String FriendlyName { get => "Custom name"; }
}
}
And then set the DisplayMember property of your ComboBox to "FriendlyName", as such
myComboBox.DisplayMember = "FriendlyName";
And to me, this was a very clean solution and gut tells me it is the intended way to do it.

Categories

Resources