Show Image for ToolStripControlHost in drop down menu - c#

I have a ToolStripSplitButton with various elements in dropdown list.
One of them is a Trackbar enclosed in a ToolStripControlHost, called ToolStripTrackbarItem. It's code (I've got it from stackoverflow):
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace Application
{
[System.ComponentModel.DesignerCategory("code")]
[System.Windows.Forms.Design.ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ContextMenuStrip | ToolStripItemDesignerAvailability.MenuStrip)]
public class ToolStripTrackbarItem : ToolStripControlHost
{
public ToolStripTrackbarItem()
: base(CreateControlInstance())
{
this.Size = Control.Size;
}
public TrackBar TrackBar
{
get { return Control as TrackBar; }
}
private static Control CreateControlInstance()
{
TrackBar t = new TrackBar();
t.AutoSize = false;
return t;
}
[DefaultValue(0)]
public int Value
{
get { return TrackBar.Value; }
set { TrackBar.Value = value; }
}
protected override void OnSubscribeControlEvents(Control control)
{
base.OnSubscribeControlEvents(control);
TrackBar trackBar = control as TrackBar;
trackBar.ValueChanged += new EventHandler(trackBar_ValueChanged);
}
protected override void OnUnsubscribeControlEvents(Control control)
{
base.OnUnsubscribeControlEvents(control);
TrackBar trackBar = control as TrackBar;
trackBar.ValueChanged -= new EventHandler(trackBar_ValueChanged);
}
void trackBar_ValueChanged(object sender, EventArgs e)
{
if (this.ValueChanged != null)
ValueChanged(sender, e);
}
public event EventHandler ValueChanged;
protected override Size DefaultSize
{
get { return new Size(300, 16); }
}
}
It works, but I need to show images to the left of the dropdown items:
I'm successful with a simple ToolStripMenuItem by setting the Image property. However, it is ineffective to set Image property of my ToolStripTrackbarItem (that is inherited from ToolStripControlHost, see code above). According to MSDN, Image property is irrelevant to ToolStripControlHost.
What does it mean? Is it not even possible to include an image left to ToolStripControlHost?
If it is possible anyway, how to do that?

You should solve 2 problems here:
ToolStripControlHost doesn't show Image property and also doesn't serialize the image when you save the form.
ToolStripProfessionalRendered doesn't draw image for ToolStripControlHost.
You need to override Image property of ToolStripControlHost and make it browsable and serializable. Also you need to create a custom renderer to draw the image in the correct location and size. Then if you simply set the renderer for ToolStrip using below code, you will get expected result:
this.toolStrip1.Renderer = new MyCustomRenderer();
ToolStripTrackBar
The item enables Image property to show in property grid and let it serialize when saving form.
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.Design;
[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.All)]
public class ToolStripTrackBar : ToolStripControlHost
{
public TrackBar TrackBar { get { return (TrackBar)Control; } }
public ToolStripTrackBar() : base(CreateControl()) { }
private static TrackBar CreateControl()
{
var t = new TrackBar()
{ TickStyle = TickStyle.None, AutoSize = false, Height = 28 };
return t;
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Image Image
{
get { return base.Image; }
set { base.Image = value; }
}
/*Expose properties and events which you need.*/
public int Value
{
get { return TrackBar.Value; }
set { TrackBar.Value = value; }
}
}
MyCustomRenderer
This renderer draws images for ToolStripTrackBar.
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
public class MyCustomRenderer : ToolStripProfessionalRenderer
{
protected override void OnRenderImageMargin(ToolStripRenderEventArgs e)
{
base.OnRenderImageMargin(e);
e.ToolStrip.Items.OfType<ToolStripTrackBar>()
.ToList().ForEach(item =>
{
if (item.Image != null)
{
var size = item.GetCurrentParent().ImageScalingSize;
var location = item.Bounds.Location;
location = new Point(5, location.Y + 1);
var imageRectangle = new Rectangle(location, size);
e.Graphics.DrawImage(item.Image, imageRectangle,
new Rectangle(Point.Empty, item.Image.Size),
GraphicsUnit.Pixel);
}
});
}
}

Related

Make Propertygrid show bound property symbol at runtime

When you bind a control's property using DataBindings to a datasource, the property grid will show a little purple or black symbol in that property coresponding grid item.
Even when you place your PropertyGrid on the form and set its SelectedObject property to the control with the bound property, that PropertyGrid on the form will show that symbol as well.
But only at design time.
Is there a (simple) way to make the very same PropertyGrid to show this symbol at runtime?
It is handled by Visual Studio designer internal things. But you also can add this feature to PropertyGrid:
You need to implement IPropertyValueUIService and using reflection, assign an instance of the service to the grid entries which should show the glyph. This implementation has a method GetPropertyUIValueItems which can be used to provide that glyph and a tooltip to show near the property label in PropertyGrid. Those values will be used in PaintLabel method of the property grid entry.
Then create an inherited PropertyGrid and override OnSelectedObjectsChanged and OnPropertySortChanged. In those method for each property grid entry item which is presenting a property which is in data bindings collection, set an instance of the implemented IPropertyValueUIService as value of pvSvc private property of the PropertyGrid and attach an event handler which will be called when PropertyGrid requests for additional information about the property. By attaching an event handler using GetPropertyUIValueItems to you can return the tooltip and image which you are going to show in front of the property.
Example
You can download the full example here:
r-aghaei/PropertyGridDataBindingGlyph
You can find main classes of the implementation as follows.
PropertyValueUIService
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing.Design;
namespace PropertyGridDataBindingGlyph
{
public class PropertyValueUIService : IPropertyValueUIService
{
private PropertyValueUIHandler handler;
private ArrayList items;
public event EventHandler PropertyUIValueItemsChanged;
public void NotifyPropertyValueUIItemsChanged()
{
PropertyUIValueItemsChanged?.Invoke(this, EventArgs.Empty);
}
public void AddPropertyValueUIHandler(PropertyValueUIHandler newHandler)
{
handler += newHandler ?? throw new ArgumentNullException("newHandler");
}
public PropertyValueUIItem[] GetPropertyUIValueItems(ITypeDescriptorContext context, PropertyDescriptor propDesc)
{
if (propDesc == null)
throw new ArgumentNullException("propDesc");
if (this.handler == null)
return new PropertyValueUIItem[0];
lock (this)
{
if (this.items == null)
this.items = new ArrayList();
this.handler(context, propDesc, this.items);
int count = this.items.Count;
if (count > 0)
{
PropertyValueUIItem[] propertyValueUiItemArray = new PropertyValueUIItem[count];
this.items.CopyTo((Array)propertyValueUiItemArray, 0);
this.items.Clear();
return propertyValueUiItemArray;
}
}
return null;
}
public void RemovePropertyValueUIHandler(PropertyValueUIHandler newHandler)
{
handler -= newHandler ?? throw new ArgumentNullException("newHandler");
}
}
}
ExPropertyGrid
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace PropertyGridDataBindingGlyph
{
public class ExPropertyGrid : PropertyGrid
{
Bitmap dataBitmap;
public ExPropertyGrid()
{
dataBitmap = new Bitmap(typeof(ControlDesigner).Assembly
.GetManifestResourceStream("System.Windows.Forms.Design.BoundProperty.bmp"));
dataBitmap.MakeTransparent();
}
protected override void OnSelectedObjectsChanged(EventArgs e)
{
base.OnSelectedObjectsChanged(e);
this.BeginInvoke(new Action(() => { ShowGlyph(); }));
}
protected override void OnPropertySortChanged(EventArgs e)
{
base.OnPropertySortChanged(e);
this.BeginInvoke(new Action(() => { ShowGlyph(); }));
}
private void ShowGlyph()
{
var grid = this.Controls[2];
var field = grid.GetType().GetField("allGridEntries",
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.Instance | BindingFlags.FlattenHierarchy);
var value = field.GetValue(grid);
if (value == null)
return;
var entries = (value as IEnumerable).Cast<GridItem>().ToList();
if (this.SelectedObject is Control)
{
((Control)this.SelectedObject).DataBindings.Cast<Binding>()
.ToList().ForEach(binding =>
{
var item = entries.Where(x => x.PropertyDescriptor?.Name == binding.PropertyName).FirstOrDefault();
var pvSvcField = item.GetType().GetField("pvSvc", BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.FlattenHierarchy);
IPropertyValueUIService pvSvc = new PropertyValueUIService();
pvSvc.AddPropertyValueUIHandler((context, propDesc, valueUIItemList) =>
{
valueUIItemList.Add(new PropertyValueUIItem(dataBitmap, (ctx, desc, invokedItem) => { }, GetToolTip(binding)));
});
pvSvcField.SetValue(item, pvSvc);
});
}
}
private static string GetToolTip(Binding binding)
{
var value = "";
if (binding.DataSource is ITypedList)
value = ((ITypedList)binding.DataSource).GetListName(new PropertyDescriptor[] { });
else if (binding.DataSource is Control)
value = ((Control)binding.DataSource).Name;
else if (binding.DataSource is Component)
value = ((Component)binding.DataSource).Site?.Name;
if (string.IsNullOrEmpty(value))
value = "(List)";
return value + " - " + binding.BindingMemberInfo.BindingMember;
}
}
}

Adding Checkbox to comboBox not visible in dropdown

I am adding a checkbox to a comboBox in a windowsform. The checkbox add and I can select them as Items but I can see the text of the box to tick. In the combobox drop down the items are list empty and clicking on them specific the correct details when incepting the selected item.
How do I make them visable to see the box and name?
You need to create your own user control. First step is to create subclass the System.Windows.Forms.ComboBox class:
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class CheckComboBox : ComboBox
{
public CheckComboBox()
{
this.DrawMode = DrawMode.OwnerDrawFixed;
}
}
}
You should set the DrawMode property to tell the ComboBox that we intend to render the drop-down list items ourselves. The next step was to define a class to contain our drop-down list item data and maintain the state. This is a simple class:
namespace WindowsFormsApp1
{
public class CheckComboBoxItem
{
public CheckComboBoxItem(string text, bool initialCheckState)
{
_checkState = initialCheckState;
_text = text;
}
private bool _checkState = false;
public bool CheckState
{
get { return _checkState; }
set { _checkState = value; }
}
private string _text = "";
public string Text
{
get { return _text; }
set { _text = value; }
}
public override string ToString()
{
return "Select Options";
}
}
}
After that go back to your CheckComboBox.cs and add delegates DrawItem and SelectedIndexChanged event.
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace WindowsFormsApp1
{
public partial class CheckComboBox : ComboBox
{
public event EventHandler CheckStateChanged;
public CheckComboBox()
{
this.DrawMode = DrawMode.OwnerDrawFixed;
this.DrawItem += new DrawItemEventHandler(CheckComboBox_DrawItem);
this.SelectedIndexChanged += new EventHandler(CheckComboBox_SelectedIndexChanged);
}
void CheckComboBox_DrawItem(object sender, DrawItemEventArgs e)
{
if (e.Index == -1)
{
return;
}
if (!(Items[e.Index] is CheckComboBoxItem))
{
e.Graphics.DrawString(
Items[e.Index].ToString(),
this.Font,
Brushes.Black,
new Point(e.Bounds.X, e.Bounds.Y));
return;
}
CheckComboBoxItem box = (CheckComboBoxItem)Items[e.Index];
CheckBoxRenderer.RenderMatchingApplicationState = true;
CheckBoxRenderer.DrawCheckBox(
e.Graphics,
new Point(e.Bounds.X, e.Bounds.Y),
e.Bounds,
box.Text,
this.Font,
(e.State & DrawItemState.Focus) == 0,
box.CheckState ? CheckBoxState.CheckedNormal :
CheckBoxState.UncheckedNormal);
}
void CheckComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
CheckComboBoxItem item = (CheckComboBoxItem)SelectedItem;
item.CheckState = !item.CheckState;
CheckStateChanged?.Invoke(item, e);
}
}
}
In DrawItems delegate, the first thing we do is to verify that the item we are rendering was added as a CheckComboBoxItem. If it is not, we render it as a simple string. Otherwise, we get the appropriate CheckComboBoxItem from the Items collection (using the DrawItemEventArgs.Index property). Then we call the CheckBoxRenderer.DrawCheckBox() method, passing in the Graphics object, into which we want to render the CheckBox, and the location, size, text, font, focus and check states.
The second one allows us to toggle the check box in the drop-downs, but doesn't allow the user of this control to know that anything has happened. So we also add a public event to notify the control's users of a change to the check state of an item in the drop-down list:
public event EventHandler CheckStateChanged;
Finally, if you want to use this control, in the default Form1 of your application type this code:
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
checkComboBox1.Items.Add(new CheckComboBoxItem("One", true));
checkComboBox1.Items.Add(new CheckComboBoxItem("Two", true));
checkComboBox1.Items.Add(new CheckComboBoxItem("Three", true));
this.checkComboBox1.CheckStateChanged += new EventHandler(this.checkComboBox1_CheckStateChanged);
}
private void checkComboBox1_CheckStateChanged(object sender, EventArgs e)
{
if (sender is CheckComboBoxItem)
{
CheckComboBoxItem item = (CheckComboBoxItem)sender;
}
}
}
}
You have so many links that can be useful to you. You do not put any code so we don't know what exactly you need...
https://www.codeproject.com/Articles/31105/A-ComboBox-with-a-CheckedListBox-as-a-Dropdown
https://www.codeproject.com/Articles/21085/CheckBox-ComboBox-Extending-the-ComboBox-Class-and
https://www.codeproject.com/Articles/18929/An-OwnerDraw-ComboBox-with-CheckBoxes-in-the-Drop
Thank you #Rob and #Mamun for correcting me.

how to create custom control in visual studio 2015 and add it to toolbox

I want to create a control that draw a table in class library project .and add this dll to toolbox and use it in my windows form app. I try and googling but I can't find.
what should i do ?
I created this class in class library project
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
namespace ClassLibrary1
{
class PanelZ : System.Windows.Forms.Panel
{
private Color color1 = Color.SteelBlue;
private Color color2 = Color.DarkBlue;
private int color1Transparent = 150;
private int color2Transparent = 150;
private int angle = 90;
public Color StartColor
{
get { return color1; }
set { color1 = value; Invalidate(); }
}
public Color EndColor
{
get { return color2; }
set { color2 = value; Invalidate(); }
}
public int Transparent1
{
get { return color1Transparent; }
set
{
color1Transparent = value;
if (color1Transparent > 255)
{
color1Transparent = 255;
Invalidate();
}
else
Invalidate();
}
}
public int Transparent2
{
get { return color2Transparent; }
set
{
color2Transparent = value;
if (color2Transparent > 255)
{
color2Transparent = 255;
Invalidate();
}
else
Invalidate();
}
}
public int GradientAngle
{
get { return angle; }
set { angle = value; Invalidate(); }
}
public PanelZ()
{
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Color c1 = Color.FromArgb(color1Transparent, color1);
Color c2 = Color.FromArgb(color2Transparent, color2);
Brush b = new System.Drawing.Drawing2D.LinearGradientBrush(ClientRectangle, c1, c2, angle);
e.Graphics.FillRectangle(b, ClientRectangle);
b.Dispose();
}
}
}
but when i add mydll to toolbox i get this error
image here
In WPF or Winforms the Toolbox is smart about components that are part of the solution you are building. For Winforms, simply add a reference to System.Windows.Forms and System.Drawing to your class library and then inherit from Control (or any other class that inherits from Control).
For example, I can create a custom control like this (Note that it has to be a public control for the toolbox to find it):
using System.Drawing;
using System.Windows.Forms;
namespace ClassLibrary1
{
public class CustomControl : Control
{
public CustomControl()
{
this.BackColor = Color.Red;
}
}
}
Once I build the project, I can then see it in the Toolbox when interacting with my Form in my application.

passing font size to user control tool tip in Winform C# project

Question: I have a Winform C# project that changes the tool tip text of a user control tool tip when passed from a host project. I need to pass font size as a variable, but dont know where to make the changes. I have tried a plethora of solutions online and am stuck with this implementation. Any help in this direction will be really appreciated.
What I have so far:
I have a C# user Control project where i have set a property to set the tool tip text to change to user specified value in my UC_ToolTipButton project, where the contents of the project are as below:
UC_ToolTipButton.cs
using System.Windows.Forms;
namespace UC_ToolTipButton
{
public partial class UC_ToolTipButton : UserControl
{
public string TT_Message
{
get{
return ToolTip_Message.GetToolTip(btnTT);
}
set{
ToolTip_Message.SetToolTip(btnTT, value);
}
}
public UC_ToolTipButton()
{
InitializeComponent();
}
}
}
In my designer file, I have placed a button (btnTT) on which I have put a tool tip (ToolTip_Message).
When I compile this User Control Forms project, it works fine and creates a dll file.
Upon importing this file in a project TryButtonTooltip, where I have the file TryTooltipForm.cs with the following content
using System.Windows.Forms;
namespace TryButtonToolTip
{
public partial class TryToolTipForm : Form
{
public TryToolTipForm()
{
InitializeComponent();
uC_TTMessage.TT_Message = #"Hi";
}
}
}
Set OwnerDraw on ToolTip to true, in ToolTip's Draw event set the desired font, then in Popup event measure and set the size of your ToolTip, as is explained in the example here.
For example like this (untested):
public partial class UC_ToolTipButton : UserControl
{
public string TT_FontFamily { get; set; }
public float TT_FontSize { get; set; }
public string TT_Message
{
get
{
return ToolTip_Message.GetToolTip(btnTT);
}
set
{
ToolTip_Message.SetToolTip(btnTT, value);
}
}
public UC_ToolTipButton()
{
InitializeComponent();
TT_FontFamily = "Tahoma";
TT_FontSize = 10;
ToolTip_Message.OwnerDraw = true;
ToolTip_Message.Draw += new DrawToolTipEventHandler(TT_Draw);
ToolTip_Message.Popup += new PopupEventHandler(TT_Popup);
}
private void TT_Popup(object sender, PopupEventArgs e)
{
using (Font f = new Font(TT_FontFamily, TT_FontSize))
{
e.ToolTipSize = TextRenderer.MeasureText(ToolTip_Message.GetToolTip(e.AssociatedControl), f);
}
}
private void TT_Draw(System.Object sender,
System.Windows.Forms.DrawToolTipEventArgs e)
{
e.DrawBackground();
e.DrawBorder();
using (StringFormat sf = new StringFormat())
{
sf.Alignment = StringAlignment.Center;
sf.LineAlignment = StringAlignment.Center;
using (Font f = new Font(TT_FontFamily, TT_FontSize))
{
e.Graphics.DrawString(e.ToolTipText, f, SystemBrushes.ActiveCaptionText, e.Bounds, sf);
}
}
}
}

Custom ColorEditor does not work properly on Color struct

I asked here how to use custom color dialog in property grid for Color struct:
Using custom color picker dialog in PropertyGrid
From that link you can see code of MyColorEditor class if needed.
Now i can use custom color dialog but only if i use my own struct which is RGBA in that example.
If i use this custom type editor on color struct it looks like this in property grid:
But if i use RGBA struct which i created, it looks properly:
Problem happens because UITypeEditorEditStyle.Modal not applying with GetEditStyle() i think.
Using Color struct can be better than using my custom color struct for me because then i can set DefaultValue for Color property without requiring to write my own type converter.
So my question is how to use custom editor on Color struct.
Finally figured out how to do it, ColorConverter was causing this problem so need to use my own ColorConverter like this:
public class MyColorConverter : ColorConverter
{
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return false;
}
}
Editor codes:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace HelpersLib
{
public class MyColorEditor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
if (value.GetType() != typeof(Color))
{
return value;
}
IWindowsFormsEditorService svc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
if (svc != null)
{
Color color = (Color)value;
using (DialogColor form = new DialogColor(color))
{
if (svc.ShowDialog(form) == DialogResult.OK)
{
return (Color)form.NewColor;
}
}
}
return value;
}
public override bool GetPaintValueSupported(ITypeDescriptorContext context)
{
return true;
}
public override void PaintValue(PaintValueEventArgs e)
{
Graphics g = e.Graphics;
Color color = (Color)e.Value;
if (color.A < 255)
{
using (Image checker = ImageHelpers.CreateCheckers(e.Bounds.Width / 2, e.Bounds.Height / 2, Color.LightGray, Color.White))
{
g.DrawImage(checker, e.Bounds);
}
}
using (SolidBrush brush = new SolidBrush(color))
{
e.Graphics.FillRectangle(brush, e.Bounds);
}
e.Graphics.DrawRectangleProper(Pens.Black, e.Bounds);
}
}
}
And example usage:
[DefaultValue(typeof(Color), "Black"),
Editor(typeof(MyColorEditor), typeof(UITypeEditor)),
TypeConverter(typeof(MyColorConverter))]
public Color Color { get; set; }
Screenshot:

Categories

Resources