Best practice for creating bordered control in WinForm - c#

I am writing WinForm application in C# .NET and I need to add dashed/dotted or any other type of border to any of UI components of application when the user clicks on it. I would like to get something like WinForm GUI editor in Visual Studio.
I am new in .NET so I don't know well what is possible via native methods and properties and what I need to implement myself. I have tried to find something on the net and here but I am not sure what to search, there are different approaches. For example it is possible to draw the border artificially, I mean using graphics. But I guess there should be easier approach.
What can you advice? What is the best practice in this situation? Please provide some portions of code.

Every Control has a Paint event. You have to subscribe to this event and look into the given arguments. The sender is the current control that should be painted. You can cast it within your method to Control. Now you can check the control if it focused by checking control.Focused and if it is true simply do whatever you like within the graphics object of the PaintEventArgs. This can furthermore be encapsulated in an extension method which would make the usage fairly easy.
public static void DrawBorderOnFocused(this Control control)
{
if(control == null) throw new ArgumentNullException("control");
control.Paint += OnControlPaint;
}
public static void OnControlPaint(object sender, PaintEventArgs e)
{
var control = (Control)sender;
if(control.Focused)
{
var graphics = e.Graphics;
var bounds = e.Graphics.ClipBounds;
// ToDo: Draw the desired shape above the current control
graphics.DrawLine(Pens.BurlyWood, new PointF(bounds.Left, bounds.Top), new PointF(bounds.Bottom, bounds.Right));
}
}
The usage within the code would then be something like:
public MyClass()
{
InitializeComponent();
textBox1.DrawBorderOnFocused();
textBox2.DrawBorderOnFocused();
}

Related

Keeping Adorner Glyphs On Top for Selected Control in Winforms Designer

TLDR: I'm seeking the correct method for timing the rendering of a juxtaposed graphic for a particular control on a design surface so that the graphic always is painted ahead of the adornment glyphs when that control is selected.
This question concerns control designers for Winforms: When the user places a control on the design surface, I want to display a graphic above the client area of the control. I have succeeded to some extent doing that for a TableLayoutPanel (TLP) control by overriding its OnPaint event handler then using the e.Graphics object available to paint a peach-colored rectangle. Below is an image showing the results: a painted graphic that spans the width of the control and is 35 pixels high--remember, this is a designer instance of a control placed on a design surface (created with a BasicLoader):
However, within the designer, if I resize the control, the graphic always ends up below the resize glyph (the glyph that has the North/South and West/East arrows on it):
I've tried creating and maintaining various Boolean flags to suppress the OnPaint message under certain circumstances. For instance, I set a flag to indicate that the control was just resized (to see how I did that, see my recent question: BeginResize/EndResize Event for Control on WinForms Design Surface) in order to suppress the painting of the graphic, but that didn't work because an OnPaint event is inevitably raised after I've cleared a flag. I don't want saddle this question with details of all the flags and places I tried to use/set them but suffice it to say that I painstakingly spent hours experimenting--to no avail. I've concluded that there must be a better way.
How can I ensure that the glyphs remain on top when I paint my graphics?
Thank you!
I can think of a few solutions, including the followings:
Using Padding of the TableLayoutPanel
Using Adorner and Glyph
Creating a custom panel, having header and editable content
I think the first solution will suit you well, however the other solutions also some points.
I can also think of a solution based on NativeWindow like what has been implemented in ErrorProvider, but It makes the post toooooo lengthy while the existing options are good enough. So I leave it to you if you like to pursue the idea.
Solution 1 - Using Padding of the TableLayoutPanel
This solution is for both design-time and run-time
TableLayoutPanel has a Padding property and its layout engine respects to the padding well. You can use the padding area to render whatever you want:
public class MyTLP : TableLayoutPanel
{
public MyTLP()
{
Padding = new Padding(0, 30, 0, 0);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.FillRectangle(Brushes.Orange,
new Rectangle(0, 0, ClientRectangle.Width, Padding.Top));
}
}
Solution 2 - Using Adorner and Glyph
This solution is just for design-time
For design-time rendering, I'll handle it using Adorner and Glyph.
If I was creating my custom designer, all the code belong to the control designer, but since you don't want to create a new control designer for TableLayoutPanel, then the same way that I injected a new custom action in the action lists, here I'll get BehaviorService and I'll inject an adorner to the adorners of the control at design time and this will be the result:
The behavior is quite similar to the other glyphs, it will be resized automatically when the control resizes and you don't need to do anything specific to handle the resize at design time.
Please note: It's a design-time solution and the painting is just being done at designer. In case you need a run-time solution, you need a totally different solution.
Here is the code:
using System;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;
public class MyTLP : TableLayoutPanel
{
private IDesignerHost designerHost;
private BehaviorService behaviorService;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (DesignMode && Site != null)
{
designerHost = Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
behaviorService = designerHost?.GetService(typeof(BehaviorService))
as BehaviorService;
if (behaviorService != null)
{
var adorner = new Adorner();
behaviorService.Adorners.Insert(0, adorner);
adorner.Glyphs.Add(new MyTLPGlypg(behaviorService, this));
}
}
}
}
class MyTLPGlypg : Glyph
{
Control control;
BehaviorService behaviorSvc;
public MyTLPGlypg(BehaviorService behaviorSvc, Control control) :
base(new MyBehavior())
{
this.behaviorSvc = behaviorSvc;
this.control = control;
}
public override Rectangle Bounds
{
get
{
var edge = behaviorSvc.ControlToAdornerWindow(control);
var h = 30;
return new Rectangle(edge.X, edge.Y - h, control.Size.Width, h);
}
}
public override Cursor GetHitTest(Point p)
{
//Uncomment if you want to attach a specific behavior
//if (Bounds.Contains(p)) return Cursors.Hand;
return null;
}
public override void Paint(PaintEventArgs pe)
{
pe.Graphics.FillRectangle(Brushes.Orange, Bounds);
}
}
class MyBehavior : Behavior
{
public override bool OnMouseUp(Glyph g, MouseButtons button)
{
//Do something and return true, meand eventhandled
return true;
}
}
Note:
To learn more about anchors, glyphs and behavior, take a look at the following links:
How to: Extend the Appearance and Behavior of Controls in Design Mode
Adorner
Glyph
Behavior
Solution 3 - Creating a custom panel, having header and editable content
You can create a custom panel having header and editable content. Then at design time disallow user from dropping content on the header part:
To do so, you need to create a new designer which enables the inner panel on design-time by calling EnableDesignMode method. Then for the inner panel, you need to create a designer which disables moving, resizing and removes some properties from designer.
I've posted a detailed answer here: UserControl with header and content - Allow dropping controls in content panel and Prevent dropping controls in header at design time

C# WPF Frame->UserControl->Usercontrol Loaded event doesn't fire

I'm new into WPF and have a problem I can't seem to find a solution for.
I'm writing a G19 (Keyboard) applet. This Keyboard has a 320x240 display attached, which you can access with C#.
I'm using WPF for this, because I don't need to do any GDI drawing anymore and use the normal controls instead.
So. It works as I wish. Everything draws properly except one UserControl. I have downloaded this control -> http://marqueedriproll.codeplex.com/
In the designer, the control works, the Loaded event get's fired and the animation is good.
When I run my application, I just see the label and the text. The animation does not work, and the Loaded event does not fire anymore.
Any help is appreciated.
The main function is my wrapper. The wrapper is already a Usercontrol and displays plugins which are switchable. This wrapper has the Frame Control(Wrapper1). I replace the content of this frame every time I switch the plugin.
public void SetPlugin(IPlugin plugin)
{
if (this.MainPlugin != null)
{
this.MainPlugin.OnHide();
((UserControl)this.MainPlugin).Visibility = System.Windows.Visibility.Hidden;
}
this.MainPlugin = plugin;
((UserControl)this.MainPlugin).Visibility = System.Windows.Visibility.Visible;
this.MainPlugin.OnShow();
this.Wrapper1.Content = this.MainPlugin;
}
I think it's the right approach to handle a plugin system that way. The plugin get's drawed on my keyboard.
What I don't understand is why the usercontrol only works in the designer view and not in the running application.
The basic code of the scrolling label is so:
public MarqueeText()
{
this.Loaded += new RoutedEventHandler(MarqueeText_Loaded);
InitializeComponent();
canMain.Height = this.Height;
canMain.Width = this.Width;
}
void MarqueeText_Loaded(object sender, RoutedEventArgs e)
{
StartMarqueeing(_marqueeType);
}
I don't see a reason why it doesn't work. Actually Ive always found a way to fix a problem but this time I see nothing.
Thanks in advance. Your help is really required today.
Have a great saturday! :)
I am guessing you are rendering to a bitmap target, rather than onscreen. If you are using RenderTargetBitmap, you have a couple of responsibilities. You need to set both a presentation source, and make sure you run events on the dispatcher.
Normally, App.xaml or Application.Run does this for you, but if you are not using a Window, you are on your own.
See this related question for details.

Add overlay to Window at runtime

I'm writing a simple "tutorial" library that will allow developers to easily add step-by-step tutorials to their existing WPF applications. The tutorials will help first time users of the application find their way around by adding an overlay that highlights a control and explains its purpose. The end result will look something like this:
The regular application:
The overlay explaining the purpose of a control:
My question is this: What's the most reliable and unobtrusive way to inject the overlay view into the current window? The best I've come up with so far is to require the developer to add an attached property to whatever window will be hosting the overlay, and then add the necessary elements on the window's Initialized callback:
public static void IsTutorialOverlayCompatibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((Boolean)e.NewValue == true)
{
if (sender as Window != null)
{
Window window = (Window)sender;
window.Loaded += new RoutedEventHandler((o, eargs) =>
{
Grid newRootElement = new Grid();
newRootElement.Name = "HelpOverlayRoot";
if (window.Content as UIElement != null)
{
UIElement currentContent = (UIElement)window.Content;
window.Content = null;
newRootElement.Children.Add(currentContent);
newRootElement.Children.Add(new HelpOverlayControl());
window.Content = newRootElement;
}
});
}
}
}
This feels like a hack, however, and I'm not sure that there isn't some edge case where this method will break the layout of the application. In addition, it requires that the window's Content property be an instance of type UIElement.
I'd like to avoid forcing developers to change their XAML (i.e, adding a custom overlay UserControl to every window) in order to use my library. What's the best way to add this kind of functionality to an existing WPF application?

drag and drop winform controls

I want to drag and drop a control (label for example) in a winform application. I saw some examples on dragging and dropping text, but this is not what I want. I want to enable the user to move a control around. Can anyone direct me to some resources or examples? Thanks.
you should look at examples on how to make draggable controls.
There are some answers here in SO as well.
See this Move controls when Drag and drop on panel in C#
this is a complete example on how to host the Form Designer:
Tailor Your Application by Building a Custom Forms Designer with .NET
I did something similar in Delphi long time ago, will search the source code, convert it into .NET C# and make a wiki page on that matter, as it is becoming such popular question recently :)
As far as i understand, where you wish to drop a control is called a container, infact any control can act as a container. So first that container, you need to enable the drop property as well as the drag property of the controls which you need to drag.
Then write events (Candrag, candrop, controladded, etc.) for each control where in which, some logic to hold the objects and display them as you may want.
Say, ill take an example where in which, you wish to drag imagetext from combombox into a picturebox and then make the picturebox analyze the text and fine related file name in a directory and load that image into its if its present.
So here, when you start dragging the text from combombox, you have to write some logic in event candrag. Then once you drop, you have to write logic to understand what kinda object was added and get the text related to it (kinda deciphering) in the control where you drop other control.
Sorry, i have no code to give you now, but i hope you got the idea how its done. May be this article can help you? http://vicky4147.wordpress.com/2007/02/04/a-simple-drag-drop-in-winforms/
bool draging = false;
int curPosX, curPosY;
private void label2_MouseDown(object sender, MouseEventArgs e)
{
draging = true;
curPosX = Cursor.Position.X;
curPosY = Cursor.Position.Y;
}
private void label2_MouseMove(object sender, MouseEventArgs e)
{
if (draging)
{
label2.Left += Cursor.Position.X - curPosX;
curPosX = Cursor.Position.X;
label2.Top += Cursor.Position.Y - curPosY;
curPosY = Cursor.Position.Y;
}
}
private void label2_MouseUp(object sender, MouseEventArgs e)
{
draging = false;
}

.NET: is there a Click-and-drag "Desktop-Like" control?

OK, first for context look at the Windows desktop; You can take items (folders, files) on the desktop and drag them around to different places and they "stay" where you dragged them. This seems to be a pretty useful feature to offer users so as to allow them to create their own "groupings" of items.
My question is thus:
Is there a control in .NET that approximates this behavior with a collection of items?
I'm thinking something like a listview in "LargeIcon" mode, but it allows you to drag the icons around to different places inside the control.
You can do this with a standard ListView control by implementing drag-and-drop. Here's a sample control that does this:
using System;
using System.Drawing;
using System.Windows.Forms;
public class MyListView : ListView {
private Point mItemStartPos;
private Point mMouseStartPos;
public MyListView() {
this.AllowDrop = true;
this.View = View.LargeIcon;
this.AutoArrange = false;
this.DoubleBuffered = true;
}
protected override void OnDragEnter(DragEventArgs e) {
if (e.Data.GetData(typeof(ListViewItem)) != null) e.Effect = DragDropEffects.Move;
}
protected override void OnItemDrag(ItemDragEventArgs e) {
// Start dragging
ListViewItem item = e.Item as ListViewItem;
mItemStartPos = item.Position;
mMouseStartPos = Control.MousePosition;
this.DoDragDrop(item, DragDropEffects.Move);
}
protected override void OnDragOver(DragEventArgs e) {
// Move icon
ListViewItem item = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
if (item != null) {
Point mousePos = Control.MousePosition;
item.Position = new Point(mItemStartPos.X + mousePos.X - mMouseStartPos.X,
mItemStartPos.Y + mousePos.Y - mMouseStartPos.Y);
}
}
}
I think the closest would the ListView control, but even that is more like an explorer window. You might be able to create your own view that does what you want, but you'd need to manually persist icon locations somewhere.
If you are not opposed to using WPF, Josh Smith has created a pretty neat canvas that I am currently using for a project. It allows you to add controls and drag them around the canvas. You would have to handle what is loaded on the canvas and where on the next load of the program, but that is pretty simple.
http://www.codeproject.com/KB/WPF/DraggingElementsInCanvas.aspx
This depends on whether this is a windows application or a web browser based application. In either case you need to have some sort of container to manage the locations of controls. You can manage the position of controls inside of a container with their X and Y coordinates.
You would handle the actual movement using the drag events. So you have drag start, while dragging (you might show a place holder graphic or change the cursor), and finally a drag end (set the control's x and y to the new position). Obviously these aren't the actual event names, but a search for "how to handle drag events" should get you started.
In a web environment, I know jquery has dragging capability built in. So you might want to look at that. The one big thing you'll have to be careful of is maintaining the positions of your controls between postbacks. I'm not sure what would happen in this case.
Windows uses ListView32, an internal control with drag n' drop placeholder features, custom borders...
The icon location can be stored in a XML file, or in the application settings (by putting the XML as string and converting it to file when needed).
You can do, for example:
<icons>
<icon1>
<name>Icon1</name>
<text>My PC</text>
<imageIndex>16</imageIndex>
</icon1>
<icon2>
.....
</icon2>
.....
</icons>
Lorenzo

Categories

Resources