How to make a window manager? - c#

I tried writing code several different times, but I came to an error with each one.
Basically, I'm trying to make "windows" similar to say Explorer, Paint, MediaPlayer, where you could drag then around, interact with them, minimize and close. Of course, if you clicked on a window, the one below it (they can overlap) shouldn't get affected.
I know how to do this, I have a list of the class I call Window, loop through it, and I only interact with the first window to contain the location of the mouse-click. This way, other windows overlap won't get affected.[1]
Next, I had to make it so that two buttons that are overlapping don't get activated when the user clicks in the "intersection of both buttons." I handled this by using the same method I used above.[2]
But the problem I'm facing now is that, if I hold the left click, but then I decide not to click a button, I drag the mouse away from the button, and release the left click, so that the button-click event won't be activated. But, when I remove the mouse from the boundaries of the button, and say, into another.. the new button get activated. Which it should not.[3]
My set up is like this:
I have a class called Window.
In Window, I have a list of the class called Interface (similar to the Control class in WinForms).
And each Interface has a struct in it that contains 4 bools, if the left/right is currently down, and if they were down in the previous processing. (prevLeft, prevRight, currLeft, currRight)
So, I'm ready to discard that (I have not yet, so I still have the source code), but I need a good structure for making an object-oriented type of application. However, I am not using WinForms. I need help with the structure alone, so no actual code is necessary, description is enough. I need to avoid the 3 problems I mentioned above.

Creating your own Window Manager is not an easy task. I know it because I'm making one too ;)
You can use an existing, though maybe not the best solution, like for example Nuclex.UI, which I personally rejected when I first saw it, but if you're not dead set on making your own WM, I suggest to use that or hybrid WinForms-XNA approach.
But if you're really dead set on implementing a custom Window Manager, you have to understand how any other WM works. Since we're talking about XNA, it means Windows, and that means Windows Explorer, which is a great thing to learn from.
You have to recognize how the simplest things work, and it's really not so hard. The hard part is figuring out what logic is updated when, and how to not spend all the CPU on only UI updates. Let me just give you a few hints on how to solve the problems you mention in your question.
To keep track of all windows, I'm using a Dictionary<string, Window>, where Window is a custom class, and the string is its unique name for rare cases where I have to call windows by name. Think of it as a window GUID or Handle. But you can just make it so that a "Form" can only appear once, and store all references in static variables.
To make WM understand what control you're clicking I use rectangles and check if they contain a Point which is at Cursor coordinates and has {1; 1} pixel size, which is probably about the same way it's done in Windows Explorer. To do that your WM needs to know in which order to update the active windows. Usually you'd want to start from the topmost window and continue towards the end of the list of active windows. For that you can just iterate through the list with a foreach loop.
But that's not all, because every window itself is a Container, which means it contains other controls, some of which may even be Containers themselves, like WinForms Panel class. This means you have to iterate through each of the Windows' Children controls. The update order should make sense too - update from the topmost child to the bottommost, recursively for Container controls, in case they also have Containers in them. This basically means you'd want to implement a recursive GetAllControls() method for your WindowManager class that would iterate through all Containers and return a list of all Controls.
Drawing all those Controls should be done in reverse order of updating them, so you can just GetAllControls().Reverse() and iterate through that in a foreach loop.
Where to draw and what to update depends on all the parent containers the current container has and their combined offset from the top-left corner of the game window. I solve this by storing a ParentContainer reference in all children controls to get the appropriate DrawRectangles and update areas via recursive properties.
When you click somewhere on the screen and a click is registered on a Control, make the WindowManager remember that (bool clickRegistered) and not run any OnClick events on any underlying Controls.
Windows Explorer remembers the control you clicked and will activate its OnRelease event if the cursor is then released in the update area of the very same control. So basically Windows Manager only does something when you release the mouse button. You can make your WindowManager and Controls to handle click events differently, like firing an event right after you press the mouse button, i.e. OnMouseDown. But remember that Microsoft aren't noobs and there's a reason for that behavior in Windows Explorer, and it's because if you accidentally press a mouse button somewhere you didn't intend, you can still fix it by moving the cursor outside the pressed control's update area and not run its action.
At this point you might be thinking "Is it really worth implementing all this?" For me the answer was "maybe", because I was a total noob in both C# and XNA at the time I started, and now I know my game, which was originally supposed to use some Window Manager, is going to benefit from my own WM implementation far more than from ready third-party solutions. And besides, it's a great exercise in logic and programming.
But if you'd like to think of yourself as a game developer, you should think in terms of reaching your goal as quickly as possible, i.e. actually making a game, and not the game engine. So in this case, better make use of existing solutions and start selling your product.

Instead of having the structure with the 4 booleans (similar to xna), how about you make a way to tell where the mouse "is." So in a sense, the mouse is in Window number 5 which is Paint, and the user is holding the mouse down on interface/control number 2 which is a button.
That sounds like it could work.

Related

Universal point position

Is there a way to know the universal coordinates of the point?
I mean the following:
we have the button which could be pressed by clicking (500;500) when application maximized;
if it possible to know which point to click to press the button when the window of application is 600x600;
And so on. Is there any function to calculate such a point?
Thanks in advance!
I don't think there is a general function, since any developer could change the appearance based on screen size.
However, different approaches may work:
Somehow get a handle to the button/element of interest, then just call its clicked command, or use that handle to get the coordinate position on the screen.
For instance, with Web pages, you can get the specific HTMLElement by name (or id) and then work backward from there.
If it is a Windows Form application, you can actually get a handle onto the window of that application, and then get the desired element/component by walking through them all.
Create a list of the button locations under different maximization/restorings/movings and calculate the function from that. (Perhaps make a computer program to help with this).
As a last-resort, like for applications which randomly move their buttons around to confuse bots (I can't think of any real life examples), you might need to do image analysis on a screenshot.
The spy++ tool in/for Visual Studio has a finder tool that can help get the window/element names of windows applications.

How can I implement an interactive tutorial inside an app?

I have C# add-in for Word and would like to implement an interactive tutorial like games usually have. I'd like to somehow highlight (maybe by circling) certain visual elements and display text boxes that describe what the element does. For example, say the add-in is a generic workflow editor. I'd like to show to the user, step by step, what needs to be done by visually selecting elements and explaining what they do and what options (s)he has. My first question is: can this be done in C#? My second question is how? :) I suppose I'd have to get the positions of said visual elements and then draw an image on top but I don't know how that could be done.
I'm a bit disappointed that not even a single member of the Stack Overflow community took the the time to at least give a hint about this. But such is life and I'm just going to share my findings here because I'm certain someone else will benefit from them.
To be able to do an interactive tutorial you need three things:
a method to find where a control is located in terms of screen coordinates
a method to point the user to the control by highlighting it or
surrounding it with a red line for example.
a method to do its default action (ie: click a button).
In other words, the idea is to have some sort of window with text describing a control and some graphical way of indicating the control in the app. Optionally, the window could provide something like a ShowMe button which shows the user how to use the control by taking control of the mouse and keyboard.
This is trivial when the controls are made by yourself and thus have source code access. However, when doing such a thing for a black box app such as Word you can use the Windows IAccessible interface. Searching for that with your favorite search engine will yield everything you need to understand it and use it. IAccessible allows one to do many things but most importantly it can get a control's position and can also do the default action.
Having sorted out these things the next step is to figure out how to graphically point out the control. There are many ways to do this but in my case I chose to surround it with a red rectangle. I did this by creating an invisible, borderless form with an empty red rectangle on it. Having the control's position and size, I had no problems placing the aforesaid form over the control.
So there you have it. I laid out the building blocks that one needs to make an interactive tutorial for any app.

How can I detect the visibility of a control?

How to detect if a control is visible to a user? I have a control that displays status of application, and I want it to be updated (it's value to be updated, for example, increase the value of a progress bar, or change a label's Text property) only if a user sees it. No need to update it if a user has minimized the form that contains this control, or if another form overlaps this control. I just don't want to do extra calculations if the control isn't visible any way.
Also, how do I detect the events that hide/show this control?
This seems to be one of the topics which is frequently asked and which has many possible solutions, depending on the context.
First, for reference sake, some links to old discussions I stumbled over during my research:
How to check absolute control visibility and how to be notified for changes of absolute visibility
C# winform check if control is physicaly visible
starting position:
An IDE for heterogenous systems with plenty of different hardware and many different transport layers where some are really slow. One feature is to display memory values of remote hw in editors. A typical use case is to have >20 editors open, where each displays between 1 and 100 different values.
Because the transport layers are limited in bandwith I was looking for a solution to "prioritise" the data acquisition.
(partial) solution:
A visibility tracker which basically manages a specialised adapter for the control itself and every ancestor control. The adapter handles the visibility dependent on the ancestors type, ie. for a Control, it's just instance.Visible, for a TabPage I check which page is selected, ...
The manager's housekeeping then is just to keep track of parent-changes for all the ancestors so that it tracks the correct visibilities.
Catching whether or not the form has focus or is minimalized is your best bet, but I am not sure that hits all cases. In general, I would update regardless of whether the user is paying attention, unless it takes tons of cycles to get the information to the form, which I doubt. Setting up conditional bits in an application just adds another place for things to go wrong.
Let's examine this a bit. Minimized forms are one thing. If the form is minimized, you know the user is not even looking at it. But, if another form is in front, then you want to check if the form has focus. the problem here is a person can be using half the screen to watch while typing in word on the other half. Word has focus, so the form does not update, which is not your intention. Same could happen if you create another form in the application. the update no longer has focus.
THere is no magic "is the user looking at me" property in Windows.
This is never necessary. Windows won't generate the Paint event when a window isn't visible. If you implemented painting yourself then be sure to only do this in the Paint event and to call Invalidate() if there's a reason to repaint.
How often does the status change? It is probably better to update the status indicator when the status changes and not worry about visibility. Let's say that status changes on average every 5 minutes. The window/control may not be visible when the status changes but could easily become visible well before the next status update. In this case, the status indicator will be wrong until the next update because you avoided changing the indicator. Unless updating the status indicator is very expensive, it's probably better not to try to project what the user will do.
Edit after reviewing other comments:
I think you might be able to achieve better efficiency improvements by identifying a model for detecting/notifying status changes instead of calculating on demand. You may need to weigh the frequency of status changes against the frequency of visibility changes to your control.
Take a look at Control.Visible, and the Control.VisibleChanged event.

Is there a way to ask about the status of a drag operation while it is in progress?

I know that when a drag/drop operation is completed, upon receiving a MouseUp or Esc key event, it returns an enum that indicates what happened (Move, Copy, None, etc.) My question is this: is there a way to send back status information to the form/control that initiated the drag event, while it is going on?
The use case is as follows (think Visual Studio-esque layout manager for all of this): I am writing a layout/window managing component that allows regions of the layout to be dragged around. I use a transparent form to paint a semi-transparent overlay that changes based on where the mouse is dragging over, a la the preview overlay that appears when dragging windows around in Visual Studio.
Another motivation is that the serialization process I describe is relatively resource intensive, and I'd prefer not to do it if the dragging is all going to occur within the same process/window. So, if there was a way to lazily serialize only when an actual "drop" in another window happens, that would probably make all the difference in usability.
What I want to do is enable dragging between different windows or even different instances of the application. I've already plumbed out the serialization code and everything, but the issue is that, when I drag a chunk of layout into another window, the first window doesn't have any way of knowing that the mouse is now over another instance of the application, which is more than capable of painting its own overlay. So, the original overlay hangs around like an idiot and my program looks like crap.
Is there any way for me to pass along some kind of callback or is there any message or property I can listen for/poll during a drag operation that will tell me if my mouse pointer is over a region that can accept its data? Please don't make me resort to listening for the CursorChanged event, I've already lost too much self respect using reflection to hack around weird wpf/winforms dragging interop bugs. If anyone could suggest a clean resolution for this problem I would be extremely grateful.
Additionally, if anyone could point me to any favorite sites which describe how to go about doing reeeeally funky things with drag and drop, it would be appreciated, as I've found there is quite a lack of really nitty gritty information available about dragging. Usual things like custom cursors and the like are okay, but I'm probably more interested in Win32 black arts and the like.
UPDATE:
I actually just found out about the GiveFeedback event a second ago, came back to my question, and there it was. Huge facepalm moment. However, since I've got you here, what about my second question: is there any way to lazily load the information only when it encounters a valid target? Could I somehow implement my own IDataObject or do things get marshaled righ when the mouse leaves the form? GiveFeedback provides me only with whether there's a valid target under the cursor, but doesn't let me change what data is being dragged...
ANOTHER UPDATE:
Is there any way to determine the source of a drag operation? That is, when my control receives a DragEnter message, how can I tell if the source of the drag is my own control or a foreign one? I know I can hackishly encode it by messing with the AllowedEffects property, but is there any more direct route?
Check out the GiveFeedback event (there's a nice article here) - that sounds to me to be exactly what you're after.

Windows Handle Issue

Facing an issue where in the user objects goes more that 10000 in windows app and the app crashes.
After much analysis we realized that we need to get rid of the panels that we use to align the controls and may be reduce the possibility of user objects reaching 10000.
Our App UI is dynamically generated driven by a configuration and it can vary. So all the UI generation is happening dynamically.
Any help would be much appreciated
This is an unfounded suggestion, but remember to make sure that unneeded Controls always detach themselves from events they are be subscribed to. A Control that's still subscribed to an event of an "active" (what's the right term?) object can't be cleaned up.
Just as a note, the Chrome development team hit this problem too, and the scroll bar arrows (among other things) weren't drawing anymore when some internal gdi limit was hit. It is quite possible to hit this limit in a complex enough gdi app.
You might want to do some research and see how they fixed it.
As an alternative, you could consider using a different platform, either gtk or wpf would do fine and they don't use gdi handles to draw.
from here,
If your program runs haywire, you will
find that it manages to create about
10,000 window manager objects and then
the system won't let it have any more.
Why stop at 10,000?
The first answer is "If you have to
ask, you're probably doing something
wrong." Programs shouldn't be creating
anywhere near ten thousands window
manager objects in the first place.
There is no need for that many handles. I think you need a new solution.
I'm guessing this from your question, but you're probably putting this large number of controls on a scrollable panel or a tab control with multiple tab pages, which means that most of these controls aren't actually visible to the user at any given point in time (because they couldn't possibly all be visible at once).
If you have all of these controls on a scrollable panel, one possible solution is to only load and display the controls that are on the visible portion as the user scrolls around in the panel. As the user scrolls, you would unload and dispose the controls that are no longer visible.
If you have all of these controls in a multi-page tab control, you can use a similar strategy and only load the controls on a tab page when that page is made visible (and unload the controls from the previous top-most tab page at the same time).
Another general strategy is to break up your one monster form into a large number of UserControls, and only show one of these UserControls at a time.

Categories

Resources