N.B THIS QUESTION HAS BEEN UPDATED, READ FURTHER DOWN
Hi,
I want to create a custom context menu that has 4 sub-menus, each in their own quadrant (top left, top right, bottom left, bottom right). Similar to 3ds Studio Max.
This is how I've approached the issue so far:
- create a custom WPF control, derive from ContextMenu[1]
- declare 4 dependency properties of ContextMenu, these will be menus displayed and can be set from XAML.
- AddOwner to the ContextMenu.IsOpenProperty, adding a property changed notification.
- when the IsOpen property changes set the IsOpen property of 4 child context menus.
- using ContextMenuService set the Vertical and Horizontal offsets of the context menus to make them appear in each quadrant; binding the actual height and width properties to calculate the offsets.
[1] Need to derive from context menu otherwise you cannot assign it to the ContextMenu property on the Window.
This appears to work, there are issues with the menus NOT staying open (they're being closed as the focus is outside the menu) but I'm sure using Reflector.NET find a way around this.
This is my first custom WPF control that I've attempted to write; and not sure if this is best approach.
Any suggestions/ideas on how to create this Quad Context menu?
I can think of two approaches, neither one are necessarily that clean but has the potential to work if you have the time.
Approach 1
Use a context menu but through setting the Template make it so that the ContextMenu really just shows a control that happens to open other windows. Perhaps tricks can be done so that opening these secondary menus doesn't take the focus away. This might end up still causing the original problem.
Approach 2
What happens if you set ContextMenu.StaysOpen to false and then try to control when the menu closes yourself?
UPDATE
OK. After playing around with custom controls I have something that is working better than before. All menus now stay open.
What I've done:
- created custom control named QuadContextMenu deriving from ContextMenu, override the default style key.
- the style for the control has 4 popup primitives:
- each with PART_ name so I get a reference to them when the OnApplyTemplate.
- each has child QuadMenu, which is custom control deriving from MenuBase.
- each has their VerticleOffset and HorizontalOffset properties binding to the ActualHeight and ActualWidth of the child [2].
QuadMenu Style/Control Template:
- has a classic border with a dockpanel, and a border with a textblock to put the menu name.
[2] How to position the menus into the Quad:
- TopLeft is offset negative the actual width and height
- TopRight is offset negative the actual height
- BottomLeft is offset negative the actual height
- BottomRight is NOT offset and is in it's original location
NEW QUESTIONS/ISSUES
These are the next challenges to making this control:
1. at the moment keyboard and mouse is locked/captured by the first menu you focus on and the others are not available by hovering over them. which means you can only use 1 of the menus.
2. flip the MenuItem so that for the TopLeft/BottomLeft the submenu opens on the left hand side.
I've already attempted to replace the menu item by overriding the GetContainerForItemOverride methods to return QuadMenuItem. QuadMenuItem is a custom control derive from MenuItem. When I do this however I lose the submenus functionality which is frustrating.
If anyone has any tip/tricks/suggestions on how to tackle these issues it would greatly appreciated! :D Thanks
Related
I'm making a custom dropdown button (since the one included in wpf requires too much hacking to style right). Now that i got the button bit out of the way i need to add the drop down part.
My first thought was to add a stackpanel and use that to contain the items but it gets cut off if it leaves the borders of the grid that the button is in. Next up was the popup primitive, it gets on top of everything nicely enough but position wise it just free floats and i haven't figured out how to make it follow the button it was spawned by. I also tried using contextmenu but that seems to have no positioning controls at all and just sits where the mouse made it..
Anyways wpf is a big package and I'm just getting into it, anybody know which direction i might find what I'm looking for?
Preferred approach normally is to use a Popup. You got two very important properties with a Popup
PlacementTarget and Placement
Setup a binding for PlacementTarget on the Popup to your custom Button and then use Placement to position the Popup accordingly w.r.t to the PlacementTarget(Button)
Placement accepts an enum of type PlacementMode which gives you quite a few options to position the Popup.
I have a control template defined, call it myVal, that is used for validation - this is then used for example in a Style targeting textbox where its Validation.ErrorTemplate is set as
Now say there are a number of such textboxes that sit in a view and that this slides in using TranslateTransform and BeginAnimation.
The result is that the adorner used in the ErrorTemplate doesn't follow the position of the textboxes as the view transitions - instead these stay in the starting position. However, the adorners reposition themselves correctly in relation to the textboxes as soon as I set focus or events such as mouse move.
How can I get the adorners to show in the correct position after the transformation without having to change the focus? Is there a way of delaying the validation until after the transition...or how can I "revalidate" the properties once the animation has finished? I read somewhere about calling invalidatevisual but can't see how I'd do that. Any help is much appreciated.
Cheers
Two ideas:
Try adding an AdornerDecorator around the textbox, or around the group of textboxes. This will tell WPF to add another layer for rending adorners. Adding a layer "closer" to the textboxes might help.
If you want to tell the adorner layer to re-render itself, then you can use something like the following code:
var al = AdornerLayer.GetAdornerLayer(myTextBox);
al.Update();
I am trying to achieve functionality similar to that of a Popup, without using a Popup, but instead adorning my ContentControl with a basic adorner. Basically, I want the ContentControl to have an "overlay" effect, whereby it is the topmost object, above all other elements - similiar to that of the Popup control.
Here is the problem that I am running into, and I am hoping that someone can point out where I am going wrong:
I have a stand grid with two row definitions. The first row contains a UI element - for example, a rectangle. The second row contains a custom control that I have developed to emulate the functionality of a "drawer" sliding out. Basically, when I click on button, I am going to animate a TranslateTransform to "slide" my ContentControl "up". This works fine - except that it gets cropped underneath the rectange in the first row of the grid. If I remove the row definitions in the grid, then when the desired behavior is achieved - the ContentControl is moved "up" and partially "on top" of the rectangle. The rectangle is merely a place holder for what I am trying to achieve. I basically want to have a drawer type control that can slide out and be on top of all other controls.
I am somewhat new to using the Adorner class, so, I am hoping that someone can please point out where I am going wrong.
Thanks.
Chris
Change the parent of the adorner to the full grid, and not just your control. If you put a control in a grid row, and set the adorner to adorn the control, it will usually be clipped to that row because the control is.
i want to write some control that will contain 2 button and listView.
Pressing button 1 will scroll the listview up.
Pressing button 2 will scroll the listview down.
The direct scroll of the listview will be unavailable - ( will be invisible ? ).
I don't find the listview method 'scroll-up' / 'scroll-down' that i could callon the button event.
How can i make the listview scroll to be always visible ?
Someone can help me here ?
Thanks.
You have two options here, one is easier than the other.
First option (the easier, but slightly hacky way): Using the VisualTreeHelper, get a reference to the ScrollViewer in the ListView's ControlTemplate. Then you can use the LineUp and LineDown methods to scroll the content up and down and the static SetVerticalScrollBarVisibility method to hide the scrollbar. I personally wouldn't use this approach because I don't like relying on the Visual Tree which can change.
Second option (a bit harder, but not too bad if you know how): Write a new Control Template for the ListView (might need to alter the templates for it's ScrollViewer + ScrollBar), adding in two buttons that call the ScrollBar.LineUpCommand and ScrollBar.LineDownCommand. If you want to do this, I'd suggest getting a copy of ShowMeTheTemplate, then you can just copy and paste the original(s) and modify.
Hope this helps point you in the right direction.
I'm writing an XBAP with a complex Popup (Canvas Z-index of 99 with a grid on it...) that I would like to "attach" to the button that opens it and follow that button around wherever it goes on the screen. For example, if the button is in a ListBox or an XamDataGrid I would like the popup to follow the button as it scrolls through. If it is beneath an Expander I want it to stay attached to the button when the expander forces it to move, etc.
Any Ideas?
When using a Popup, neither PlacementTarget nor CustomPopupPlacementCallback is used after the popup has originally appeared. So any use of these properties will not allow the popup to track the button as it moves.
Several ways occur to me of achieving what you desire:
Attach a custom Adorner to the button and put the popup inside it. Disadvantage: Popup is not connected to Button or surrounding elements, so it won't inherit properties & DataContext from them.
Attach a custom Adorner to the button. This adorner will get measure and arrange calls when the button moves relative to the AdornerLayer, allowing you to manually update the Popup position. As long as your AdornerDecorator doesn't move relative to your Window (eg if it is the direct child of the Window), you can easily detect the AdornerLayer being moved by monitoring changes to Window size. Disadvantage: Complex to code & get right.
Don't use a Popup at all. Instead wrap the button in a <Grid> alongside a <Canvas> with zero width and height and the desired position. Inside the <Canvas> add the UserControl for the popup with an appropriate ZIndex. It will extend past the edge f the Canvas, which is just fine in WPF. Instead of using a Popup control just control the visibility of the UserControl. Disadvantage: Will not really be totally on top of all other objects, can't extend off edge of window (may not be an issue for XBAP, though).
I'm not sure if it will auto-update for you or not, but the PlacementTarget property allows you to specify a control to position the popup relative to. If that doesn't work, then maybe CustomPopupPlacementCallback will do the trick?