ICSharpCode.TextEditor Vertical Scrolling - c#

Is it possible to configure vertical scrolling in ICSharpCode.TextEditor such that by default no vertical scrollbar is visible. And that only when someone types a lot of lines (beyond current height of this control) that a vertical scrollbar appears automatically. If yes, how?

Its easy to add the function yourself:
1) Goto the namespace ICSharpCode.TextEditor and open the TextAreaControl class. The file location is: C:...\ICSharpCode.TextEditor\Project\Src\Gui\TextAreaControl.cs
2) Add a method to set the visibility of the Horizontal or Vertical scrollbar:
public void ShowScrollBars(Orientation orientation,bool isVisible)
{
if (orientation == Orientation.Vertical)
{
vScrollBar.Visible = isVisible;
}
else
{
hScrollBar.Visible = isVisible;
}
}
3) In the project with the TextEditor, this is how you call the ShowScrollBars() method:
editor.ActiveTextAreaControl.ShowScrollBars(Orientation.Vertical,false);
This code does the trick to show the vertical scrollbar based on the number of text lines:
public TextEditorForm()
{
InitializeComponent();
AddNewTextEditor("New file");
SetSyntaxHighlighting("Mathematica");
editor.ActiveTextAreaControl.TextEditorProperties.IndentationSize = 0;
editor.ActiveTextAreaControl.ShowScrollBars(Orientation.Vertical,false);
editor.TextChanged += new EventHandler(editor_TextChanged);
}
void editor_TextChanged(object sender, EventArgs e)
{
bool isVisible = (editor.ActiveTextAreaControl.GetTotalNumberOfLines > editor.ActiveTextAreaControl.TextArea.TextView.VisibleLineCount);
editor.ActiveTextAreaControl.ShowScrollBars(Orientation.Vertical, isVisible);
}
In the TextAreaControl:
public int GetTotalNumberOfLines()
{
return this.Document.TotalNumberOfLines;
}
ps I'm using this Code Project ICSharpCode-TextEditor project.

Related

How to change BorderColor of a selectable Panel?

I want to change all panel's BorderColor to Color.Lime:
foreach (Control G in GetAllControls (this))
{
Panel p = sender as Panel;
ControlPaint.DrawBorder(e.Graphics, p.DisplayRectangle, Color.Lime, ButtonBorderStyle.Inset);
}
It shows me this error:
Severity Code Description Project File Line Suppression State
Error CS1061 'EventArgs' does not contain a definition for 'Graphics' and no accessible extension method 'Graphics' accepting a first argument of type 'EventArgs' could be found (are you missing a using directive or an assembly reference?)
As a suggestion, use a Custom Control derived from Panel -if you need its features - or from Control, to have a lightweight Control - if you don't require any predefined behavior (except the base Control functionality, that is).
For example, a simple tweak of the Panel class that adds some properties that allow to define:
a Color to assign to the Border
the Size of the Border
the Color of the Border when the Control is selected
means to make the Panel selectable: it will change the Border Color when the Control is entered, you can set TabStop = true and Tab to highlight etc.
To make the Control selectable, some styles are modified when the Selectable property is set.
The Control calls SetStyle() and sets ControlStyles.Selectable, ControlStyles.UserMouse and ControlStyles.StandardClick to true or false, then UpdateStyles(), to force the new style (the Control retrieves the styles from the CreateParams property), then Invalidate() itself (this calls the OnPaint method, which in turn raises the Paint event) to draw the Border in the new state.
Add class named PanelEx to the Project, paste in what's in here (preserving the Namespace, of course), build the Project, find the new Control in the ToolBox and drop is on a Form.
If you want to replace all standard Panel Controls with this one, use the search/replace functions of Visual Studio (CTRL+H) and replace existing standard Panel Type objects (those that need the new behavior) with the PanelEx Type.
NOTE - x64 Projects:
If the main Project needs to target x64, you can add a new Class Library Project that targets AnyCPU to the Solution. Add this Custom Control class (or any other Custom Control you have built, of course) to this Library. Also add a Reference to the System.Windows.Forms and System.Drawing assemblies.
Rebuild the Solution and Visual Studio will add to the Toolbox all the Controls found in the library.
Nobody will complain when you then drop your Controls on a Form from the Toolbox.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
[DesignerCategory("code")]
public class PanelEx : Panel
{
private Color m_BorderColorSel = Color.Transparent;
private Color m_BorderColor = Color.Transparent;
private bool m_Selectable = false;
private bool m_Selected = false;
private int m_BorderSize = 1;
public PanelEx() { }
public Color BorderColor { get => m_BorderColor;
set {
if (value == m_BorderColor) return;
m_BorderColor = value;
Invalidate();
}
}
public int BorderSize {
get => m_BorderSize;
set {
if (value == m_BorderSize) return;
m_BorderSize = value;
Invalidate();
}
}
public bool Selectable {
get => m_Selectable;
set {
if (value == m_Selectable) return;
m_Selectable = value;
SetStyle(ControlStyles.Selectable | ControlStyles.UserMouse | ControlStyles.StandardClick, value);
UpdateStyles();
Invalidate();
}
}
public Color BorderColorSelected {
get => m_BorderColorSel;
set {
m_BorderColorSel = value;
if (!Selectable || value == m_BorderColorSel) return;
Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
Color penColor = m_Selectable && m_Selected ? m_BorderColorSel : m_BorderColor;
int rectOffset = BorderSize / 2;
using (Pen pen = new Pen(penColor, BorderSize)) {
var rect = new Rectangle(rectOffset, rectOffset, ClientSize.Width - BorderSize, ClientSize.Height - BorderSize);
e.Graphics.DrawRectangle(pen, rect);
}
base.OnPaint(e);
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
OnEnter(e);
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
OnLeave(e);
}
protected override void OnEnter(EventArgs e)
{
base.OnEnter(e);
m_Selected = true;
Invalidate();
}
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
m_Selected = false;
Invalidate();
}
}

Custom panel that fills itself with only one child with a padding

I am trying to draw a border that looks exactly like the one a ListView on Windows 10 has. I am doing this because, Fixed3D looks sunken, and FixedSingle looks different from the aforementioned border.
With the Magnifier, I found out that the Windows 10 border is two-pixel wide. So, my idea was create a custom panel that has no borders but draws two-pixel-wide rectangles on its client area, and fit a child, whose border is set to none, inside the rectangles. I have tried the following, but it does not work well with the Designer, nor does it work at all.
What is wrong?
class CustomBorder:Panel
{
protected override void OnControlAdded(ControlEventArgs e)
{
if (Controls.Count == 2)
{
Controls.Remove(e.Control);
return;
}
base.OnControlAdded(e);
}
protected override void OnLayout(LayoutEventArgs levent)
{
var child = levent.AffectedControl;
if (levent.AffectedProperty == "Bounds")
{
FillChild(child);
}
base.OnLayout(levent);
}
void FillChild(Control child)
{
const int padding = 2;
var childSize = new Size(Bounds.Width - padding * 2, Bounds.Height - padding * 2);
child.SetBounds(padding, padding, childSize.Width, childSize.Height);
}
}
The description of AffectedControl is "Gets the child control affected by the change". But weirdly, it was actually the panel itself. So I changed it like the following.
protected override void OnLayout(LayoutEventArgs levent)
{
if (levent.AffectedProperty == "Bounds")
{
if(HasChildren)
FillChild(Controls[0]);
}
base.OnLayout(levent);
}
It works, but there is some flickering when resizing the panel, probably because it is actually two controls, not one as the native border is.

How to Programmatically Scroll a Panel

I have a System.Windows.Forms.Panel with some content.
I am trying to programmatically scroll the panel (vertically) either up or down.
I have tried setting the AutoScrollPosition property to a new Point on the panel but that doesn't seem to do it.
I have the AutoScroll property set to true.
I even tried to set the VerticalScroll.Value twice as suggested here, but that doesn't seem to work either.
This is what I am currently doing:
//I have tried passing both positive and negative values.
panel.AutoScrollPosition = new Point(5, 10);
The X and Y values on AutoScrollPosition remain 0 and 0.
Any help or direction on this would be greatly appreciated it.
Thanks in advance,
Marwan
Here is a solution. I guess you can scroll your Panel by arbitrary position using Win32 however there is a simple trick to help you achieve your requirement here:
public void ScrollToBottom(Panel p){
using (Control c = new Control() { Parent = p, Dock = DockStyle.Bottom })
{
p.ScrollControlIntoView(c);
c.Parent = null;
}
}
//use the code
ScrollToBottom(yourPanel);
Or use extension method for convenience:
public static class PanelExtension {
public static void ScrollToBottom(this Panel p){
using (Control c = new Control() { Parent = p, Dock = DockStyle.Bottom })
{
p.ScrollControlIntoView(c);
c.Parent = null;
}
}
}
//Use the code
yourPanel.ScrollToBottom();
UPDATE
If you want to set the exact position, modifying the code above a little can help:
//This can help you control the scrollbar with scrolling up and down.
//The position is a little special.
//Position for scrolling up should be negative.
//Position for scrolling down should be positive
public static class PanelExtension {
public static void ScrollDown(this Panel p, int pos)
{
//pos passed in should be positive
using (Control c = new Control() { Parent = p, Height = 1, Top = p.ClientSize.Height + pos })
{
p.ScrollControlIntoView(c);
}
}
public static void ScrollUp(this Panel p, int pos)
{
//pos passed in should be negative
using (Control c = new Control() { Parent = p, Height = 1, Top = pos})
{
p.ScrollControlIntoView(c);
}
}
}
//use the code, suppose you have 2 buttons, up and down to control the scrollbar instead of clicking directly on the scrollbar arrows.
int i = 0;
private void buttonUp_Click(object sender, EventArgs e)
{
if (i >= 0) i = -1;
yourPanel.ScrollUp(i--);
}
private void buttonDown_Click(object sender, EventArgs e)
{
if (i < 0) i = 0;
yourPanel.ScrollDown(i++);
}
Another solution you may want to use is using Panel.VerticalScroll.Value. However I think you need more research to make it work as you expect. Because I can see once changing the Value, the scrollbar position and control position don't sync well. Notice that Panel.VerticalScroll.Value should be between Panel.VerticalScroll.Minimum and Panel.VerticalScroll.Maximum.
This surprisingly works! NOTE THE MINUS SIGN in the code. There is strange behavior in setting scroll position. If you set the position to exact value (50), it goes negative when you read it next time (-50). So you have to invert it before setting new scroll value.
Scroll down:
private void ButtonScrollDown_OnClick(object sender, EventArgs e)
{
Point current = yourScrollPanel.AutoScrollPosition;
Point scrolled = new Point(current.X, -current.Y + 10);
yourScrollPanel.AutoScrollPosition = scrolled;
}
Scroll up similarly, (-current.Y - 10)
If you have a class that derives from Panel, then call these two protected methods to scroll the panel:
// The bottom is off screen; scroll down. These coordinates must be negative or zero.
SetDisplayRectLocation(0, AutoScrollPosition.Y - item.BoundingRect.Bottom + ClientRectangle.Bottom);
AdjustFormScrollbars(true);
In my example, item.BoundingRect.Bottom is the Y coordinate of the bottom of a thumbnail, and I need to scroll the panel down so that the whole thumbnail is visible.
#King King's solution of creating a temporary Control just so that scrolling could be done seemed "heavy" to me. And #Hans Passant's suggestion of setting AutoScrollMinSize and AutoScrollPosition didn't work for me.
Leave AutoScroll to its default value of 'true'.
Try this:-
panel.ScrollControlIntoView(childcontrol);
This should work. childcontrol is the particular control that you want to show in your display area.
Setting the value of the HorizontalScroll property and then using the method ScrollControlIntoView works for me:
lpanel.HorizontalScroll.Value = 100;
lpanel.ScrollControlIntoView(lpanel);
Use #King King Answered Code and if you want to hide horizontal and vertical scroll bar, just apply the below code in the constructor or initialization.
yourPanel.AutoScroll = false;
yourPanel.HorizontalScroll.Maximum = 0;
yourPanel.HorizontalScroll.Visible = false;
yourPanel.VerticalScroll.Maximum = 0;
yourPanel.VerticalScroll.Visible = false;
yourPanel.AutoScroll = true;
I had an issue where I couldnt get my panel to scroll back to top . I tried many things to try and get the panel to scroll back to the top after populating it with many controls.
Nomatter what I did it always put the VScroll bar to the bottom.
After exhaustive testing I found it was because my controls had the TabStop property set to true (default on user controls) was causing the issue.
Setting TabStop to false fixed it.
Create an control that sits slightly outside the visible area (so -1 at the top and clientsize+1 ) and then call ScrollControlIntoView:
public static class PanelExtension {
public static void ScrollDown(this Panel p)
{
using (Control c = new Control() { Parent = p, Height = 1, Top = p.ClientSize.Height + 1 })
{
p.ScrollControlIntoView(c);
}
}
public static void ScrollUp(this Panel p )
{
using (Control c = new Control() { Parent = p, Height = 1, Top = -1})
{
p.ScrollControlIntoView(c);
}
}
}
//use the code, suppose you have 2 buttons, up and down to control the scrollbar instead of clicking directly on the scrollbar arrows.
private void buttonUp_Click(object sender, EventArgs e)
{
yourPanel.ScrollUp();
}
private void buttonDown_Click(object sender, EventArgs e)
{
yourPanel.ScrollDown();
}
with yourpanel.SetAutoScrollMargin(1, 1); you can set very fine scrolling steps and then take a timer to call the srolling when buttons are down

Enable a button to be clicked at design-time in Visual Studio?

My setting:
I've got a C# application (.NET 3.5) in Visual Studio 2008. No chance to switch to WPF or whatsoever :).
My app contains a custom control (a button class derived from Windows.Forms.Button) that acts as a replacement for the Windows.Forms.TabControl. I can associate these buttons with one another and each button can be associated with one control that it is dealing with (usually some sort of Windows.Forms.Panel). It looks something like this:
public class TabButton : System.Windows.Forms.Button
{
// ...
protected override void OnClick(EventArgs e)
{
base.OnClick(e);
this.myAssociatedControl.Visible = true;
this.tellMyBuddiesToHideTheirControls();
}
// ...
}
Basically it is just about clicking a button, showing its bound control and having the controls bound to the associated buttons disappear - just like the TabControl, but the approach is easily designable and I can place the buttons far from their content panels.
The problem:
This works pretty well at runtime, but the usage at design time is arguably odd: With the mouse, find a control that´s belonging to the group and run a series of <Send To Back>s until the desired control is visible.
The question:
Is there a way to tell the VS designer to evaluate the clicks on the buttons at design time like it does with the TabControl so that I can switch the tabs just by clicking them like I would at runtime?
I've been searching for quite a while now. There are some articles here at SO but they only seem to cover adding additional attributes to the properties designer.
Edith says:
By request, an answer to my own question ...
This is the solution that is suitable to my application. It is basically an example from the msdn with some twists to get the custom designer to use a callback on click. Hope it helps anyone :-).
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name = "FullTrust")]
public class TabButtonDesigner : System.Windows.Forms.Design.ControlDesigner
{
ShowTabGlyph myGlyph = null;
Adorner myAdorner;
public TabButtonDesigner()
{
}
public override void Initialize(IComponent component)
{
base.Initialize(component);
// Add the custom set of glyphs using the BehaviorService.
// Glyphs live on adornders.
myAdorner = new Adorner();
BehaviorService.Adorners.Add(myAdorner);
myGlyph = new ShowTabGlyph(BehaviorService, Control);
myGlyph.Callback = () =>
{
((MyCustomTabButton)this.Control).ShowMyTab();
};
myAdorner.Glyphs.Add(myGlyph);
}
class ShowTabGlyph : Glyph
{
Control control;
BehaviorService behaviorSvc;
public Action Callback
{
get;
set;
}
public ShowTabGlyph(BehaviorService behaviorSvc, Control control) :
base(new ShowTabBehavior())
{
this.behaviorSvc = behaviorSvc;
this.control = control;
}
public override Rectangle Bounds
{
get
{
// Create a glyph that is 10x10 and sitting
// in the middle of the control. Glyph coordinates
// are in adorner window coordinates, so we must map
// using the behavior service.
Point edge = behaviorSvc.ControlToAdornerWindow(control);
Size size = control.Size;
Point center = new Point(edge.X + (size.Width / 2),
edge.Y + (size.Height / 2));
Rectangle bounds = new Rectangle(
center.X - 5,
center.Y - 5,
10,
10);
return bounds;
}
}
public override Cursor GetHitTest(Point p)
{
// GetHitTest is called to see if the point is
// within this glyph. This gives us a chance to decide
// what cursor to show. Returning null from here means
// the mouse pointer is not currently inside of the glyph.
// Returning a valid cursor here indicates the pointer is
// inside the glyph, and also enables our Behavior property
// as the active behavior.
if (Bounds.Contains(p))
{
return Cursors.Hand;
}
return null;
}
public override void Paint(PaintEventArgs pe)
{
// Draw our glyph. It is simply a blue ellipse.
pe.Graphics.DrawEllipse(Pens.Blue, Bounds);
}
// By providing our own behavior we can do something interesting
// when the user clicks or manipulates our glyph.
class ShowTabBehavior : Behavior
{
public override bool OnMouseUp(Glyph g, MouseButtons button)
{
//MessageBox.Show("Hey, you clicked the mouse here");
//this.
ShowTabGlyph myG = (ShowTabGlyph)g;
if (myG.Callback != null)
{
myG.Callback();
}
return true; // indicating we processed this event.
}
}
}
}
[DesignerAttribute(typeof(TabButtonDesigner))]
public class MyCustomTabButton : System.Windows.Forms.Button
{
// The attribute will assign the custom designer to the TabButton
// and after a rebuild the button contains a centered blue circle
// that acts at design time like the button in runtime does ...
// ...
}

c# WinForm: Remove or Customize the 'Focus Rectangle' for Buttons

Is there a way to disable or better yet draw your own focus rectangle for a regular button control! (that dotted line seems so Windowss 95ish)
I've noticed that the control properties (FOR BUTTONS) does not have a ownerdrawfixed setting (which I don't know if that's even the route to use for the solution, although i've seen it used for customizing other controls).
Getting this right is trickier than it sounds. No doubt one of the reasons that custom button painting isn't overridable. This worked as expected:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
class MyButton : Button {
private VisualStyleRenderer renderer;
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
if (this.Focused && Application.RenderWithVisualStyles && this.FlatStyle == FlatStyle.Standard) {
if (renderer == null) {
VisualStyleElement elem = VisualStyleElement.Button.PushButton.Normal;
renderer = new VisualStyleRenderer(elem.ClassName, elem.Part, (int)PushButtonState.Normal);
}
Rectangle rc = renderer.GetBackgroundContentRectangle(e.Graphics, new Rectangle(0, 0, this.Width, this.Height));
rc.Height--;
rc.Width--;
using (Pen p = new Pen(Brushes.DarkGray)) {
e.Graphics.DrawRectangle(p, rc);
}
}
}
}
A quick and easy way to disable the focus rectangle all together is to subclass the control and include this code:
public class CustomButton : Button
{
protected override bool ShowFocusCues
{
get
{
return false;
}
}
}
Just simple way.
Set
button.FlatStyle = Flat;
button.FlatAppearance.BorderColor = Color.FromArgb(0, 255, 255, 255);
button.FlatAppearance.BorderSize = 0;
button.TabStop = false;
FlatAppearance.BorderColor
set on code cause could not transparent color set in design mode.
Subclass the Button class and override OnPaint. If your override does not call base.OnPaint, nothing will be drawn for the button and you will have complete control (including the focus rectangle).
One quick/dirty solution that I found (for removing the focus rectangle only) was explicitly defining the background color. For the default control color, for ex:
this._dropDownButton.BackColor = System.Drawing.ColorTranslator.FromHtml("#F0F0F0");
EDIT: Apparently this doesn't work. It was being fixed in my case for an unrelated reason. Apologies.
I had the same issue when using BackgroundImage to set an image on the button. When the user pressed 'Tab', my image button got a black rectangle.
The solution that worked for me is:
Call for NotifyDefault(false) for every button I used.
Set the TabStop property to false for every button I used.
Tested on .NET Framework 4.6.2.
Before:
After:
In my case, I have to use both solutions above to make it work.
public class ButtonNoFocus : Button
{
protected override bool ShowFocusCues
{
get
{
return false;
}
}
public override void NotifyDefault(bool value)
{
base.NotifyDefault(false);
}
}
A simple solution to hide the focus frame would be to switch the focus from buttons to a dummy control as soon as the button has been clicked:
public frmMain()
{
...
RemoveControlFocusFrame(this);
}
private void RemoveControlFocusFrame(Control c)
{
if (c.Controls.Count == 0)
{
if (c is Button || c is CheckBox)
c.GotFocus += (o, e) => lblFocusDump.Focus();
return;
}
foreach (Control sub in c.Controls)
RemoveControlFocusFrame(sub);
}
The dummy lblFocusDump label has its Visible set to true and can be hidden in Designer by pushing it to background behind any other control.

Categories