If I override OnPaint and draw a square on the control, how do I get that e.graphics.draw... to show up when I'm previewing it in the designer?
http://msdn.microsoft.com/en-us/magazine/cc164048.aspx
http://msdn.microsoft.com/en-us/magazine/cc164145.aspx
While you could manually register with
Control.OnPaint to add your design
time UI, you'll find that overriding
OnPaintAdornments is a better option
because it is only called after the
control's design-time/run-time UI is
painted, letting you put the icing on
the cake (see Figure 20). Simply
adding DesignerAttribute to the
ClockControl class completes the
association: Copy Code
[
Designer(typeof(ClockControlDesigner))
] class ClockControl : Control { ... }
If you're trying to draw a focus rectangle for you control, and have it be visible when manipulating your control after it has been added to a form, then Gaijin42's approach should work.
If you're trying to see what your custom drawn control looks like...
If you've overriden the OnPaint method in the control, you should see the results of that OnPaint when you look at your control after it has been added to a form (or another control).
If you're trying to see the results of the Paint when looking at the designer for the control itself, I don't think that is possible (at least it wasn't with VS2005):
Unfortunately this is not possible.
While you're writing the behaviours
of the user control the code isn't
compiled and the designer actually
uses an ordinary user control to host
the child controls. The OnPaint
override etc will not run.
To debug the overrides in your control
you need to run it in some sort of
host. VS2005 provides a new control
host for this purpose but I find that
even this is not sufficient sometimes.
Related
I'm creating an OverflowPanel derived from the WPF Panel class. The intent is that it will fill with items in a single direction, and when there are too many items to display, excess items will be removed and replaced with another control to hold the overflow. Think of a website's breadcrumbs, or the address bar in Windows File Explorer. This is a .Net Core 3/C# 8 project.
I have a partially working solution: I've inherited from Panel and overridden MeasureOverride() and ArrangeOverride() to get the behavior I want. My problem now is getting a button or some other control to display in place of the items being removed.
My initial, naive approach was to just create a Button in code and try to Measure/Arrange it.
public class OverflowPanel : Panel
{
// First by itself, but I did also try to host this in a new UIElementCollection
private readonly Button _overflowButton = new Button();
public override Size MeasureOverride(Size availableSize)
{
...
_overflowButton.Measure(availableSize);
// Do stuff with _overflowButton.DerivedSize.
...
}
// Also attempted to draw int in ArrangeOverride()
}
This did give me non-zero result for the measurement. (I put some dummy content in the button.) My algorithm gives me space on the screen where the button should go, however, nothing gets rendered there.
I also confirmed that there wasn't simply a button being drawn with no visual style, by inspecting the Live Visual Tree in Visual Studio.
I tried to make a UIElementCollection and add the button to that to see if it would add it to the visual tree, but this also did not work.
Most Google/StackOverflow results I've seen suggest something along the lines of this.Children.Add(_overflowButton), but this does not work when hosted inside an ItemsControl, as it takes over managing the collection of objects and throws an exception if you attempt to mess with it.
After digging around in the code for Panel and UIElementCollection, I noticed that Panel lets you override
UIElementCollection CreateUIElementCollection(FrameworkElement logicalParent)
to use a derived implementation of UIElemenetCollection. I created a PinningUIElementCollection to trick WPF into rendering the extra element. It stores extra items and then slips them in whenever the iterator is accessed. It also does index mangling to access both the extra collection of items and the automatically generated one.
This actually worked. My button is now displayed (albeit without the correct styling, but that's a separate issue.)
However my issue with this approach is that it seems like a lot of work. It also seems error prone: I could easily miss when it tries to use a numerical index and forget to mangle it, causing unpredictable results.
Is there a simpler/more straightforward way, in my derived Panel implementation, to display an extra button or some other arbitrary control with only a few less hoops?
Delphi's VCL has a very useful class, CustomControl. This class is a direct base class of Control, which is equivalent to C# WF's UserControl.
For those, who are not familiar with VCL, CustomControl differs very little from Control; the main difference is that most of properties are protected; when implementing the new control, developer may decide, which ones does he want to publish and which ones shall remain hidden.
I'm developing my own control for Windows Forms and I want to hide some properties and events. For instance, I don't want to expose the MouseDown event - instead I allow capturing clicking on control's elements.
Is there an equivalent of VCL's CustomControl in Windows Forms? If not, how can I hide unwanted public properties and events in my control?
In response to answers:
This is not a matter of security, but rather a matter of code elegance. In Delphi I can derive from CustomControl, leave the OnMouseDown event protected (as in C#'s protected) and say to the control's user:
You cannot use OnMouseDown, because there is none. If you want to react to user clicking on control, simply use OnElementClicked - you'll even get detailed information about which element was clicked and what was its state.
I may disable calling the MouseDown event as Hans Passant suggested, but then I would have to include the following in the control's user's manual:
Please do not use the MouseDown event, because I've overridden the OnMouseDown method, such that it won't call the MouseDown event. This is because the control's logic is designed in such way, that you should use OnElementClicked rather than OnMouseDown. Please don't criticize the control because of MouseDown not working. Please don't report it as a bug, because it is by design. Please don't post messages in forums or create blog entries explaining how to fix the MouseDown problem by inheriting the class and manually calling the MouseDown event, because it would break the control's logic. Pleas don't... damnit, told you so!
If someone actually inherits from my control - I assume then, that he knows, what he's doing (also because one would then gain access to my control's internal logic as well). But if someone just uses my control, I would give him only these properties, events and methods, that I'm sure will work as designed.
I hope it explains my motives :)
There's a fundamental difference between hiding, what you asked for, and making it inaccessible, what I assume Delphi does. Hiding is simple, just repeat the declaration and apply attributes:
using System;
using System.ComponentModel;
using System.Windows.Forms;
class MyControl : Control {
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
private new event MouseEventHandler MouseDown;
}
Which prevents the event from being displayed in the Properties window, it won't show up in the IntelliSense dropdown and generates a compile error when he tries to assign the event in code anyway.
It is however not an absolute guarantee that the client programmer couldn't work around the restriction anyway. By casting or overriding your class for example. But the ultimate backdoor is implementing the IMessageFilter interface, nothing you can do about that. So this ought to be good enough if elegance is the goal.
I have a relatively simple setup. I have a custom usercontrol that has a bunch of components on it, some text boxes, and a listview.
In the designer, I can drag and drop other controls into my usercontrol, and it adds them to the usercontrol instance. I don't want this.
How can I explicitly say "Don't allow additional controls to be added to this usercontrol?"
That's not the way it works. When you drop your user control on a form then adding controls to it isn't supported. That requires a special designer, this answer shows what is required. Maybe it looks like the controls get added but they merely overlap your user control. Their parent is still the form.
If a programmer opens your user control class itself in the designer then, sure, he can add controls as he pleases. The only way to stop that is to not ship the source code and use the sealed keyword to prevent deriving from it.
You could create a boolean property MyContainer.DisableAddControls or something.
If your MyContainer.Controls.Add(..) is overridden, then you can throw some custom exception in that Add() method as follows:
if(DisableAddControls)
{
throw new DisableAddControlsException();
}
If you are inheriting that method straight from ContainerControl, then you can handle the ControlAdded event and throw the exception there.
myContainer.ControlAdded += myContainerControlAdded;
private void Control_Added(object sender, System.Windows.Forms.ControlEventArgs e)
{
if(DisableAddControls)
{
throw new DisableAddControlsException();
}
}
On second thought, this won't throw out your designer at design time... nevermind.
Is there something like an "OnPaint" method in Silverlight?
Back when I was writing C++, I found that it was easy to use the OnPaint event to customize the display of a class to the screen?
Is there an equivalent in Silverlight? If I want to do something when a UserControl is displayed on the screen, what method would I override?
I noticed this post:
C# WPF OnPaint method alternative?
but it seems that in Silverlight, thre is no "OnRender" method for a UserControl class.
OnPaint was a workaround... to allow you to customise the appearance of controls. That was because you did not have much control over the default appearance of any controls in WinForms applications.
With Silverlight that all changes. Every control is now effectively skinned, using templates and styles, and there are few limitations on how you can customise them. There are far too many links so I just grabbed a couple for you.
Get yourself a good book on Silverlight and learn the proper way to work with it (not around it). This one is one of my favorites.
If you have specific things you are trying to do, to the appearance of user controls, best to list those instead and find out the best way to do it the Silverlight way. :)
You haven't specified what you're trying to do. If you just want to know when a frame is being rendered, the CompositionTarget.Rendering Event will tell you that. If you actually want to draw on the frame being rendered, you cannot do so.
It is LayoutUpdated.
As in:
...
this.LayoutUpdated += new EventHandler(LayoutUpdated);
}
void LayoutUpdated(object sender, EventArgs e)
{}
In a program I have written users can add controls to the form and move them around and set some properties in a pseudo design mode. I want to be able to lock all these controls into one location when they press a button to switch to "data mode". How can I do this? I wanted to do be able to loop through all the controls and use the Lock Property but I noticed it didn't show up in intellisense.
Thanks!
The Locked property is not a real property -- it is one which is added in by the Windows Forms designer (like the Generate Member and Modifiers "properties"). You would therefore need to simulate it yourself, either at the form level or (if required) at the control level (say with a dictionary of which controls are locked), and manually check it in the code you've written for moving controls around.
I am assuming by "pseudo-design mode" you do mean that your application is in a run-time state, and the end-user is experiencing a "virtual design mode" : please correct me if I am wrong.
But, I am assuming you are referring to the design-time 'Locked property of controls, and that you wish to "emulate" this at run-time ... correct ?
I'm also assuming you are attaching mouse up/down/move handlers to the controls you do allow to move around, probably by looping through all, or a subset of, the controls on the form (or a collection you are maintaining of controls allowed to be moved).
If my assumptions are correct, I would go for removing the event handlers that enable moving when you need to disable control movement, then restoring those event handlers when you need to allow controls to be moved again.
One main reason being that it is, imho, "best practice" to control event-handling rigorously (leaving event handlers "in-place" can interfere with object disposal ... although that may, in no way, apply to your scenario here).
One more idea : you have an "invisible" Panel docked 'fill to the Form : on this panel are all controls that can be moved : this may allow you to more easily "narrow your focus" on which controls you "spend" this extra code on. The drawbacks in using this approach are usually :
if you use hostingForm.ActiveControl to determine which control got the mousedown (and, thus, can then be moved) : you'll find some controls, like labels, and pictureboxes, do not become the activecontrol of the form when clicked, but most do.
you have a "z-order" thing to think about since a control not in your panel encapsulating the controls you wish to allow to move sent behind the pseudo-transparent panel will be hidden.
For these reasons, imho, I think disabling and re-enabling event handler attachments is best, most simple, and since it can be done when the controls are "down-cast" to their control "identity" :
private void enableControlsMove()
{
foreach (Control theControl in panel1.Controls)
{
Console.WriteLine(theControl.Name);
theControl.MouseDown += new MouseEventHandler(theControl_MouseDown);
theControl.MouseUp += new MouseEventHandler(theControl_MouseUp);
theControl.MouseMove += new MouseEventHandler(theControl_MouseMove);
}
}
private void disableControlsMove()
{
foreach (Control theControl in panel1.Controls)
{
Console.WriteLine(theControl.Name);
theControl.MouseDown -= theControl_MouseDown;
theControl.MouseUp -= theControl_MouseUp;
theControl.MouseMove -= theControl_MouseMove;
}
}
I use it this way.
best, Bill
Locking controls prevents them from
being dragged to a new size or
location on the design surface.
However, you can still change the size
or location of controls by means of
the Properties window or in code.
MSDN
I guess it's a visible-to-designer-only property. I think you'd have to implement your own freeze mechanism - a little flag to toggle between Design and Use modes.
Update: It seems that custom designer classes can add properties to controls based on whether they are in Design Mode or not.
More details available here if you intend to take the VS architectural hammer path. In any case, worth 10 mins of reading time.
Custom Design-time Control Features in Visual Studio .NET - Dino Esposito