How do I implement a tab control with vertical tabs in C#? - winforms

How do I implement a tab control with vertical tabs in C#?

Create an instance of System.Windows.Forms.TabControl (one of the standard container controls for Windows Forms) and set the Alignment property to Left.

First set in properties the Alignment property to Left.
Second set SizeMode property to Fixe.
Third set ItemSize property to prefered size example width :30 height :120.
After that you need to set the DrawMode property to OwnerDrawFixed.
Next step is define a handler for the DrawItem event of TabControl that renders the text from left to right.
Example
In form Designers.cs file
TabControl.DrawItem += new DrawItemEventHandler(tabControl_DrawItem);
Definition for tabControl_DrawItem method:
private void tabControl_DrawItem(Object sender, System.Windows.Forms.DrawItemEventArgs e)
{
Graphics g = e.Graphics;
Brush _textBrush;
// Get the item from the collection.
TabPage _tabPage = TabControl.TabPages[e.Index];
// Get the real bounds for the tab rectangle.
Rectangle _tabBounds = TabControl.GetTabRect(e.Index);
_textBrush = new System.Drawing.SolidBrush(Color.Black);
// Use our own font.
Font _tabFont = new Font("Arial", (float)12.0, FontStyle.Bold, GraphicsUnit.Pixel);
// Draw string. Center the text.
StringFormat _stringFlags = new StringFormat();
_stringFlags.Alignment = StringAlignment.Center;
_stringFlags.LineAlignment = StringAlignment.Center;
g.DrawString(_tabPage.Text, _tabFont, _textBrush, _tabBounds, new StringFormat(_stringFlags));
}
Effect:Ready horizontal tabcontrol
I was based on https://msdn.microsoft.com/en-us/library/ms404305(v=vs.110).aspx

Related

C# UserControl Draw outside of TableLayoutPanel cell

C# .Net 4.5 Winform
On my Form, I have a SplitContainer, and in the right panel, my custom UserControl with a TableLayoutPanel.
As I draw UserControl items in each cell of the TableLayoutPanel, I want to use DrawString to display text on the division of the current cell and the cell above it.
Currently:
The cells' UserControls are docked as 'fill' in each cell of the TableLayoutPanel.
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
AutoScroll = false;
AutoSize = false;
Dock = DockStyle.Fill;
Margin = new Padding(0);
}
I offset the draw rectangle to be half it's height higher:
var topOffset = ClientRectangle.Top - (ClientRectangle.Height / 2);
paintRect = new Rectangle(ClientRectangle.X + Padding.Left,
topOffset + Padding.Top,
ClientRectangle.Width - Padding.Left - Padding.Right,
ClientRectangle.Height - Padding.Top - Padding.Bottom);
I create a new clipping region for graphics, and print out the text:
using (Graphics newGraphics = this.CreateGraphics())
{
newGraphics.SetClip(paintRect);
e.Graphics.SetClip(newGraphics);
e.Graphics.DrawString(
"ABCD 1234",
Font,
new SolidBrush(ForeColor),
paintRect, style);
}
The text gets cut off - see image
The cell UserControl that needs to display text (at it's top, halfway sticking into the control, and the TableLayoutPanel cell, above it. I think I need to change the clipping of those cells to not clip the text, but am unsuccessful.
How can I display the text 'outside' or 'on top' of the 'layer' that the TableLayoutPanel cell containing the UserControl is in, from within the UserControl OnPaint?

How to get a one-to-one match of an Adorner with its adorned element within a ScrollViewer?

wpf
I have a control A, (an inkcanvas), within a Grid within a ScrollViewer. Conrol A is taller then the physical window, so the ScrollViewer correctly adds a vertical scroll bar and the entire control can be viewed by scrolling down. When attaching an Adorner with a control B, (another inkcanvas), to control A, scrolling downward shows the Adorner to be cut off at the bottom of the screen. That is, the Adorner is not completely covering the adorned element and/or is not extended downward when scrolling.
How do I get the Adorner (the control) to completely cover the adorned element and respect the ScrollViewer. (I need a one-to-one match between the pixels of the Adorner control and the adorned element within the ScrollViewer).
TIA
Edit#1: The key line in the Adorner that sets the background of the InkCanvas is
_inkcanvas.Background = CreateGrid();
public InkCanvasTextAdorner(InkCanvas element)
: base(element)
{
_element = element;
_visuals = new VisualCollection(this);
_inkcanvas = new InkCanvas();
_inkcanvas.Background = CreateGrid();
_visuals.Add(_inkcanvas);
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(element);
adornerLayer.Add(this);
}
If an InkCanvas is used (as the above) the Adorner's background is clipped at the bottom. The adorner control, however, does continue to the bottom of the adorned element.
However, if a Canvas is used instead of the InkCanvas, the Adorner's background does extend to the bottom of the adorned element.
What's wrong?
I'm guessing that the difference in the Background property expanding past the physical screen with the Canvas and not the InkCanvas may be because the Canvas inherits from Panel whereas the InkCanvas does not. Based on the finding that the Canvas background does do what I need, I find the below code does accomplish what I want--the canvas allows images from layers beneath it to be seen yet posts a grid of lines overwhich the InkCanvas will accept strokes. All is well :)
public InkCanvasTextAdorner(InkCanvas element)
: base(element)
{
_element = element;
// The VisualCollection has only one visual parent. I.e. InkCanvasTextAdorner is the parent to the VisualCollection.
// By overriding default rendering behavior of the VisualCollection, any kind of control and its children can be placed in the Adorner.
_visuals = new VisualCollection(this);
_inkcanvas = new InkCanvas();
_inkcanvas.Background = Brushes.Transparent;
_canvas = new Canvas();
_canvas.Background = CreateGrid();
_grid = new Grid();
_grid.Children.Add(_canvas);
_grid.Children.Add(_inkcanvas);
// The _grid is a logical child of the VisualCollection of the Adorner. The ArrangeOverride and MeasureOverride will set up the Grid control.
_visuals.Add(_grid); // Adding a single control for display.
// AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(element);
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(element);
adornerLayer.Add(this);
}
Which looks like:

How to dynamically create an object of a custom label class and add it to the canvas in WPF?

I have found a class of Label which adds the functionality of dragging and resizing the Label to it. And I don't know how I can dynamically (like by pressing a button which says "create a label") create an object of that class and add it to the Canvas of my WPF application.
I also want to specify some of it's properties like width, height, name, content etc. before/after adding it to the Canvas.
How is it done the proper way ?
//adding new MyLabel(class of Label)
MyLabel lbl = new MyLabel();
lbl.Text = "test text";
lbl.Height = 27;
lbl.Name="testLabel";
canvas.Children.Add(lbl);
About getting element by name after creating you can read here:
http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.findname.aspx
Create a new Label instance, set some of its properties, set its position on the Canvas and add it to the Canvas' Children collection:
var label = new Label
{
Width = 200,
Height = 50,
Content = "A Label"
};
Canvas.SetLeft(label, 100);
Canvas.SetTop(label, 100);
canvas.Children.Add(label);

WPF TextBox recalculate size

When using a wpf textbox without explicit height and width values, and when there is space available to expand, the textbox resizes as you type.
However when I change the border thickness, it does not recalculate it and for very thick borders, part of the text is covered by the border. How do I explicitly precipitate a recalc?
Coincidently I am using a derived custom textbox class so I should know when the border thickness changes.
This bug must be some optimization gone wrong
Overriding Metadata for BorderThickness or adding a Dependency Property that affects Measure, Arrange or Render don't help
Undocking and Redocking from the parent container had no effect either
Even Undocking from the parent container and Redocking into a new container won't help if the space it is given in the new container is exactly the same as the space that it had in the old container
It seems like the size is only re-calculated once Text, Width, Height or available space changes. I looked around with Reflector but things get pretty complex down there so I couldn't find the source for this.
Here is a small workaround that listens to changes in BorderThickness and in the changed event handler, make a small change to the Width and once it is updated, change it right back
public class MyTextBox : TextBox
{
public MyTextBox()
{
DependencyPropertyDescriptor borderThickness
= DependencyPropertyDescriptor.FromProperty(MyTextBox.BorderThicknessProperty, typeof(MyTextBox));
borderThickness.AddValueChanged(this, OnBorderThicknessChanged);
}
void OnBorderThicknessChanged(object sender, EventArgs e)
{
double width = this.Width;
SizeChangedEventHandler eventHandler = null;
eventHandler = new SizeChangedEventHandler(delegate
{
this.Width = width;
this.SizeChanged -= eventHandler;
});
this.SizeChanged += eventHandler;
this.Width = this.ActualWidth + 0.00000001;
}
}
First of all, this looks like a bug.
If the problem is that dynamic changes of the border thickness are not taken into account, you can perhaps make a workaround by creating a dependency property with AffectsMeasure in FrameworkPropertyMetadata, and bind it to the border thickness. Hope this quirk helps.
If the static setting of the border thickness are not taken into account, you can try to replace the TextBox's default template with your own (correct) version.

Wpf Adorner not responding to interactions

I'm trying to create an overlay in wpf (with darkening background), similar to the ones you can find on the web to popup images.
I would like it to be reusable in more than 1 part of the application, with diffent types of content.
this is the temporary code of the constructor of the adorner class (just to try)
private readonly Grid _grid = new Grid();
public DarkOverlayAdorner(UIElement adornedElement, Object content) :
base(adornedElement)
{
_grid.Background = new SolidColorBrush(Color.FromArgb(99, 0, 0, 0));
IsHitTestVisible = true;
var visual = content as UIElement;
if (visual != null)
_grid.Children.Add(visual);
}
In addition in the class (of course), I have the ovverrides of MeasureOverride and ArrangeOverride to give the adorner the correct size of the adorned element, GetVisualChild, and VisualChildCount...
The problem here is that the adorner is correctly shown, but no events or behaviour are applied on the adorned element. For example:
AdornerLayer layer = AdornerLayer.GetAdornerLayer(textBoxProva);
layer.Add(new DarkOverlayAdorner(textBoxProva, new Button{Content = "prova"}));
The button here is shown, but I can-t click the button and no effects on button mouseover are applied.
I still can't figure out the problem.
Ok, I've lost a lot of time trying to figure out what was the problem.
In the end I found the solution:
If you want the element added to react to events, I think that the element must be bound to the visual tree of the adorner.
The way to do it is to use a VisualCollection, intitialized to the adorner itself:
VisualCollection visualChildren;
FrameworkElement #object;
public DarkOverlayAdorner(UIElement adornedElement) :
base(adornedElement)
{
visualChildren = new VisualCollection(this);
#object = new Button {Content = "prova"};
visualChildren.Add(#object);
}
protected override Visual GetVisualChild(int index)
{
return visualChildren[index];
}
This way the events are correctly routed.
You might want to take a look at the ChildWindow control in the Extended WPF Toolkit. It is a control that pops up a Window with a modal background effect, and you can specify the content to put inside the Window.

Resources