Painting over a custom control that already paints - winforms

I created several custom .NET Winform controls that inherit from UserControl. One of them, ArrowButton, shares a lot of properties with another, slightly simpler one called CustomButton. ArrowButton is essentially CustomButton but with an arrow drawn next to the text. Both of them currently add a common Button to the UserControl base and then have Paint events call functions that draw the gradiant background, customized text, and arrow graphics in the case of the ArrowButton. This works fine.
But then I started thinking it would make sense to have the ArrowButton use a CustomButton instead of the normal button so that it gets the background and text from that, and all ArrowButton would have to do is draw the arrow on top of it.
I replaced the Button in ArrowButton with CustomButton. So far so good. But when it runs, it just looks like a CustomButton and the arrow part is never being drawn. As far as I can tell, the painting code in ArrowButton is not being called. Or possibly being painted over by the CustomButton.
So is it possible to have CustomButton paint first and then ArrowButton paint on top of it in an additive fashion? And if so should I be doing so using
void InitializeComponent(void)
{
this->CustmBtn = (gcnew CustomButton());
....
this->CustmBtn->Paint += gcnew System::Windows::Forms::PaintEventHandler(this, &ArrowButton::ArrowBtn_Paint);
overriding OnPaint(), or using some other method? I'm working in C++/CLI but C# examples will probably work too.
Edit - Extra details:
I changed ArrowButton to inherit from CustomButton instead of UserControl. This definitely seems like the way to go since I get to auto-inherit all of the properties from CustomButton and it still works fine in the VStudio designer. That said, it still refuses to draw the arrow. And I confirmed (using Console::WriteLine() calls) that both the Paint event function and my OnPaint() override functions where I'm trying to do the arrow paint are never getting called at all.

I came up with a solution of my own and as far as I can tell its a good one. Feels a lot simpler then the transparency or Region clipping-based workarounds I've seen in similar questions too.
I added this to CustomButton:
virtual void ExtraChildDraw(Graphics^ grph) {}
At the end of my CustomButton paint event function I call it and pass in the PaintEventArgs->Graphics reference I had been using there. Since it uses the default version of the function in the base class, it does nothing extra.
Then in ArrowButton or any other child-class I want to make of CustomButton I override the function and do all the extra 2D painting I want to add on top of what CustomButton does. It works great and even shows up correctly in the VStudio designer.

Related

cannot find GoToElementState under VisualStateManager & VisualStateManager.GoToState returns false

I am trying to use animation using (For a windows 8 Phone Application)
VisualStateManager
But when i do
VisualStateManager.GoToState
it returns false. I have defined all the states and grids properly. I have a Grid Layout with name "ContentPanel" under the Main Grid Layout. Under "ContentPanel" i have rectangles on which this animation has to work. I have created all those rectangles dynamically using Code Behind. I have created all the states and animations in the same code and trying to run this above mentioned line.
I have googled and found different solutions to use GoToElementState() method. But i do not see this method in the class VisualStateManager class that i use. I know that is a static method, But it doesnt show up in the suggestions.
I Also tried to use ExtendedVisualStateManager class. Even this is not available.
Above all, I am not sure why my GoToState is not working. If i have the main element as a Grid (as LayoutRoot) then this animation is working. I used following lines
VisualStateManager.GetVisualStateGroups(this.ContentPanel);
// ContentPanel is the name of the layout
where i have other rectangles on which the
animations has to be binded. Then i used this command..
VisualStateManager.GoToState(this, "stateName", true) // Returns false. Nothing happens when this is executed. Can any one please suggest me with the right implementation of this.
Thanks,
Sri Tej N.
If your Visual States are under a Grid, then
VisualStateManager.GoToState(this, "stateName", true)
won't work because this is most likely a Page, which doesn't contain the Visual States directly.
and
VisualStateManager.GoToState(this.YourGrid, "stateName", true)
won't work either because GoToState's first parameter needs to be a type of Control - Grid doesn't inherit from Control.
You will need to use this one.
ExtendedVisualStateManager.GoToElementState(this.YourGrid, "stateName", true)

User control, custom control, inheritance and override

I know this question is probably going to get alot of "Duplicate question" comments but i have yet to see one with an answer that works or isn't simply "Avoid it at all cost". But here goes, i have created a control lets call it "ControlA" in a project called "ControlA_Project" and i was intending to override some of its virtual methods in a control called "ControlB" that inherites "ControlA" in another project called "ControlB_Project" . The idea being that "ControlA" has save and load methods that i wish to change from saving and loading to file, too saving and loading to database ( and if later on another save and load type is required i can just override those methods again ).
The problem i have is i originally had "ControlA" as a usercontrol and when i tried adding the new control with the overrides ("ControlB") into a window i would get this error:
The component 'ControlB_Project.ControlB' does not have a resource identified by the URI '/ControlA_Project;component/usercontrols/ControlA.xaml'.
Googling the error i came to the conclusion you could not inherit from a user control ( or for the sake of arguement it wasn't a good idea ). So i then changed the control from a user control to a custom control. this however then leads me to another problem, a template for a control doesnt link to the code behind (i.e On_Click) like it does in a user control and there is no easy way to simply override the behavier (as far as i am aware). I know i can retemplate ControlB by copy and pasting ControlAs template and changing a few lines but controlA has a large template as it is and making mutliple copies seems a waste of space.
So put simply is there a correct way to change the behavier of a control in wpf?
First, remember that the ControlTemplate can be changed by the user alot so you need to make sure that important fields are clearly marked.
So if you want an OnClick event. First mark your button as "important"
<Button x:Name="PART_MyButton"/>
Its also a good idea to mark this aswell on your control class
[TemplatePart(Name = "PART_MyButton", Type = typeof(Button))]
public class MyCustomControl : Control
Now you want to attach to the on click event of that button to do that, override the OnApplyTemplate method.
public override void OnApplyTemplate()
{
mButton = Template.FindName("PART_MyButton", this) as Button;
mButton.Click += MyEventHandler;
}
Depending on how well your control can work without the control, you should gracefully handle a not found control or throw an exception.
One final thing is. If you override a control which has a default style, it might be a good idea to provide a new default style.
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));
}
and placing an implicit style in your generic.xaml

MFC: how to render an Aero-style combo box for owner draw?

I have inherited a large MFC application which contains a CComboBox subclass that overrides OnPaint. Currently it does all its drawing by hand (with lines and rectangles), and renders a combo box that looks decidedly Windows 98-style. However, it otherwise works great and provides a lot of useful custom functionality that we rely on, and rewriting the entire control is probably not an option.
I would like to modernize it so that the OnPaint draws in Aero style where available (falling back to the old code when modern theming is unavailable). I've done this with some other custom controls we have, like buttons, and it works great for our purposes. I know there are some tiny behaviors that it won't get right, like gentle highlights on mouse-hover, but that's not a big deal for this app.
I have access to the CVisualStylesXP ckass, so I've already got the infrastructure to make calls like OpenThemeData, GetThemeColor or DrawThemeBackground pretty easily (via LoadLibrary so we don't force Vista as a min-system). Unfortunately, I don't know the proper sequence of calls to get a nice looking combo box with the theme-appropriate border and drop-down button.
Anyone know what to do here?
Honestly, I don't know why they originally tried to override OnPaint. Is there a good reason? I'm thinking that at least 99% of the time you are just going to want to override the drawing of the items in the ComboBox. For that, you can override DrawItem, MeasureItem, and CompareItem in a derived combo box to get the functionality you want. In that case, the OS will draw the non-user content specific to each OS correctly.
I think you best shot without diving in the depth of xp theming and various system metrics is take a look at this project: http://www.codeproject.com/Articles/2584/AdvComboBox-Version-2-1
Check the OnPaint of the CAdvComboBox class - there is a full implementation of the control repainting including xp theme related issues.
Not sure if it's the same situation - but when I faced this problem (in my case with subclassed CButtons), solving it only required changing the control declaration to a pointer and creating the control dynamically.
Let's assume that your subclassed control is called CComboBoxExt.
Where you had
CComboBoxExt m_cComboBoxExt;
You'll now have
CComboBoxExt* m_pcComboBoxExt;
And on the OnInitDialog of the window where the control is placed, you create it using
m_pcComboBoxExt = new CComboBoxExt();
m_pcComboBoxExt->Create(...)
Since this is now a pointer, don't forget to call DestroyWindow() and delete the pointer on termination.
This solved my particular problem - if your control is declared in the same way, consider giving it a try.

performance in C# Application

i use some pictures in my 'MainForm' And My Windows Application was writing by c sharp.
i use this form to start other forms in my project.
And I use some label and panel with Transparent Color.
but when the program started i see many blink in transparent label and panel.
And it is very bad.
How I Can Fix this problem?
Enabling DoubleBuffered as stax suggested above is helpful but it may not be sufficient.
In your form, add the following method override:
protected override void OnPaintBackground(PaintEventArgs e) {}
And, in the OnPaint method, paint the background yourself instead. If you don't do this, drawing the background and painting are separate events, and background painting has higher priority, meaning that it will happen earlier.
Furthermore, if you add child controls (like labels), they receive their own paint background/paint events. You may be able to disable the Label's background. If I do stuff like this, I tend to not use controls but paint the text and the images in one OnPaint.
did you test it on multiple machines.
did you use an updated machine with all the .net service packs needed.
etc

Displaying controls on an alpha-blended form

I tried the Visual C# Kicks code for an alpha-blended form. This works (as soon as I remove the TransparencyKey property); that is, I can use the W3C's PNG alpha test image and see other windows underneath, but makes all controls on the form invisible. Presumably, they simply aren't painted, as OnPaint is overridden. I tried calling the superclass's OnPaint:
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
UpdateFormDisplay()
MyBase.OnPaint(e)
End Sub
, but this didn't change anything. (Calling MyBase.OnPaint first doesn't make any difference either.)
Unfortunately most articles about alpha-blended forms focus on pure splash screens without any controls on them — but we need a panel which first shows sign-in fields, then a progress bar.
The controls, by the way, do not need transparency; it's only on the outer edges that the PNG's transparency truly matters. So faking this by adding another form on top of this all (with the two always moving in tandem) might suffice, but I'd prefer a smoother solution.
Try putting this in your form ctor after InitializeComponents();
base.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);

Resources