How to set top padding of Entry in Xamarin Forms - c#

In my Xamarin forms application, I need to set a top padding for Entry control in iOS. I created renderers for Entry , but only I am able to set Left and Right padding. Please help me. Following is the my Entry Renderer
public class CustomRenderer: EntryRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged (e);
if (Control != null) {
Control.LeftView = new UIView (new CGRect (0, 0, 15, 0));
Control.LeftViewMode = UITextFieldViewMode.Always;
}
}
}

To accomplish this, we need to do two things:
Increase the height of the Entry in Xamarin.Forms
Create an iOS Custom Renderer to align the text to the bottom of the UITextField
Increase The Entry Height in Xamarin.Forms
In this example, I use a RelativeLayout to set the height of the Padded Entry to 50, Constraint.Constant(50).
using System;
using Xamarin.Forms;
namespace CustomEntrySample
{
public class EntryWithTopPadding : Entry
{
}
public class App : Application
{
public App()
{
var normalEntry = new Entry
{
Text = "This Entry Has Normal Padding",
BackgroundColor = Color.Lime
};
var paddedEntry = new EntryWithTopPadding
{
Text = "This Entry Has Extra Top Padding",
BackgroundColor = Color.Aqua
};
var mainLayout = new RelativeLayout();
mainLayout.Children.Add(
normalEntry,
Constraint.Constant(0),
Constraint.RelativeToParent(parent => parent.Y + 10),
Constraint.RelativeToParent(parent => parent.Width)
);
mainLayout.Children.Add(
paddedEntry,
Constraint.Constant(0),
Constraint.RelativeToView(normalEntry, (parent, view) => view.Y + view.Height + 10),
Constraint.RelativeToParent(parent => parent.Width),
Constraint.Constant(50)
);
MainPage = new NavigationPage(
new ContentPage
{
Title = "Title",
Content = mainLayout,
Padding = new Thickness(10, 0, 10, 0)
}
);
}
}
}
Create Custom Renderer to Align Text to the Bottom of UITextField
Set the VerticalAlignment property of the UITextField to UIControlContentVerticalAlignment.Bottom
using UIKit;
using CustomEntrySample;
using CustomEntrySample.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(EntryWithTopPadding), typeof(EntryWithTopPaddingCustomRenderer))]
namespace CustomEntrySample.iOS
{
public class EntryWithTopPaddingCustomRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if(Control == null)
return;
Control.VerticalAlignment = UIControlContentVerticalAlignment.Bottom;
}
}
}

Use this for setting padding to an entry cell:
Padding in IOS :
Control.LeftView = new UIView(new CGRect(0,15,0,0));
Control.LeftViewMode = UITextFieldViewMode.Always;
Control.RightView = new UIView(new CGRect(0,10, 0, 0));
Control.RightViewMode = UITextFieldViewMode.Always;
Padding in Android:
Control.SetPadding(0, 10, 0, 0);
Accordingly, you can set the value to make text start from specific position.

Related

First tab icon not resizing iOS TabbedRenderer

I am working on iOS Tabs using custom TabbedRenderer, In renderer page I am resizing & setting icons. But for the first Tab icon not getting resized rest of all tabs setting icons fine.
public class CustomTabRenderer_iOS : TabbedRenderer
{
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
foreach (var item in TabBar.Items)
{
item.Image = GetTabIcon(item.Title);
}
}
private UIImage GetTabIcon(string title)
{
UITabBarItem item = null;
switch (title)
{
case "Dairy":
item = new UITabBarItem("Dairy", UIImage.FromFile("dairy"), 0);
break;
case "My kid":
item = new UITabBarItem("My kid",UIImage.FromFile("kid"),0);
break;
case "Events":
item = new UITabBarItem("Events", UIImage.FromFile("events"), 0);
break;
case "About":
item = new UITabBarItem("About", UIImage.FromFile("about"), 0);
break;
}
var img = (item != null) ? UIImage.FromImage(item.SelectedImage.CGImage, item.SelectedImage.CurrentScale, item.SelectedImage.Orientation) : new UIImage();
var imgR = ResizeImage(img, 20, 20);
return imgR;
}
public UIImage ResizeImage(UIImage sourceImage, float width, float height)
{
UIGraphics.BeginImageContext(new SizeF(width, height));
sourceImage.Draw(new RectangleF(0, 0, width, height));
var resultImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();
return resultImage;
}
}
Below is TabbedPage from PCL project
<Shared:MyTabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:edTheSIS"
x:Class="edTheSIS.ParentDashboard"
xmlns:Shared="clr-namespace:edTheSIS.Shared;assembly=edTheSIS">
<local:DairyTabPage Icon="dairy" HeightRequest="10" WidthRequest="10" ></local:DairyTabPage>
<local:MykidTab Icon="kid" ></local:MykidTab>
<local:Events Icon="events"></local:Events>
<local:About Icon="about"></local:About>
</Shared:MyTabbedPage>
See below screenshot
second screenshot below
According to the iOS Human Interface Guidelines, the images should be sizes as shown in the table underneath for a tab bar.
If you size the icons accordingly, they should always show right. There would be no reason to resize in code.
When you still want to use the code to resize an icon, update your ViewWillLayoutSubviews method like this, also setting the SelectedImage property:
public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
foreach (var item in TabBar.Items)
{
item.Image = GetTabIcon(item.Title);
item.SelectedImage = GetTabIcon(item.Title);
}
}
The tab bar icon size on iOS can be set with the UITabBarItem.ImageInset in a custom TabbedRenderer
[assembly: ExportRenderer(typeof(TabbedPage), typeof(CustomTabbedPageRenderer))]
namespace AppNameSpace.iOS.Renderers
{
class CustomTabbedPageRenderer : TabbedRenderer
{
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
if (Element is TabbedPage)
if (TabBar?.Items != null)
foreach (var item in TabBar.Items)
item.ImageInsets = new UIEdgeInsets(16, 16, 16, 16);
}
}
}

Create multiple textbox dynamically in single form using c#?

My aim is to create a .dll file dynamically having TextBox,Button which can be used by anyone in a program using Visual C#.
It would get created in Class Library, No WFA tools would get used.
I need help in creating a form which can generate multiple TextBox according to the attributes provided by the user.
1)No of TextBox
2)Location
3)Size etc
Code
CLASS.CS
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
namespace Forms
{
public class TextForm : Form
{
public TextBox txtBox1;
public TextForm(int a, int b, int c, int d, string e)
{
Form f1 = new Form();
txtBox1 = new TextBox();
txtBox1.Visible = true;
//f1.ActiveControl=txtBox1;
f1.Controls.Add(txtBox1);
txtBox1.Focus();
f1.Visible = true;
txtBox1.Size = new Size(a, b);
txtBox1.Location = new Point(c, d);
txtBox1.Text = (e).ToString();
this.Controls.Add(txtBox1);
txtBox1.Visible = true;
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
}
}}
PROGRAM.CS
using System;
using Forms;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
TextForm Box1 = (new TextForm(150, 14, 20, 32, "This is a TextBox 1"));
TextForm Box2 = (new TextForm(180, 34, 40, 52, "This is a TextBox 2"));
}}}
What should be the code?
The problem is that you are creating a Form for each TextBox. This is not what you want, provided that you plan to have forms with multiple text boxes.
I see two possibilities: you either want to create a) a textbox that you can easily add to your form, or b) a form with textboxes.
public class TextInput : Form
{
public TextBox TxtBox {
get; private set;
}
public Control Container {
get; private set;
}
public TextInput(Control c, int a, int b, int c, int d, string e)
{
this.Container = c;
this.TxtBox = new TextBox();
var txtBox1 = this.TxtBox;
txtBox1.Visible = true;
c.Controls.Add(txtBox1);
txtBox1.Focus();
txtBox1.Size = new Size(a, b);
txtBox1.Location = new Point(c, d);
txtBox1.Text = (e).ToString();
txtBox1.Visible = true;
}
}
You'd use this as follows:
var f = new Form();
var txtBox1 = new TextInput( f, 100, 25, 10, 10, "Name" );
var txtBox1 = new TextInput( f, 100, 25, 10, 50, "Age" );
var txtBox1 = new TextInput( f, 100, 25, 10, 100, "Address" );
var txtBox1 = new TextInput( f, 100, 25, 10, 150, "Phone" );
The second possibility is much more interesting, in my opinion. You want to create a special Form that automatically adds text boxes as soon a you call a simple method. I'm going to simplify your code, though. It is not a good idea (at all), to use absolute positioning in your forms.
The following creates a form with text boxes and their labels. The textboxes occupy the whole width of the form. This is achieved by using a TableLayoutPanel in which a Panel subPanel is used for each row.
This subPanel holds a label and a text box.
public class InputForm: Form {
public InputForm()
{
this.Panel = new TableLayoutPanel{ Dock = DockStyle.Fill };
this.textBoxes = new List<TextBox>();
this.Controls.Add( this.Panel );
}
public TextBox AddTextBox(string label)
{
var subPanel = new Panel { Dock = DockStyle.Top };
var lblLabel = new Label { Text = label, Dock = DockStyle.Left };
var tbEdit = new TextBox{ Dock = DockStyle.Fill };
subPanel.Controls.Add( tbEdit );
subPanel.Controls.Add( lblLabel );
this.Panel.Controls.Add( subPanel );
return tbEdit;
}
public TableLayoutPanel Panel {
get; private set;
}
public TextBox[] TextBoxes {
get {
return this.textBoxes.ToArray();
}
}
private List<TextBox> textBoxes;
}
You can use this with the following simple code:
var form = new InputForm();
var tbName = form.AddTextBox( "Name" );
var tbAge = form.AddTextBox( "Age" );
var tbAddress = form.AddTextBox( "Address" );
form.Show();
Application.Run( form );
If you'd like to give a few attributes to the text boxes to be created (colors, font, bold...), then you have two ways. The first one is to add parameters to the AddTextBox() method, though that would not scalate well as long as the number of attributes grows. The alternative is to create a TextBoxAttributes class, which will hold the configuring attributes for a given TextBox.
public class InputForm: Form {
public class TextBoxAttributes {
public TextBoxAttributes() {
this.ForeColor = DefaultForeColor;
this.BackColor = DefaultBackColor;
this.Font = DefaultFont;
}
public Color ForeColor {
get; set;
}
public Color BackColor {
get; set;
}
public Font Font {
get; set;
}
public bool Bold {
get {
return this.Font.Bold;
}
set {
var style = FontStyle.Regular;
if ( value ) {
style = FontStyle.Bold;
}
this.Font = new Font( this.Font, style );
}
}
public bool Italic {
get {
return this.Font.Bold;
}
set {
var style = FontStyle.Regular;
if ( value ) {
style = FontStyle.Italic;
}
this.Font = new Font( this.Font, style );
}
}
public bool Underline {
get {
return this.Font.Bold;
}
set {
var style = FontStyle.Regular;
if ( value ) {
style = FontStyle.Underline;
}
this.Font = new Font( this.Font, style );
}
}
public float FontSize {
get {
return this.Font.Size;
}
set {
this.Font = new Font( this.Font.FontFamily, value );
}
}
}
// ... more things...
public TextBox AddTextBox(string label)
=> this.AddTextBox( label, new TextBoxAttributes() );
public TextBox AddTextBox(string label, TextBoxAttributes attr)
{
var subPanel = new Panel { Dock = DockStyle.Top };
var lblLabel = new Label { Text = label, Dock = DockStyle.Left };
var tbEdit = new TextBox{
Dock = DockStyle.Fill,
ForeColor = attr.ForeColor,
BackColor = attr.BackColor,
Font = attr.Font
};
subPanel.Controls.Add( tbEdit );
subPanel.Controls.Add( lblLabel );
this.Panel.Controls.Add( subPanel );
return tbEdit;
}
// ... more things...
}
The main code would be:
public static void Main()
{
var form = new InputForm();
var tbName = form.AddTextBox( "Name", new InputForm.TextBoxAttributes {
ForeColor = Color.Yellow,
BackColor = Color.Blue
});
var tbAge = form.AddTextBox( "Age", new InputForm.TextBoxAttributes {
ForeColor = Color.Green,
BackColor = Color.Black,
Bold = true
});
var tbAddress = form.AddTextBox( "Address" );
form.Show();
Application.Run( form );
}
Hope this helps.
If you are willing to switch to WPF this will become a lot easier, since you can profit from Autolayout and Bindings.
You could easily switch the Wrappanel for a StackPanel or a DockPanel.
The class will create a View based on the public properties of the handed over object. You might have to add additional Types to the Types-Dictionary. In my case those two where sufficient.
Create a Property of the DynamicControl and Bind to it in XAML.
XAML:
<ContentPresenter Content="{Binding Path=DynView}" />
public ViewModel
{
public UserControl DynView {get; private set};
private ModelType _model;
public ViewModel(ModelType model)
{
_model = model;
DynView = new DynamicControl<ModelType>(_model);
}
}
public class DynamicControl<T> : UserControl
{
static DynamicControl()
{
Types[typeof(bool)] = (binding) =>
{
CheckBox cb = new CheckBox();
cb.SetBinding(CheckBox.IsCheckedProperty,binding);
return cb;
};
Types[typeof(String)] = (binding) =>
{
TextBox tb = new TextBox();
tb.SetBinding(TextBox.TextProperty, binding);
return tb;
};
// add additional Types if necessary
}
private T _model;
public DynamicControl(T model)
{
_model = model;
WrapPanel wp = new WrapPanel();
foreach (PropertyInfo pi in model.GetType().GetProperties())
{
Grid g = new Grid();
g.Margin = new Thickness(5, 5, 25, 5);
g.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
g.ColumnDefinitions.Add(new ColumnDefinition());
g.ColumnDefinitions.Add(new ColumnDefinition());
g.RowDefinitions.Add(new RowDefinition());
TextBlock tb = new TextBlock() { Text = pi.Name };
tb.VerticalAlignment = VerticalAlignment.Center;
Grid.SetColumn(tb, 0);
Grid.SetRow(tb, 0);
g.Children.Add(tb);
System.Windows.FrameworkElement uie = GetUiElement(pi);
uie.Margin = new Thickness(10, 0, 0, 0);
uie.VerticalAlignment = VerticalAlignment.Center;
Grid.SetColumn(uie, 1);
Grid.SetRow(uie, 0);
g.Children.Add(uie);
wp.Children.Add(g);
}
this.Content = wp;
}
private FrameworkElement GetUiElement(PropertyInfo pi)
{
System.Windows.Data.Binding binding = new System.Windows.Data.Binding(pi.Name);
binding.Source = _model;
Func<System.Windows.Data.Binding, FrameworkElement> func;
FrameworkElement uie = null;
if (Types.TryGetValue(pi.PropertyType, out func))
uie = func(binding);
else
uie = Types[typeof(String)](binding);
return uie;
}
private static Dictionary<Type, Func<System.Windows.Data.Binding, FrameworkElement>> Types = new Dictionary<Type, Func<System.Windows.Data.Binding, FrameworkElement>>();
}
I think you're going at this all wrong. Just define your dimensions and text in advance, put them in a data class, and then feed a list of those data classes to your form constructor so it can construct them all on the fly.
The data class:
public class TextboxInfo
{
public Int32 Width { get; set; }
public Int32 Height { get; set; }
public Int32 X { get; set; }
public Int32 Y { get; set; }
public String Text { get; set; }
public TextboxInfo(Int32 w, Int32 h, Int32 x, Int32 y, String text)
{
this.Width = w;
this.Height = h
this.X = x;
this.Y = y
this.Text = text;
}
}
The code to construct the form:
public class TextForm : Form
{
public TextBox[] TextBoxes
{
get { return _textBoxes.ToArray(); }
}
private List<TextBox> _textBoxes;
public TextForm(TextboxInfo[] textboxes, Int32 padX, Int32 padY)
{
_textBoxes = new List<TextBox>();
Int32 reqWidth = 0;
Int32 reqHeight = 0;
foreach (TextboxInfo tbi in textboxes)
{
reqWidth = Math.Max(reqWidth, tbi.X + tbi.Width);
reqHeight = Math.Max(reqHeight, tbi.Y + tbi.Height);
TextBox txtB = new TextBox();
txtB.Size = new Size(tbi.Width, tbi.Height);
txtB.Location = new Point(tbi.X, tbi.Y);
txtB.Text = tbi.Text;
_textBoxes.Add(txtB);
this.Controls.Add(txtB);
}
// You may want to add some kind of OK button at the end here (based on reqHeight)
// and link that to a click listener that closes the form.
// Don't forget to adjust your reqHeight to the added height of that button!
// ...
// Set form to the minimum needed size according to its elements.
this.Size = new Size(reqWidth + padX, reqHeight + padY);
}
}
The calling code:
class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
TextboxInfo[] info = new TextboxInfo[2];
info[0] = new TextboxInfo(150, 14, 20, 32, "This is a TextBox 1");
info[1] = new TextboxInfo(180, 34, 40, 52, "This is a TextBox 2");
TextForm frm = new TextForm(info, 20, 32);
frm.ShowDialog();
// Now you can access the form's text box values through frm.TextBoxes[i].Text
}
}
Mind you, this whole system may seem useful at first, but consider that none of the text boxes have labels on them. Just starting values.
I've made systems for custom data before in a project I created, to generate a custom save options dialog depending on the chosen file type to save to, since each file type needed specific options.
Realistically, you'd create a form with some kind of description at the top and an OK and a Cancel button at the bottom, with a panel in between which has its vertical scrollbar set to enable-when-needed. Then you can dynamically put different custom controls in there to support different data types, like say, a checkbox, a text field, a numeric field, et cetera. They'll automatically be listed vertically in the list simply by keeping track of each control's height to get the next control's Y-offset, and if they'd exceed the form size the panel will make sure you can scroll down.
All you'd give to the form are objects of a data class like the one I showed, but without positioning data. They'd have a type, to figure out what kind of custom control to create, a description text, a default value to set the input control to, and possibly some kind of initialisation value, for example, to limit the range of a numeric value, or, as in the image I showed, values for a dropdown list.

C# Xamarin Label Padding for iOS app

I know this question has been asked a lot but I've tried the various solutions and can't seem to find one that works. I have a label on my storyboard titled Messages. On button click, different text appears in the label. I need to pad just this label.
I've looked at:
Adding space/padding to a UILabel,
UILabel text margin,
and Resizing a UILabel to accommodate insets.
Also not sure where in my ViewController.cs to put the code. I put it under public partial class ViewController : UIViewController but get errors.
Edit 2:
Okay, at first I did a UITextView but couldn't get vertical align to work so went back to label. This is what I have:
public partial class ViewController : UIViewController
{
public partial class PaddedUILabel : UILabel
{
private UIEdgeInsets EdgeInsets { get; set; }
public PaddedUILabel()
{
EdgeInsets = new UIEdgeInsets(0, 10, 0, 10);
}
public override void DrawText(CoreGraphics.CGRect rect)
{
base.DrawText(EdgeInsets.InsetRect(rect));
}
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
Message.Layer.BorderWidth = 1;
Message.BackgroundColor = UIColor.FromWhiteAlpha(1, 0.88f);
PaddedUILabel _paddedUILabel = Message as PaddedUILabel;
I'm still not getting any padding.
You can either use a UITextView and provide the insets directly (as mentioned in this link) which you can do either in ViewDidLoad or ViewWillAppear, or you can subclass your UILabel and override the DrawText method.
The DrawText method helps manipulate the rectangle in which the text within a label is drawn. I'd suggest you pay around with some values to get started with it.
Something like:
public partial class PaddedUILabel : UILabel
{
//Override draw text here
}
If you are using Xamarin iOS Native, give your label(for example: myLabel) the custom class from story board and retrieve the label as:
PaddedUILabel _paddedUILabel = this.myLabel as PaddedUILabel;
I'm sorry I would have given you the whole code but as of now, don't have access to a Mac environment. Let me know if you need anything else.
For vertical align in TextView follow link.
Cheers
i tried many solutions as discussed here but the actual out coming not what i wanted so i made it like that :
if (this.Padding != default(Thickness))
{
Device.BeginInvokeOnMainThread(() =>
{
if (Parent is Grid)
{
var parentAsGrid = Parent as Grid;
var index = parentAsGrid.Children.IndexOf(this);
parentAsGrid.Children.Remove(this);
Grid marginGrid = new Grid() { BackgroundColor = this.BackgroundColor, HorizontalOptions = this.HorizontalOptions, VerticalOptions = this.VerticalOptions };
var lbl = new Label() { Text = this.Text, TextColor = this.TextColor, BackgroundColor = this.BackgroundColor, HorizontalOptions = this.HorizontalOptions, VerticalOptions = this.VerticalOptions, FontSize = this.FontSize };
lbl.Margin = this.Padding;
if (!parentAsGrid.Children.Contains(this))
{
marginGrid.Children.Add(lbl);
parentAsGrid.Children.Insert(index, marginGrid);
}
}
if (Parent is StackLayout)
{
var parentAsGrid = Parent as StackLayout;
var index = parentAsGrid.Children.IndexOf(this);
parentAsGrid.Children.Remove(this);
Grid marginGrid = new Grid() { BackgroundColor = this.BackgroundColor, HorizontalOptions = this.HorizontalOptions, VerticalOptions = this.VerticalOptions };
var lbl = new Label() { Text = this.Text, TextColor = this.TextColor, BackgroundColor = this.BackgroundColor, HorizontalOptions = this.HorizontalOptions, VerticalOptions = this.VerticalOptions, FontSize = this.FontSize };
lbl.Margin = this.Padding;
if (!parentAsGrid.Children.Contains(this))
{
marginGrid.Children.Add(lbl);
parentAsGrid.Children.Insert(index, marginGrid);
}
}
});

How to display a control which (transparent) BackColor doesn't cover below controls?

I have a custom Control:
public class Temp : Control
{
public Temp(Color col, int x, int y)
{
Size = new Size(x + 10, y + 10);
this.x = x;
this.y = y;
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
BackColor = col;
}
int x, y;
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (var p = new Pen(Color.Black, 3))
{
e.Graphics.DrawLine(p, new Point(10, 10), new Point(x, y));
}
}
}
And from the Load event of my Form I add two of these control to the Controls of a Panel I added as the only control of my Form:
panel1.Controls.Add(new Temp(Color.Red, 50, 50));
panel1.Controls.Add(new Temp(Color.Violet, 10, 100));
This is the output:
As you can see the first control cover the second one, while I'd want to display only the two lines, where the controls background color is transparent.
Note that using a transparent BackColor doesn't work:
panel1.Controls.Add(new Temp(Color.Transparent, 50, 50));
panel1.Controls.Add(new Temp(Color.Violet, 10, 100));
And this is the output:
How can I solve this problem? That is, display only (and completely) both my lines?
Instead of Inheriting from Control, create two points in your custom class.
Sample code given below
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace SOWinForm
{
public partial class Form1 : Form
{
List<Line> lines;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
lines = new List<Line>();
lines.Add(new Line(){ StartPoint = new Point(10,10), EndPoint = new Point(10,100)});
lines.Add(new Line() { StartPoint = new Point(10, 10), EndPoint = new Point(50, 50) });
}
protected override void OnPaint(PaintEventArgs e)
{
foreach (var line in lines)
{
using (var p = new Pen(Color.Black, 3))
{
e.Graphics.DrawLine(p, line.StartPoint, line.EndPoint);
}
}
}
}
public class Line
{
public Point StartPoint {get;set;}
public Point EndPoint { get; set; }
//Add Custom Properties
}
}
When you are setting transparent backcolor that doesn't mean that the background color is transparent but the color of its parent. The parent of first control is the panel(with gray color) so also the color of the control is gray. Set the parent of the first control to be the second one.
valter

Align text vertically with VisualStyleRenderer for VisualStyleElement.ToolTip.Standard.Normal

I'm creating a custom ToolTip that will bold the first line of text if the text is multi-line. I'm also using the VisualStyleRenderer to draw the tool tip correctly with styles. However, when I draw the text (even with TextFormatFlags.VerticalCenter set), it draws at the top of the box. I was going to just bump the bounding box down 2 pixels (which fixed it on Windows 7) but I wasn't 100% sure how portable that would be to another OS. Does anyone know how to draw the text vertically centered correctly?
EDIT: To make this clear, I know this code doesn't bold the first line. I'm trying to first replicate a standard tooltip, and then afterwards do the bolding.
public class BoldedFirstLineToolTip : ToolTip
{
public BoldedFirstLineToolTip()
{
this.OwnerDraw = true;
this.Draw += new DrawToolTipEventHandler(OnDraw);
}
private void OnDraw(object sender, DrawToolTipEventArgs e)
{
// Try to draw using the visual style renderer.
if (VisualStyleRenderer.IsElementDefined(VisualStyleElement.ToolTip.Standard.Normal))
{
var renderer = new VisualStyleRenderer(VisualStyleElement.ToolTip.Standard.Normal);
renderer.DrawBackground(e.Graphics, e.Bounds);
var b = e.Bounds;
// b.Y + 2 // This works when using e.Graphics.DrawString.
renderer.DrawText(e.Graphics, b, e.ToolTipText, false /*drawDisabled*/,
TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter);
}
else
{
// Fall back to non-visual style drawing.
e.DrawBackground();
e.DrawBorder();
e.DrawText();
}
}
}
I've decided to just use padding fixes. I've provided my full solution below. I tested on both XP and Windows 7.
public class BoldedFirstLineToolTip : ToolTip
{
public BoldedFirstLineToolTip()
{
this.OwnerDraw = true;
this.Draw += new DrawToolTipEventHandler(OnDraw);
}
private void OnDraw(object sender, DrawToolTipEventArgs e)
{
// Try to draw using the visual style renderer.
if (VisualStyleRenderer.IsSupported && VisualStyleRenderer.IsElementDefined(VisualStyleElement.ToolTip.Standard.Normal))
{
var bounds = e.Bounds;
var renderer = new VisualStyleRenderer(VisualStyleElement.ToolTip.Standard.Normal);
renderer.DrawBackground(e.Graphics, bounds);
var color = renderer.GetColor(ColorProperty.TextColor);
var text = e.ToolTipText;
using (var textBrush = new SolidBrush(renderer.GetColor(ColorProperty.TextColor)))
using (var font = e.Font)
{
// Fix the positioning of the bounds for the text rectangle.
var rendererBounds = new Rectangle(e.Bounds.X + 6, e.Bounds.Y + 2, e.Bounds.Width - 6 * 2, e.Bounds.Height - 2 * 2);
if (!text.Contains('\n'))
{
renderer.DrawText(e.Graphics, rendererBounds, text);
}
else
{
var lines = text.Split('\n').Select(l => l.Trim());
var first = lines.First();
var otherLines = Environment.NewLine + String.Join(Environment.NewLine, lines.Skip(1).ToArray());
// Draw the first line.
using (var boldFont = new Font(font, FontStyle.Bold))
{
e.Graphics.DrawString(first, boldFont, textBrush, rendererBounds.X - 1, rendererBounds.Y - 1);
}
renderer.DrawText(e.Graphics, rendererBounds, otherLines, false /*drawDisabled*/, TextFormatFlags.Left);
}
}
}
else
{
// Fall back to non-visual style drawing.
e.DrawBackground();
e.DrawBorder();
using (var sf = new StringFormat())
{
sf.LineAlignment = StringAlignment.Center;
e.Graphics.DrawString(e.ToolTipText, SystemFonts.DialogFont, Brushes.Black, e.Bounds, sf);
}
}
}
}

Categories

Resources