Change the backcolor of current line of caret position [duplicate] - vb.net

I have uploaded a image of what i want to achive...
So as you can see i want to highlight the line i click on [And update it on _textchanged event!
Is there any possible way of doing this in any colour... doesnt have to be yellow. I have searched alot but i cant understand how to get the starting length and end length and all of that.
It has confused me alot and i don;'t understand and would require some help.
Thanks for all help that is given in this thread. Also it is windows form. Im making a notepad app like notepad++ or some other notepad app... .NET Windows Form C# RichTextBox

You'll need to create your own control that inherits from RichTextBox and use that control on your form. Since the RichTextBox does not support owner drawing, you will have to listen for the WM_PAINT message and then do your work in there. Here is an example that works fairly well, though the line height is hard coded right now:
public class HighlightableRTB : RichTextBox
{
// You should probably find a way to calculate this, as each line could have a different height.
private int LineHeight = 15;
public HighlightableRTB()
{
HighlightColor = Color.Yellow;
}
[Category("Custom"),
Description("Specifies the highlight color.")]
public Color HighlightColor { get; set; }
protected override void OnSelectionChanged(EventArgs e)
{
base.OnSelectionChanged(e);
this.Invalidate();
}
private const int WM_PAINT = 15;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_PAINT)
{
var selectLength = this.SelectionLength;
var selectStart = this.SelectionStart;
this.Invalidate();
base.WndProc(ref m);
if (selectLength > 0) return; // Hides the highlight if the user is selecting something
using (Graphics g = Graphics.FromHwnd(this.Handle))
{
Brush b = new SolidBrush(Color.FromArgb(50, HighlightColor));
var line = this.GetLineFromCharIndex(selectStart);
var loc = this.GetPositionFromCharIndex(this.GetFirstCharIndexFromLine(line));
g.FillRectangle(b, new Rectangle(loc, new Size(this.Width, LineHeight)));
}
}
else
{
base.WndProc(ref m);
}
}
}

Related

Why my scroll view does not scroll to the bottom when adding two sentences consecutively?

I created a scroll window like this, hoping it will auto scroll to the bottom when text continuously come:
public class LogWindow : MonoBehaviour
{
public Text logText;
Queue<string> textQueue = new Queue<string>();
public ScrollRect scrollRect;
public Scrollbar scrollbar;
void RefreshText()
{
string buffer = "";
foreach(string text in textQueue)
{
buffer += text;
buffer += '\n';
}
logText.text = buffer;
scrollRect.verticalNormalizedPosition = 0.0f;
scrollbar.value = 0.0f;
}
public void Log(string text, string color = "white")
{
text = String.Format("<color={0}>{1}</color>", color, text);
textQueue.Enqueue(text);
RefreshText();
}
}
It works fine when I call RefreshText once at a time. But when I do this:
Log("something 1");
Log("something 2");
I see that the log window is only scrolled down so that "something 1" is shown. I have to drag it manually to see "something 2".
My settings on the Text is:
I suppose there is something wrong in RefreshText. Can anyone tell me where did I go wrong?
Thank you!

how to set xaml mapcontrol mapicon always visible

I'm fairly new to programming in XAML and I'm making a test application on windows phone 8.1 emulator with a MapControl.
I wanted to add a MapIcon to my map but the icon doesn't appear when the map is zoomed out. I've searched the internet and couldn't find anything regarding my problem.
I want my zoomlevel 12 and show the mapicon on that zoomlevel.
namespace TEST.APPLICATION
{
public partial class MapView : Page
{
Geolocator geo = null;
public MapView()
{
this.InitializeComponent();
this.NavigationCacheMode = NavigationCacheMode.Required;
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
}
void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
if (Frame.CanGoBack)
{
e.Handled = true;
Frame.GoBack();
}
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
map.Center = new Geopoint(new BasicGeoposition()
{
Latitude = 51.5856935784736,
Longitude = 4.79448171225132
});
map.ZoomLevel = 12;
displaySightings();
}
private void displaySightings()
{
MapIcon sighting1 = new MapIcon();
sighting1.Location = new Geopoint(new BasicGeoposition()
{
Latitude = 51.5940,
Longitude = 4.7795
});
//sighting1.NormalizedAnchorPoint = new Point(0.5, 1.0);
sighting1.Title = "VVV";
map.MapElements.Add(sighting1);
}
}
Is there any way to make the MapIcon always visible?
The MapIcon is not guaranteed to be shown. It may be hidden when it obscures other elements or labels on the map.
For some stupid reason, Microsoft thought that labels and other map elements should outrank map icons when rendering the display. So, if you're making an app displaying the locations of all the nearby Starbucks, the name of the high school across the street from the Starbucks is more important than the pushpin, according to them.
You'll need to render the pushpins using XAML instead.

XAML: Tap a textbox to enable?

In XAML and WinRT, Is there a way to set up a textbox so that it is disabled for text input until it is tapped.
I tried setting up the Tapped event and then setting the IsEnabled=true, but that only seems to work if the IsEnabled=true in the first place.
I found this on MSDN:
http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/708c0949-8b06-40ec-85fd-201139ca8b2d
Talks about adding the TappedEvent manually to the event handled for each TextBox, which is cumbersome, but also doesn't seem to work unless IsEnabled was already set to true.
Basically, I want a form where all textboxes display data but are disabled unless the user taps to enable the box and then type.
You can use IsReadOnly instead of IsEnabled to achieve what you are looking for. In addition, you can set up the tapped event handlers in code easily. I'm not sure if setting up handlers in code is a requirement for this to work, as you noted above; however, it does simplify things.
Here are the details.
In the constructor of your page class (here it is MainPage), call the setup function:
public MainPage()
{
this.InitializeComponent();
// call the setup for the textboxes
SetupTextBoxes();
}
Here is where we do the magic - make all textboxes on this page readonly and set up tap handler:
private void SetupTextBoxes()
{
var tbs = GetVisualChildren<TextBox>(this, true);
foreach (var tb in tbs)
{
tb.IsReadOnly = true;
tb.AddHandler(TappedEvent, new TappedEventHandler(tb_Tapped), true);
}
}
Utility function to get a list of all children of the given type (T) of the passed in parent.
private List<T> GetVisualChildren<T>(DependencyObject parent, bool recurse = true)
where T : DependencyObject
{
var children = new List<T>();
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
DependencyObject v = (DependencyObject)VisualTreeHelper.GetChild(parent, i);
var child = v as T;
if (child == null && recurse)
{
var myChildren = GetVisualChildren<T>(v, recurse);
children.AddRange(myChildren);
}
if (child != null)
children.Add(child);
}
return children;
}
Finally, the event handler. This enables each textbox when tapped.
private void tb_Tapped(object sender, TappedRoutedEventArgs e)
{
((TextBox)(sender)).IsReadOnly = false;
}

Telerik Mail Merge - Friendly Names (using RadRichTextBox)

I think i'm missing something obvious...
I'm using the Telerik Rad controls for WPF but i assume that the Rich text box uses some similar implementation for the mail merge functionality.
I want to have some friendly names on my mail merge fields. (namely spaces in the field names)
So i have a class for instance
Public Class someclass
{
<DisplayName("This is the complex description of the field")>
Public property thisfieldnamehasacomplexdescription as string
Public property anothercomplexfield as string
}
This is the only way i know to get "Friendly" names in the dropdown that is the mail merge.
So the two fields turn up okay as :
"This is the complex description of the field"
"anothercomplexfield"
but only anothercomplexfield actually populates with data when you do the merge.
Am i going to have to template the raddropdownbutton that holds the mail merge fields?
Is there an example of this somewhere?
Also a sub question. How do i add a scroll bar on these things?
(also i know this board is not a TELERIK specific board (duh!) but this might be useful to someone in the future. So i'll copy the answer i get from Telerik into here!
http://www.telerik.com/community/forums/wpf/richtextbox/558428-radrichtextbox-mailmerge---using-displayname-to-create-a-friendly-name-with-spaces.aspx )
This is what telerik gave me:
With the default MergeFields, it is not possible to change the display name fragment of the field in order to achieve a more friendly look. This should be possible if you implement a custom MergeField by deriving from the MergeField class. Here is a sample implementation that shows how this can be done:
public class CustomMergeField : MergeField
{
private const string CustomFieldName = "CustomField";
static CustomMergeField()
{
CodeBasedFieldFactory.RegisterFieldType(CustomMergeField.CustomFieldName, () => { return new CustomMergeField(); });
}
public override string FieldTypeName
{
get
{
return CustomMergeField.CustomFieldName;
}
}
public override Field CreateInstance()
{
return new CustomMergeField();
}
protected override DocumentFragment GetDisplayNameFragment()
{
return base.CreateFragmentFromText(string.Format(Field.DisplayNameFragmentFormat, this.GetFriendlyFieldName(this.PropertyPath)));
}
private string GetFriendlyFieldName(string fieldName)
{
int lettersInEnglishAlphabet = 26;
List<char> separators = new List<char>(lettersInEnglishAlphabet);
for (int i = 0; i < lettersInEnglishAlphabet; i++)
{
separators.Add((char)('A' + i));
}
StringBuilder newFieldName = new StringBuilder();
int previousIndex = 0;
for (int i = 1; i < fieldName.Length; i++)
{
if (separators.Contains(fieldName[i]))
{
if (previousIndex > 0)
{
newFieldName.Append(" ");
}
newFieldName.Append(fieldName.Substring(previousIndex, i - previousIndex));
previousIndex = i;
}
}
newFieldName.Append(" " + fieldName.Substring(previousIndex));
return newFieldName.ToString();
}
}
Note that the fragment that is shown when the DisplayMode is Code cannot be changed.
As for your other question, you can change the content of the dropdown button to show the friendly name of the fields and to include a scrollbar in the following way:
1. First, remove the binding of the button to the InsertMergeFieldEmptyCommand from XAML and give it a name (e.g. insertMergeField).
2. Next, add the following code in code-behind:
AddMergeFieldsInDropDownContent(this.insertMergeFieldButton);
private void AddMergeFieldsInDropDownContent(RadRibbonDropDownButton radRibbonDropDownButton)
{
Grid grid = new Grid();
grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(100, GridUnitType.Pixel) });
ScrollViewer scrollViewer = new ScrollViewer();
scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
StackPanel stackPanel = new StackPanel();
foreach (string fieldName in this.editor.Document.MailMergeDataSource.GetColumnNames())
{
RadRibbonButton fieldButton = new RadRibbonButton()
{
Text = this.GetFriendlyFieldName(fieldName),
Size = ButtonSize.Medium,
HorizontalAlignment = HorizontalAlignment.Stretch,
HorizontalContentAlignment = HorizontalAlignment.Left
};
fieldButton.Command = this.editor.Commands.InsertFieldCommand;
fieldButton.CommandParameter = new MergeField() { PropertyPath = fieldName };
//or
//fieldButton.CommandParameter = new CustomMergeField() { PropertyPath = fieldName };
stackPanel.Children.Add(fieldButton);
}
stackPanel.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
scrollViewer.Content = stackPanel;
grid.Children.Add(scrollViewer);
radRibbonDropDownButton.DropDownContent = grid;
}
You can, of course optimize the code of the GetFriendlyName method and add it in a way that will be available by both classes.

How to make a custom ComboBox (OwnerDrawFixed) looks 3D like the standard ComboBox?

I am making a custom ComboBox, inherited from Winforms' standard ComboBox. For my custom ComboBox, I set DrawMode to OwnerDrawFixed and DropDownStyle to DropDownList. Then I write my own OnDrawItem method. But I ended up like this:
How do I make my Custom ComboBox to look like the Standard one?
Update 1: ButtonRenderer
After searching all around, I found the ButtonRenderer class. It provides a DrawButton static/shared method which -- as the name implies -- draws the proper 3D button. I'm experimenting with it now.
Update 2: What overwrites my control?
I tried using the Graphics properties of various objects I can think of, but I always fail. Finally, I tried the Graphics of the form, and apparently something is overwriting my button.
Here's the code:
Protected Overrides Sub OnDrawItem(ByVal e As System.Windows.Forms.DrawItemEventArgs)
Dim TextToDraw As String = _DefaultText
__Brush_Window.Color = Color.FromKnownColor(KnownColor.Window)
__Brush_Disabled.Color = Color.FromKnownColor(KnownColor.GrayText)
__Brush_Enabled.Color = Color.FromKnownColor(KnownColor.WindowText)
If e.Index >= 0 Then
TextToDraw = _DataSource.ItemText(e.Index)
End If
If TextToDraw.StartsWith("---") Then TextToDraw = StrDup(3, ChrW(&H2500)) ' U+2500 is "Box Drawing Light Horizontal"
If (e.State And DrawItemState.ComboBoxEdit) > 0 Then
'ButtonRenderer.DrawButton(e.Graphics, e.Bounds, VisualStyles.PushButtonState.Default)
Else
e.DrawBackground()
End If
With e
If _IsEnabled(.Index) Then
.Graphics.DrawString(TextToDraw, Me.Font, __Brush_Enabled, .Bounds.X, .Bounds.Y)
Else
'.Graphics.FillRectangle(__Brush_Window, .Bounds)
.Graphics.DrawString(TextToDraw, Me.Font, __Brush_Disabled, .Bounds.X, .Bounds.Y)
End If
End With
TextToDraw = Nothing
ButtonRenderer.DrawButton(Me.Parent.CreateGraphics, Me.ClientRectangle, VisualStyles.PushButtonState.Default)
'MyBase.OnDrawItem(e)
End Sub
And here's the result:
Replacing Me.Parent.CreateGraphics with e.Graphics got me this:
And doing the above + replacing Me.ClientRectangle with e.Bounds got me this:
Can anyone point me whose Graphics I must use for the ButtonRenderer.DrawButton method?
PS: The bluish border is due to my using PushButtonState.Default instead of PushButtonState.Normal
I Found An Answer! (see below)
I forgot where I found the answer... I'll edit this answer when I remember.
But apparently, I need to set the Systems.Windows.Forms.ControlStyles flags. Especially the ControlStyles.UserPaint flag.
So, my New() now looks like this:
Private _ButtonArea as New Rectangle
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
MyBase.SetStyle(ControlStyles.Opaque Or ControlStyles.UserPaint, True)
MyBase.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
MyBase.DropDownStyle = ComboBoxStyle.DropDownList
' Cache the button's modified ClientRectangle (see Note)
With _ButtonArea
.X = Me.ClientRectangle.X - 1
.Y = Me.ClientRectangle.Y - 1
.Width = Me.ClientRectangle.Width + 2
.Height = Me.ClientRectangle.Height + 2
End With
End Sub
And now I can hook into the OnPaint event:
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
If Me.DroppedDown Then
ButtonRenderer.DrawButton(Me.CreateGraphics, _ButtonArea, VisualStyles.PushButtonState.Pressed)
Else
ButtonRenderer.DrawButton(Me.CreateGraphics, _ButtonArea, VisualStyles.PushButtonState.Normal)
End If
MyBase.OnPaint(e)
End Sub
Note: Yes, the _ButtonArea rectangle must be enlarged by 1 pixel to all directions (up, down, left, right), or else there will be a 1-pixel 'perimeter' around the ButtonRenderer that shows garbage. Made me crazy for awhile until I read that I must enlarge the Control's rect for ButtonRenderer.
I had this problem myself and the reply by pepoluan got me started. I still think a few things are missing in order to get a ComboBox with looks and behavior similar to the standard ComboBox with DropDownStyle=DropDownList though.
DropDownArrow
We also need to draw the DropDownArrow. I played around with the ComboBoxRenderer, but it draws a dark border around the area of the drop down arrow so that didn't work.
My final solution was to simply draw a similar arrow and render it onto the button in the OnPaint method.
Hot Item Behavior
We also need to ensure our ComboBox has a hot item behavior similar to the standard ComboBox. I don't know of any simple and reliable method to know when a mouse is no longer above the control. Therefore I suggest using a Timer that checks at each tick whether the mouse is still over the control.
Edit
Just added a KeyUp event handler to make sure the control would update correctly when a selection was made using the keyboard. Also made a minor correction of where the text was rendered, to ensure it is more similar to the vanilla combobox' text positioning.
Below is the full code of my customized ComboBox. It allows you to display images on each item and is always rendered as in the DropDownList style, but hopefully it should be easy to accommodate the code to your own solution.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace CustomControls
{
/// <summary>
/// This is a special ComboBox that each item may conatins an image.
/// </summary>
public class ImageComboBox : ComboBox
{
private static readonly Size arrowSize = new Size(18, 20);
private bool itemIsHot;
/* Since properties such as SelectedIndex and SelectedItems may change when the mouser is hovering over items in the drop down list
* we need a property that will store the item that has been selected by comitted selection so we know what to draw as the selected item.*/
private object comittedSelection;
private readonly ImgHolder dropDownArrow = ImgHolder.Create(ImageComboBox.DropDownArrow());
private Timer hotItemTimer;
public Font SelectedItemFont { get; set; }
public Padding ImageMargin { get; set; }
//
// Summary:
// Gets or sets the path of the property to use as the image for the items
// in the System.Windows.Forms.ListControl.
//
// Returns:
// A System.String representing a single property name of the System.Windows.Forms.ListControl.DataSource
// property value, or a hierarchy of period-delimited property names that resolves
// to a property name of the final data-bound object. The default is an empty string
// ("").
//
// Exceptions:
// T:System.ArgumentException:
// The specified property path cannot be resolved through the object specified by
// the System.Windows.Forms.ListControl.DataSource property.
[DefaultValue("")]
[Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
public string ImageMember { get; set; }
public ImageComboBox()
{
base.SetStyle(ControlStyles.Opaque | ControlStyles.UserPaint, true);
//All the elements in the control are drawn manually.
base.DrawMode = DrawMode.OwnerDrawFixed;
//Specifies that the list is displayed by clicking the down arrow and that the text portion is not editable.
//This means that the user cannot enter a new value.
//Only values already in the list can be selected.
this.DropDownStyle = ComboBoxStyle.DropDownList;
//using DrawItem event we need to draw item
this.DrawItem += this.ComboBoxDrawItemEvent;
this.hotItemTimer = new Timer();
this.hotItemTimer.Interval = 250;
this.hotItemTimer.Tick += this.HotItemTimer_Tick;
this.MouseEnter += this.ImageComboBox_MouseEnter;
this.KeyUp += this.ImageComboBox_KeyUp;
this.SelectedItemFont = this.Font;
this.ImageMargin = new Padding(4, 4, 5, 4);
this.SelectionChangeCommitted += this.ImageComboBox_SelectionChangeCommitted;
this.SelectedIndexChanged += this.ImageComboBox_SelectedIndexChanged;
}
private static Image DropDownArrow()
{
var arrow = new Bitmap(8, 4, PixelFormat.Format32bppArgb);
using (Graphics g = Graphics.FromImage(arrow))
{
g.CompositingQuality = CompositingQuality.HighQuality;
g.FillPolygon(Brushes.Black, ImageComboBox.CreateArrowHeadPoints());
}
return arrow;
}
private static PointF[] CreateArrowHeadPoints()
{
return new PointF[4] { new PointF(0, 0), new PointF(7F, 0), new PointF(3.5F, 3.5F), new PointF(0, 0) };
}
private static void DrawComboBoxItem(Graphics g, string text, Image image, Rectangle itemArea, int itemHeight, int itemWidth, Padding imageMargin
, Brush brush, Font font)
{
if (image != null)
{
// recalculate margins so image is always approximately vertically centered
int extraImageMargin = itemHeight - image.Height;
int imageMarginTop = Math.Max(imageMargin.Top, extraImageMargin / 2);
int imageMarginBotttom = Math.Max(imageMargin.Bottom, extraImageMargin / 2);
g.DrawImage(image, itemArea.X + imageMargin.Left, itemArea.Y + imageMarginTop, itemHeight, itemHeight - (imageMarginBotttom
+ imageMarginTop));
}
const double TEXT_MARGIN_TOP_PROPORTION = 1.1;
const double TEXT_MARGIN_BOTTOM_PROPORTION = 2 - TEXT_MARGIN_TOP_PROPORTION;
int textMarginTop = (int)Math.Round((TEXT_MARGIN_TOP_PROPORTION * itemHeight - g.MeasureString(text, font).Height) / 2.0, 0);
int textMarginBottom = (int)Math.Round((TEXT_MARGIN_BOTTOM_PROPORTION * itemHeight - g.MeasureString(text, font).Height) / 2.0, 0);
//we need to draw the item as string because we made drawmode to ownervariable
g.DrawString(text, font, brush, new RectangleF(itemArea.X + itemHeight + imageMargin.Left + imageMargin.Right, itemArea.Y + textMarginTop
, itemWidth, itemHeight - textMarginBottom));
}
private string GetDistplayText(object item)
{
if (this.DisplayMember == string.Empty) { return item.ToString(); }
else
{
var display = item.GetType().GetProperty(this.DisplayMember).GetValue(item).ToString();
return display ?? item.ToString();
}
}
private Image GetImage(object item)
{
if (this.ImageMember == string.Empty) { return null; }
else { return item.GetType().GetProperty(this.ImageMember).GetValue(item) as Image; }
}
private void ImageComboBox_SelectionChangeCommitted(object sender, EventArgs e)
{
this.comittedSelection = this.Items[this.SelectedIndex];
}
private void HotItemTimer_Tick(object sender, EventArgs e)
{
if (!this.RectangleToScreen(this.ClientRectangle).Contains(Cursor.Position)) { this.TurnOffHotItem(); }
}
private void ImageComboBox_KeyUp(object sender, KeyEventArgs e)
{
this.Invalidate();
}
private void ImageComboBox_MouseEnter(object sender, EventArgs e)
{
this.TurnOnHotItem();
}
private void ImageComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (!this.DroppedDown)
{
if (this.SelectedIndex > -1) { this.comittedSelection = this.Items[this.SelectedIndex]; }
else { this.comittedSelection = null; }
}
}
private void TurnOnHotItem()
{
this.itemIsHot = true;
this.hotItemTimer.Enabled = true;
}
private void TurnOffHotItem()
{
this.itemIsHot = false;
this.hotItemTimer.Enabled = false;
this.Invalidate(this.ClientRectangle);
}
/// <summary>
/// Draws overridden items.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ComboBoxDrawItemEvent(object sender, DrawItemEventArgs e)
{
//Draw backgroud of the item
e.DrawBackground();
if (e.Index != -1)
{
Brush brush;
if (e.State.HasFlag(DrawItemState.Focus) || e.State.HasFlag(DrawItemState.Selected)) { brush = Brushes.White; }
else { brush = Brushes.Black; }
object item = this.Items[e.Index];
ImageComboBox.DrawComboBoxItem(e.Graphics, this.GetDistplayText(item), this.GetImage(item), e.Bounds, this.ItemHeight, this.DropDownWidth
, new Padding(0, 1, 5, 1), brush, this.Font);
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// define the area of the control where we will write the text
var topTextRectangle = new Rectangle(e.ClipRectangle.X - 1, e.ClipRectangle.Y - 1, e.ClipRectangle.Width + 2, e.ClipRectangle.Height + 2);
using (var controlImage = new Bitmap(e.ClipRectangle.Width, e.ClipRectangle.Height, PixelFormat.Format32bppArgb))
{
using (Graphics ctrlG = Graphics.FromImage(controlImage))
{
/* Render the control. We use ButtonRenderer and not ComboBoxRenderer because we want the control to appear with the DropDownList style. */
if (this.DroppedDown) { ButtonRenderer.DrawButton(ctrlG, topTextRectangle, PushButtonState.Pressed); }
else if (this.itemIsHot) { ButtonRenderer.DrawButton(ctrlG, topTextRectangle, PushButtonState.Hot); }
else { ButtonRenderer.DrawButton(ctrlG, topTextRectangle, PushButtonState.Normal); }
// Draw item, if any has been selected
if (this.comittedSelection != null)
{
ImageComboBox.DrawComboBoxItem(ctrlG, this.GetDistplayText(this.comittedSelection), this.GetImage(this.comittedSelection)
, topTextRectangle, this.Height, this.Width - ImageComboBox.arrowSize.Width, this.ImageMargin, Brushes.Black, this.SelectedItemFont);
}
/* Now we need to draw the arrow. If we use ComboBoxRenderer for this job, it will display a distinct border around the dropDownArrow and we don't want that. As an alternative we define the area where the arrow should be drawn, and then procede to draw it. */
var dropDownButtonArea = new RectangleF(topTextRectangle.X + topTextRectangle.Width - (ImageComboBox.arrowSize.Width
+ this.dropDownArrow.Image.Width) / 2.0F, topTextRectangle.Y + topTextRectangle.Height - (topTextRectangle.Height
+ this.dropDownArrow.Image.Height) / 2.0F, this.dropDownArrow.Image.Width, this.dropDownArrow.Image.Height);
ctrlG.DrawImage(this.dropDownArrow.Image, dropDownButtonArea);
}
if (this.Enabled) { e.Graphics.DrawImage(controlImage, 0, 0); }
else { ControlPaint.DrawImageDisabled(e.Graphics, controlImage, 0, 0, Color.Transparent); }
}
}
}
internal struct ImgHolder
{
internal Image Image
{
get
{
return this._image ?? new Bitmap(1, 1); ;
}
}
private Image _image;
internal ImgHolder(Bitmap data)
{
_image = data;
}
internal ImgHolder(Image data)
{
_image = data;
}
internal static ImgHolder Create(Image data)
{
return new ImgHolder(data);
}
internal static ImgHolder Create(Bitmap data)
{
return new ImgHolder(data);
}
}
}