We have a custom timeline-esque control we're creating in C#/WinForms. In it, we have a bunch of keyframes that, when clicked, popup a properties dialog with specific and dynamic controls based on the type of keyframe it is.
This box is currently drawn within the timeline's clip rectangle, so if it's bigger than the available space only shows part of the panel. I wouldn't mind allowing this panel to break out of the parent's clip rectangle, and even overhang outside of the form itself.
The only problem is I don't know where to begin! Thanks!
Related
The questions says it all.
How can I move a control, say a PictureBox between multiple panels, or betwween a panel and a flow layout panel.
I'm aware I can drag and drop controls between multiple panels and such, however this is does not make the control visually movable between the containers. The mouse only changes to a diferent cursor and after you drag to the other control and release the mouse button the control appears on the other container. I require the control to be visually movable.
Can someone provide a simple example, so I can extract the idea to apply to my situation.
NOTE: Runtime of course.
Assuming you need it runtime:
You can save the control as bitmap using Control.SaveToBitmap method
Create cursor from image.
Set the current cursor which we created from control.
Once drag and drop completed reset the cursor.
Let's take a simple example of dragging a button around.
Suppose you have two types of container controls:
1) an X-Y layout
2) a flow layout (assume left to right)
When you click to drag the button, record the x-offset and y-offset from the click to the top left corner of the control. As well, record the index of the control within the Controls collection.
As the mouse moves, first check if the mouse has changed container controls.
If so, then remove the button from its current parent and add it to the new parent.
If the button is added to a flow control, then you need to calculate the new index. To do this, calculate the distance from the mouse to the closest edge of a bounding box of all other controls. Then if the mouse is left of the center of that control, insert to that control's index minus 1, otherwise insert to the right of that control (index + 1).
If the button is added to an X-Y layout, then the index doesn't matter that much. You can simple just set the button's location relative to the mouse plus the x-offset, and y-offset.
As the mouse is dragging, you will need to force the controls to refresh. I think calling Invalidate() on the container control should be sufficient.
This should give you the basic idea that you could use to start coding something.
I have a panel (here called parent), where I draw a calculated picture.
Some rectangular areas of this picture shall higlight by hoovering over.
It is the same behaviour like on a web page using , and , e. g.
the german map on the right upper side.
At hoovering the according rectangle shall be covered by a half transparent blue .
(And depending on keys like Alt, Ctrl and/or Shift in other colors, and clickable).
The first solution was a single instance of a transparent Panel - inherited from the Panel class.
In the hoovering event of the parent I moved and resized the single instance to the right place, changing the color.
This had some problems:
* moving and resizing (SetBounds()) fired MouseLeave event of the parent and a MouseEnter event of the single panel. The events had to be adapted accordingly to get it working correctly, I did it, but it is was very slow, due to finding the right map area from the list.
The second solution was to generate dynamically an instance of a transparent panel for each map area.
Each transparent panel had to set the e. g. Color.FromArgb(50, Color.Blue) at entering, and
remove it at leaving the panel.
But it seems to be even slower than before.
If the mouse hurries over several maps, they are all drawn like hoovered, and slowly get transparent again.
Does anybody know a good solution for this requirements:
at picture resize in parent panel, map etc. has to be changed as well
partly transparent highlight hoovered rectangle area.
detection of Ctrl/Shift/Alt as events for an area and change of the color.
detect click events there
Are there other controls I better use for this purpose?
Thanks for on practice based ideas.
PS: The world map with satellite pictures shows better what I want to do:
At hoovering the background is still visible more or less.
But in my case the parent image, its size and the maps are calculated at runtime
(after settings are completed by the user).
Solution
Description
I found now a solution, that reacts sufficiently fast to hoovering areas with the mouse.
The Main pictures is drawn in a PictureBox instance, in more detail its property Image is assigned.
The SizeMode property is set to Zoom, that automatically centers and resizes the image with keeping the aspect ratio of the assigned image.
I use a dynamically created Picturebox instance for each map area (childs), that is invisible, if the mouse does not hoover over it.
At hoovering over the map area the child shall appear - this is done in the mouse move event of the parent PictureBox,
where I iterate over the children, detecting whether the mouse position is in the bounds of a child.
The found child is set visible. Therefore the mouse enters this child control.
In the leave event of the child control I set it invisible again.
I experienced losses of mouse leave events for the child controls, if the mouse is moved too fast over all map areas.
I assume, if the mouse pointer already left the area before it has been set visible, the event is never raised.
The solution is, that all (other) child controls are set invisible, if in the mouse move event handler of the parent control does find no (a) child control.
Steps to implement
What to do, to implement my solution:
Use a designed parent PictureBox instance.
Assign the (dynamically) drawn picture to the Image property
Set SizeMode to Zoom
a list of Picturebox instances as form field
At assignment of a newly calculated image to the parent Picturebox instance:
remove all child controls, its event handler, its entry from the list, if they already exist.
create dynamically a PictureBox instance for each map area and add it to a list.
add it to the parent PictureBox instance as a child control.
set its Tag property points to a data object containing the origin map bounds and the object represented by the rectangular map area.
set the bounds to the map area scaled and centered according to the bounds of the parent Picturebox instance, that it suits the automatically zoomed image.
register a mouse click event
register a mouse leave event
set background color to e. g. semi transparent green
set Visible to false
In the parent Picturebox instance the mouse over event handler does:
finding the child Picturebox instance, where the mouse points at
if found it sets the found child visible
set all (other) child Picturebox invisible
In the parent Picturebox instance the resize event handler does:
scale/move all the map area Picturebox instances according to the bounds of the parent image and the bounds of the parent Picturebox instance.
The mouse leave event of each map area Picturebox instance set itself invisible (losses of events previously mentionned).
The mouse click event of each map area Picturebox instance makes whatever shall be done by clicking the map area.
Here playing a sine tone of the right chromatic pitch.
Pictures
The pictures below show the prototype with the map areas (not yet correctly aligned, some offset):
The first picture is for illustration of parent picture and all map areas.
The main picture (scale) and all map areas (dynamically created child Picturebox instances) are drawn in the first picture (by disabling the invisible action for map areas).
The second image is productive, where the mouse hoovers over tone G4.
In the second image the form has been resized - therefore the parent image is automatically been centered and resized.
The map areas were simply changed in their Bound property in the resize event handler of the parent PictureBox.
And the invisible action has been enabled for the map areas.
I checked the ImageMap:
It is a user control, that contains a static image, that was drawn before compile time.
At runtime the clickable areas are added – rectangle, polygon, ellipse are possible.
It uses a PictureBox, that is created as child of the ImageMap (that is inherited from UserControl).
It registers a Click, a Move and a Leave event to its event handlers.
In these event handlers it checks the current position against the list of paths in a GraphicsPath instance and returns an index (int).
The ImageMap keeps track of the last selected index (-1 = no object selected), sets the cursor accordingly (hand or default), sets or removes the tooltip.
But it has no hoover event, it does not change the area at hoovering, like I need it too.
Therefore I can need ImageMap it for proper detection of area,
but my picture is drawn at runtime!
And still I have to switch on and off the rectangles with its semi transparent layer.
I got the idea to use the property Visible to switch on and off the controls for the areas.
That it is easier to draw, I will set the background to the part of the parent image,
covered with the semi transparent color - this is a workaround, that is possible,
because the maps are relatively fixed to the parent picture.
If I have time, I will test this solution idea - it is a private project, therefore I can not work fulltime :-).
The scenario is that i want the user to create a shape in a small panel that opens (the added shape can later be placed on the canvas), but for a better reference, i want the user to be able to move the semi-transparent panel somewhere on the canvas and then draw with the accurate reference.
Please tell me:
Which panel type to use
How to make it moving by clicking the mouse on the move button (not the whole panel as dragging will be used for drawing lines) and move it around.
How to make it semi transparent.
How to make it appear and disappear (this should be pretty simple)
How to somehow limit its movement inside the canvas so it cannot move on the ribbon
And I really really hope there will be something built-in in WPF that i'll be able to use, and i will not have to do it the hard way i.e. create a rectangle, and do customized hit testing on it to allow the user to draw on top of that rectangle, make that rectangle transparent, and add graphics items for the buttons and controls on that rectangle "panel".
I am asking this because i have never seen such feature in any Windows application and i have no idea what to use for this purpose and how to implement it. The closest thing to what i want is in Adobe Acrobat Pro, which is the small preview of the page that appears when i double click with the middle mouse button. It doesn't move, nor it is transparent or can be drawn upon, but scale and shape wise i want something similar.
You should be able to place a second Canvas inside of your main canvas, and place whatever UserControl you'd like with your "view" inside of it.
You'll have to handle the mouse click/drag for moving it around yourself, but otherwise, it should be very straightforward.
I am trying to overlap panels so that whenever I click a button A certain panel will be visible.
However doing this job is very tricky as because the panels doesn't overlap.
for ex. I have panel 1 and panel 2:
I make panel 2 the same as panel 1,
Whenever I position them on the same position...
Sometimes, the panel 2 becomes a member of panel 1 and whenever I set the visibility of panel 1 to true panel 2 also shows up.
What I want is that the two panels to overlap each other.
"Btw, I'm making a vertical tab that's why I thought that hiding, unhiding the panels might be my best approach.
Is it possible to make the panels overlap each other?
The designer is fighting you do get them overlapped. You need to use a little trick to stop the bottom panel from sucking up the overlapping panel. Put it overlapping panel somewhat off towards the upper-left so they are truly overlapped. Then put it in the right spot by adding code to the form constructor:
public Form1() {
InitializeComponent();
panel2.Location = panel1.Location;
panel2.Size = panel1.Size; // optional
}
Another way to do it is with View + Other Windows + Document Layout. You can drag and drop the inner panel to the outer container (form). You will however have to edit the Location property by hand.
It is absolutely possible to have overlapping panels.
The problem you're facing is that the GUI editor treats your panel as containers (that is true) and as long as you place something (including other panel) within a panel it is "nested" in this container.
To avoid this behavior first place one panel and position/size it appropriately. Then right-click on it and choose "Lock controls". That will lock all current form controls and you will be able to put new controls -- including panels -- directly over them, without any fear that something will be nested or somehow placed inside an existing container.
And of course your controls can overlap -- consider only the order or control creation, that will also define the z-order of them in the form -- controls added later and drawn later and thus are positioned on top of those added earlier.
EDIT: Unfortunately I wasn't completely right with my answer. Locking panels does not prevent them from sucking up the controls places entirely within them. But in case of a partial overlap both containers are created on the same level of deepness, so the problem does not exist in case of overlapping panels, as it was asked in the question.
The Panel has a property called Location, which you can modify to fit your needs. As long as you manage to place your Panel so that it is given the correct parent, you can change the position by altering the Location property later. There's really no need to put design code into the constructor or anything such.
And to place the panel in the correct parent, just have the parent selected and double click the Panel control in the toolbar, rather than dragging it into the form manually. There's really no need to try to fight the designer on this one.
I need to create a form with two panels:
1. Destination
2. Source
On the source panel there will be picture boxes. I need to be able to move it from source to point at destination panel with mouse.
I have a problem connected with different coordinates of the panels.
Please, help with advice or an idea what to do.
Moving those controls requires changing their Parent property. That's not easy to do, there is no good time to do this while the user is dragging with the mouse. You'll also get the effect of the panel clipping the control, you cannot display it on both with half of the control on one and the other half on the other panel. And yes, you have to change the Location property of the control when you change the parent or it will jump.
Punt the problem, don't use two panels. It only has to look like a panel, easily done by drawing one in the form's Paint method (or OnPaint override, better). Use e.Graphics.DrawRectangle or FillRectangle.