|
Scott Dorman wrote: Since I posted I stumbled across a partial answer to this problem. I am now able to get the label to persist and generally behave properly with one exception. When I first put the extender on the form and start adding labels, none of them appear on the design surface until I close the form and re-open it. I haven't tracked down the reason for this yet.
You probably realize this... but the reason is *always* that your algorithm hasn't triggered drawing yet. It is closing the form and re-viewing/restoring it that triggers an invalidate -- which finally gets your labels to draw.
|
|
|
|
|
mike montagne wrote: You probably realize this... but the reason is *always* that your algorithm hasn't triggered drawing yet. It is closing the form and re-viewing/restoring it that triggers an invalidate -- which finally gets your labels to draw.
Yes, that is the premise I am working under. Currently, I am attempting to resolve this by calling Invalidate myself, on both the label and it's parent control but so far it isn't having an effect.
-----------------------------
In just two days, tomorrow will be yesterday.
|
|
|
|
|
Interesting... because I've had some similiar issues with a visual control project I'm currently working on. I have quite a bit of time in developing a (proprietary) scheme which ensures that drawing is performed. (Can't share it, sorry. But you can get there too.)
A related issue for instance demonstrates to my satisfaction that there are problems with compiler output. I find some overridden event handlers such as OnSizeChanged() are correctly fired. But I find others such as OnSystemColorsChanged() and OnEnabledChanged() are never fired!
OK. So we can fix OnEnabledChanged() because we can write a new Enabled property. But we can't fix OnSystemColorsChanged() because there is no way for us to detect *and* fire the outer system event.
So, I called Microsoft about this, explained these issues for about an hour, and they wanted to charge me $249 to report them... to which I replied I wouldn't give the 2 cents to report them... I'd be more inclined to initiate a class action suit if they had so little concern about fixing all these things.
That's of course another matter, and neither are the non-fired events exactly your issue. They are surrounding issues which tell us we are not working in a perfect environment. I don't believe we should simply accept these things, because if you and I are to succeed, not only do we have to do far more work than we should have to, we have to do our work to a substantially higher standard than the authors of the tools.
It's possible you have committed a relatively obvious ommission or failed to perform a responsibility. Otherwise, the challenge is devising a sound scheme which ensures your drawing is performed when it needs to be.
|
|
|
|
|
I'm not sure I will need to go to the level of developing a completely custom scheme since I'm not actually creating a new control type. It is interesting to know about the event handlers not being raised. I don't think I will encounter that problem as there are very few events that I should actually need to listen for.
I'm a little surprised that Microsoft wanted to charge for reporting the bugs.
I do completely agree with you in that we aren't working in a perfect environment. I think the problem is that a lot of people don't need to dig this deeply into these types of behaviors/development tasks so the visibility to some of these types of problems is relatively low.
-----------------------------
In just two days, tomorrow will be yesterday.
|
|
|
|
|
I was *more* than a little surprised...
Anyway... custom schemes are required when we want to dispense with the baggage given to a higher-level control which for instance supports substantially extended behavior in suspend and resume layout, by descending from a lower-level class such as Control. You are on your own then, and (as with OnSystemColorsChanged()) I found that Invalidate() will not always invoke a re-drawing, but that I could force IL to respect Invalidate() always, by ensuring that IL recognized that something *needed* to be drawn.
|
|
|
|
|
The strange thing here is that once the label is displayed (by closing/opening the form) it shows up in the designer visually, in the designer Properties window, and I can interact with it. However, there is no code present for it in the Designer.cs file. If I further customize one of the label properties directly, I do get code in the Designer.cs file. Even with no code in this file, everything still behaves correctly at run-time and design-time.
-----------------------------
In just two days, tomorrow will be yesterday.
|
|
|
|
|
Yes, that's definitely strange, because without a parent, drawing algorithms should have no idea even where to draw the control (where the coordinate system starts, etc.)
It is possible that an assumption is made, not in performing your constructor (which would perform the initial draw which is failing), but in repainting all the existing controls (which may explain the later appearance, for instance when you collapse and restore the IDE window).
What may be happening there is that the coordinate system is simply assumed to be the design window, and your control is painted *over* it. In other words, it may be painted as a separate, unparented windowed control, floating over your design surface.
|
|
|
|
|
Hmmm...I see the point you are making, but it doesn't answer the issue of the Designer.cs file not containing any code for the label control even though it is visible in the design surface and I can interact with it.
-----------------------------
In just two days, tomorrow will be yesterday.
|
|
|
|
|
No, if the control is painted *over* the window, even if it is erroneously attached to the design window's coordinate system), and if the control is dynamically assigned a parent after design time processes would normally attach it to a parent (which triggers membership to the form code body), this would engender exactly the results you report.
|
|
|
|
|
(This also means that designers are not an issue.)
|
|
|
|
|
I'm not sure, but the fact you can interact with an unparented control might too be anomalous behavior, because I believe it is the form designer's responsibility to provide your ability to interact.
|
|
|
|
|
Well, then again, the anomalous behavior may be the result of being added to two separate Controls arrays -- somehow leaving you in a limbo where the Form designer *is* supporting interaction, but failed/aborted/reverted membership to the form's code body.
|
|
|
|
|
Sorry for the delay in responding...somewhere in one of your last posts opened the door and I have been able to get everything working. Part of the problem I was running in to seems to be a Visual Studio bug that required me to close VS and restart it to make sure that the designer was picking up the latest build of the control.
I'm putting the finishing touches on it tomorrow and then should, hopefully, get the green light to write this (or a slimmed down version at least) up as an article.
Thanks for the help you provided.
Thanks,
Scott.
-----------------------------
In just two days, tomorrow will be yesterday.
|
|
|
|
|
Way to go, Scott.
I've had some other problems with VS as well -- all related to component development situations. I'm not sure I'm dealing with them the best way (you probably aren't either).
I had a long stint where I was trying to fix some design time behavior issues (which are VS problems such as non-response to OnSystemColorsChanged, OnSizeChanged, OnEnabledChanged), and (this seems stupid) I found out after a while that it was impossible the development environment was working off my many revisions of code. I had crossed some documentation warnings that told not set some project option, but evidently that was a deprecated option from earlier VS -- before I was using VS. Just the same, my projects were working like all the designer versions had been cached, and my changes had no effect.
I switched to totally non-visual design for several days, and when I came back, new code was honored consistently. But damn if I can tell what the heck was going on for a while. I wasted half a day's work at least just wrestling with issues I had no control over.
I'm wondering now that it didn't have something to do with a DefaultValueAttribute I had declared on an outer class property, that I later found by much experimentation could only be declared on the properties of the inner property class. I worked that out (and had much misbehavior while I was trying do use that outer DefaultAttribute), and haven't returned to it. No problem since.
Pretty strange. I don't feel like I'm out of the woods yet.
|
|
|
|
|
mike montagne wrote: Way to go, Scott.
Thanks! I'm glad I was able to get this working the way I wanted.
mike montagne wrote: I've had some other problems with VS as well -- all related to component development situations. I'm not sure I'm dealing with them the best way (you probably aren't either).
I'm sure we're not. It seems like there is still a lot of undocumented aspects when dealing with the designers and design-time behavior once you get past anything rudimentary.
mike montagne wrote: Just the same, my projects were working like all the designer versions had been cached, and my changes had no effect.
I think this is the situation that I was running in to as well. I discovered it when one the automatic updates rebooted my computer. When I came and restarted VS, things were working the way I had expected them to, even though I hadn't started changing any code yet. That's when I realized that it must have had to do with VS being shutdown.
mike montagne wrote: Pretty strange. I don't feel like I'm out of the woods yet.
I know that feeling. After I integrated this code in with the rest of the component, my RTL handling stopped working correctly. It displays fine at run-time but not at design time. That's my task for tomorrow morning...figure out what broke between the simple LabelProvider class and the more extensive class that will be used in the application.
The only thing I don't have working that I would like to is support for an ImageList (actually the ImageKey and ImageIndex properties specifially) for the label itself. That and figuring out how to make the property grid realize the additional Font property on the extended control already has it's default value. (It does the right things, it just displays it in bold showing that it has changed from the default when it really hasn't.) Both of these are minor and only affect the design-time experience so I'm not going to put much effort into worrying about them.
-----------------------------
In just two days, tomorrow will be yesterday.
|
|
|
|
|
Scott Dorman wrote: I know that feeling. After I integrated this code in with the rest of the component, my RTL handling stopped working correctly. It displays fine at run-time but not at design time. That's my task for tomorrow morning...figure out what broke between the simple LabelProvider class and the more extensive class that will be used in the application.
In my case I can say this... there was a lot of experimenting I had to do, and, in the end, I can see why they want you to do things the way I found you had to do them. It was way too painstaking though to have to solve these wee little, almost unexplainable issues, until, one after another, you have every little detail worked out (which of course is something you have to do anyway), and finally the misbehavior goes away. What I think is going on is there are many conditions which are not handled -- which just fall through the cracks without upsetting anything -- and you *could* go on developing a bad component if you aren't a perfectionist. But it's way more difficult to get there sometimes, and that's really too irresponsible of them for my tastes. I don't like being a guinea pig, when my work is better than theirs is!
I would bet that your situation is the same... that you are going to have to chase out some things which really aren't necessarily the wrong way to do something, and which are in keeping with the language specs, but which somebody didn't decide to support in a bullet proof way.
It's pretty esoteric material to ponder, when you find after too many days work that you can't fire accessors in a class property supported by a TypeConverter -- and that you can only declare DefaultAttributes on the accessors of the inner property class.
Scott Dorman wrote: The only thing I don't have working that I would like to is support for an ImageList (actually the ImageKey and ImageIndex properties specifially) for the label itself. That and figuring out how to make the property grid realize the additional Font property on the extended control already has it's default value. (It does the right things, it just displays it in bold showing that it has changed from the default when it really hasn't.)
It looks like we're covering a lot of the same ground with our projects. I looked at ImageList and decided I wanted to handle my graphics myself. It turned out this was even easier than I anticipated. Most of this C# has been (for which too it should be praised). I'm glad I did it the way I did now. The only quirk I ran into is I found that you can't (or I couldn't) create a null bitmap property, or allow anyone to assign null to a bitmap property. When they make the assignment, handle it in your accessor and if the assignment is null, create a new bitmap of size 1,1. Initialize the property to a new bitmap of 1,1 as well.
I know that an ImageList has one theoretical advantage -- that if you want to use an image multiple places, you can do so from the one instance as you can from a compiled, embedded resource. But I decided to let them do it that way if *they* want to, and to manage my images/glyphs myself. I'm happy with that as a solution.
|
|
|
|
|
mike montagne wrote: In my case I can say this... there was a lot of experimenting I had to do
I got lucky with finding this particular issue. As I was comparing code between the simple LabelProvider (which worked) and the one that didn't, the only difference was in my "remove" method (which runs when the extender is removed or the extendees handle is being recreated). In the one that worked, I had this line: this.label = null; while in the one that didn't I had this.label.Dispose(); . Once I changed it, everything started working again. It would seem that in my cleanup I got a little too aggressive about making sure resources were being released.
I completely agree about needing to be a perfectionist to get this working correctly. I think the design capabilities of the runtime are incredibly powerful and equally as complicated. It seems like they had an idea that people would want to use it, but never really had time to completely finalize everything. So we are left with a lot of things that don't work *quite* right unless all of a certain set of conditions are met, and, of course, those conditions aren't fully documented anywhere.
mike montagne wrote: It's pretty esoteric material to ponder, when you find after too many days work that you can't fire accessors in a class property supported by a TypeConverter -- and that you can only declare DefaultAttributes on the accessors of the inner property class.
Absolutely. I've learned more details about how the Framework does things related to UI in the last two weeks than I have in several years...and all of it from trial and error.
I did implement the ability to set a single image to be associated with the label (same behavior as the normal Label control) and that works just fine, but I wanted the ability for the ImageList specifically so I could specify it in one place. For the UI that is being built that will use this component, that capability makes a lot of sense. I think everything was working properly, I just didn't have the nice design-time experience of being able to set the image using a dropdown that showed all of the images in the list (like the Label does).
I've found that there are a lot of things that are going on behind the scenes with the designer experience that don't exactly follow the model Microsoft is espousing for the rest of us.
-----------------------------
In just two days, tomorrow will be yesterday.
|
|
|
|
|
Scott Dorman wrote: Absolutely. I've learned more details about how the Framework does things related to UI in the last two weeks than I have in several years...and all of it from trial and error.
Yes. The bandwagon believes all these things are easy. In truth it often takes the greatest expertise, ability, and effort to make some of the most simple things work.
Scott Dorman wrote: I've found that there are a lot of things that are going on behind the scenes with the designer experience that don't exactly follow the model Microsoft is espousing for the rest of us.
Absolutely.
Something I think they need to do is make developers document all this stuff, because it is very often only in doing so that you realize what too complicated a world you are building (and never documenting, so that people "don't find" the flaws in it).
The truth is, because of all that is wrong and is never resolved, probably 80%+ of the overhead invested in *good* designs is in all these unnecessary trials. I have right now about 10 weeks in a project I wrote the first time in just a few days. There was nothing wrong at all with the first design. It was perfectly straightforward, and efficient.
But it didn't draw to my standards, and I re-wrote it again and again and again, until I developed an extremely refined/optimized automatically forking (intelligent behavior) method of drawing (still not up to C++ standards, thanks to the expectable inferiority of .Net).
OK, so that's fine. But then there's all these inconsistencies in the development environment... so out of all of it I still have 80%+ of my time in fussing (too nice a word) with stuff that none of us would have to fuss with if the people building the tools built them to the standards that we are building product to.
|
|
|
|
|
Scott Dorman wrote: I've found that there are a lot of things that are going on behind the scenes with the designer experience that don't exactly follow the model Microsoft is espousing for the rest of us.
Yes, and there's where we *all* have to worry about the hidden agendas which might exist. On the surface, all this is exalted as cross platform compatible. But in truth, it is these very inconsistencies which can purposely reduce competitors to dogs perpetually chasing their tails for naught, because *our work* itself is forced to comply with the inconsistencies (merely to work).
When you find you can't fire functions from outer class property accessors when the property class is associated with a TypeConverter, is your code base really written as you would want to write it for MONO?
Not at all.
So are you going to fix it for MONO, and is the promise of platform independence a reality?
Not at all.
And then, we're sacrificing speed and working within this huge extra environment for the ostensible purpose of light resource reliance?
What we really need is to C-sharpify-C++ some... deploy all these libraries from the OS... and realize the speed benefits of today instead of downgrading them (substantially).
|
|
|
|
|
Now that I've declared victory on the extender component, I'm getting ready to tackle creating a custom layout engine. Do you have anything you can point me to as a good reference? This is in a similar state as IExtenderProvider, very powerful and not well documented at all.
-----------------------------
In just two days, tomorrow will be yesterday.
|
|
|
|
|
Damn! It's like we're walking down the same path here.
This is where I developed my alternate scheme of drawing.
I took a hard look at all this, and (believing I'm pretty intuitive about where Anders H likes to go with these design features), I decided to descend from Control so that I could do what I wanted with optimal efficiency. In other words, my interpretation of what they're doing with the external classes which provide further services to given classes, is basically a way of partitioning so that the *whole* class you are using (really) *is* not perceived to be so oversized.
My approach was to build just what needed to be built, and to leave all that extra stuff out of my landscape. Totally custom.
After all, you will probably spend more time wrestling with the huge Cadilac they built than *designing* a Porsche. I look ahead, see too many mysteries or insufficiently documented issues... ambigous issues... hidden workings... all that sort of thing, and (thinking I can build it as well as better anyway) I just tend to dispose of all that stuff that can get in the way.
In the end it comes down to this: If *you* can build it as efficiently as it can be built, then why wrestle with all this ostensibly fancy stuff with the huge footprints, when in the end it's going to slow you down so much that you're 2 steps backward (at least), and you've incorporated an eventual dinosaur that, when *they* change it, your life's work is down the tube again.
I trust my standards better than Microsoft's -- and I've already revisited those lessons far too often.
|
|
|
|
|
Just returning for a quick thought.
All this layout engine stuff is really (as far as I'm concerned) about double buffering. It is challenging to build your own scheme which gets the benefits of double-buffering. Well, that needs to be qualified -- *when* you have to do some of the things we had to accomplish.
It always comes down to one little trick: For me, it was realizing that I could cause drawing without calling Invalidate() (which I found *not* to support double-buffering across the conditions I needed support for).
|
|
|
|
|
Scott Dorman wrote: The fact that the Parent property is null at runtime is normal as far as I can tell. The other controls and extenders that I looked at also had the same problem. (I'm not 100% sure of this since it has been several days since I looked at that aspect of it, but I'm pretty sure that was the case.)
PS. None of us can be experts on everything, so the thought I pass along here must be declared to be no more than an educated assumption (based on all experience).
In every visual component design project I've seen or been involved in, null != Parent is obligatory to drawing. My thinking then is that your assumption is a dangerous one to build further work upon. In other words, you might very well waste considerable work before you find indeed that this assumption is wrong (which, without exception, is my experience).
I would absolutely get that Parent assigned, or expect the anomalous behavior you are describing. I mean... how can any process draw your label, if it doesn't even know what to draw it on?
|
|
|
|
|
I see your point. If I create a simple form with two controls on it, the Parent property is not set until the call to this.Controls.Add(...) . In my code that I use to add the label control, I have a similar line which also sets the parent property.
The code I ended up with looks basically like this:
this.label = this.provider.EnsureLabel(this.control);
this.label.Text = text;
ISite site = this.control.Site;
if (site != null)
{
site.Container.Add(this.label);
}
this.control.Parent.Controls.Add(this.label);
-----------------------------
In just two days, tomorrow will be yesterday.
|
|
|
|
|
You are *exactly* right.
It is in the underlying handler for Controls.Add() that Parent is assigned -- and the scheme of the visual system is based on this interraction.
To replicate that, all you have to do with your label of course is to add it to the Controls array.
|
|
|
|
|