How to turn on submenu triangle from code for a MenuItem? - c#

I generate and set my own context menus and group the items into submenus as per
SubTitle.Items.Add(..);
These popup submenus work fine, almost all is nice .. but...
I cannot get the submenu triangle to show up UNLESS i set the MenuItem Role (which you can set per Trigger in Style) to e.g. TopLevelHeader etc.
MenuItem.Role has no direct setter, so i would be obliged to create my submenu MenuItems as XAML static objects that i then endow with nice styles and so they'd get the submenu triangles, too.
The problem is, my context menus are fully programatically derived from e.g. a DB and if I reuse static XAML MenuItems i need set Garbage Collector onto them to break Parent relationship before i can reuse them, and/or i have to know no of these elements in advance.
Is there some way to:
MenuItemThatIsTitle.Role = MenuItemRole.TopLevelHeader;
//(in order to get the triangle?)
..from the code?
===
example code - adding a submenu to context menu from code:
contextM.Items.Add(new Separator());
var TitleH = new MenuItem
{
Header = SettingsDomain.Default.TitleHeader,
FontWeight = FontWeights.Bold,
Height = 25,
IsEnabled = true,
};
if (!(TitleH.Items.Count > 0))
//TitleH.Role = MenuItemRole.TopLevelHeader; to get the triangle..
{
var listmen = List.Select(c => cMenuItem(c)).ToList();
TitleH.ItemsSource = listmen;
}
contextM.Items.Add(TitleH);
contextM.Items.Add(new Separator());
NB: no relevant XAML

Solution: do not set the width of menu to some neat width, it will cut off the right hand side of the menu TOGETHER WITH the submenu marks (triangles) that will simply disappear.

Related

Add key bindings to control specific context menu

I'm unable to get the keyboard shortcuts working on context menu items belonging to specific controls and I was wondering if anyone could help me.
Use case: I'm trying to write an attached behavior that can be applied to various elements in order to provide undo/redo context menu functionalists to those controls. The context menu part is working but the key bindings don't seem to work.
What I've tried so far:
Attempt 1:
var menu = new ContextMenu();
var undoCommand = new DelegateCommand(Undo);
var undoMenuItem = new MenuItem
{
Header = "Undo",
Command = undoCommand,
InputGestureText = "Ctrl+Z",
};
menu.Items.Add(undoMenuItem);
AssociatedObject.ContextMenu = menu; // AssociatedObject is a textbox as an example
Attempt 2:
var menu = new ContextMenu();
var undoCommand = new DelegateCommand(Undo);
var undoMenuItem = new MenuItem
{
Header = "Undo",
Command = undoCommand,
};
undoMenuItem.InputBindings.Add(new InputBinding(undoCommand,
new KeyGesture(Key.Z, ModifierKeys.Control)))
menu.Items.Add(undoMenuItem);
AssociatedObject.ContextMenu = menu; // AssociatedObject is a textbox as an example
Neither of these seem to be working. The context menu is selectible from the UI (right click on a text box for instance and select "Undo") but the keyboard shortcut is not fired.
Is there a way to do this in an attached behavior? I would like to avoid having access to the underlying Window element inside my attached behavior if possible and would like the keys to work within their respective bounds (for instance, only if the text box is focused should Ctrl+Z cause an Undo on that text box).
Many thanks

Silverlight DataGrid: Set visibility of control within row details template?

I have an instance where the details template has two main stack panels within it.
One is set to collapsed by default. However upon right click and choosing, that stack panel's visibility is to be triggered. This may occur when the row details are expanded already.
However when the row details are expanded and the right click is chosen to view that stack panel, I'm programmatically changing its visibility to Visible but it's not becoming visible.
Is there a secondary call I need to make to update the UI and force the visibility setting to take?
Here's my code:
private void SetWFHistoryVisibility(bool show)
{
var elements = VisualTreeHelper.FindElementsInHostCoordinates(position, this);
var row = (from element in elements
where element is DataGridRow
select element).FirstOrDefault() as DataGridRow;
if (row != null)
{
DataGridDetailsPresenter presenter = VisualHelper.FindVisualChild<DataGridDetailsPresenter>(row);
if (presenter.Children.Count > 0)
{
var grid = (from el in presenter.Children
where el is Grid
select el).FirstOrDefault() as Grid;
if (grid != null)
{
StackPanel wfgc = grid.FindName("wfGridContainer") as StackPanel;
if (show)
wfc.Visibility = System.Windows.Visibility.Visible;
else
wfc.Visibility = System.Windows.Visibility.Collapsed;
}
}
}
}
You may need to call DataGridView.Refresh(), or equivalent depending on your panel/layout. Also it can be a parent control as well needing to update/refresh.
I know that in Silverlight DataGrid does have an AutoRefresh, but it is primarily controlled by an EventTrigger either in a DGV or DataGrid class. As you discovered it isn't perfect.
Perhaps you can trigger one of the following events and allow the Control to work out it's own view update.
https://msdn.microsoft.com/en-us/library/system.windows.controls.datagrid(v=vs.110).aspx
This one solution:
Dispatcher.BeginInvoke(() => {
//set the added control to be visible
});
Was suggested by a user on MSDN for StackPanel refreshing.
Several other suggestions suggest utilizing a combination of:
_.Invalidate()
_.UpdateLayout()
For forcing a refresh.

AdornerLayer.GetAdornerLayer() return NULL for all controls in a Panel

I'm facing the fact that i cannot understand Well how AdornerLayer is added for UIElements.
I have such a situation:
I have a WPF Form which is built with 3 controls:
A Grid on which are 1 Button and 1 TextBox.
In my System, when I click to open this Form, all 3 elements have AdornerLayer not null .
var controls = _frameworkElementProvider.GetUIElements(Content);
var controlsWithAddorner = new List<FrameworkElement>();
foreach (var control in controls) {
var adornerLayer = AdornerLayer.GetAdornerLayer(control);
if (adornerLayer != null) {
controlsWithAddorner.Add(control);
}
}
The collection controlsWithAddorner contains all my 3 controls.
The method GetUIElements(FrameworkElement parent) returns an IEnumerable<FrameworkElement> in which are all controls within a Panel.
I have such a functionality:
Refresh Form Designer. Which recreates the xaml for that Form.
After that Refresh is done, I check the list of controls for AdornerLayer. For all controls the AdornerLayer is null.
The problem is here, I cannot understand where AdornerLayer (s) are lost?
Should I take care To add them forr each UIElement when I Refresh the Designer of the Form?
Please advice me with some suggestions.
Thank you!
EDIT:
I'll show all the solution if other will encounter such problems :)
The mission is: When there is a SelectedControl in designer, keep it selected even a RefreshDesigner is done.
RefreshDesigner functionality recreates the xaml for the whole form.
// Refresh the Designer
private void RefreshDesigner() {
Content = _xamlProvider.ParseXaml(_xaml.ToString());
//Here was the Problem. All visual child elements of the Content wa not updated after xaml recreation.
//By including that call -> solved the problem
Content.UpdateLayout();
}
Firstly: The xaml of the Form is Updated by using the ParseXaml() method from XamlProvider
// in XamlProvider class
public Panel ParseXaml(string xaml) {
var regex = new Regex("<Grid ");
const int first = 1;
xaml = Regex.Replace(xaml, #"xmlns:x=""http://schemas.microsoft.com/winfx/2006/xaml""", string.Empty);
xaml = Regex.Replace(xaml, #"xml:space=""preserve""", string.Empty);
//...
xaml = Regex.Replace(xaml, "<BindingGroup .*/>", string.Empty);
var content = (Panel)XamlReader.Parse(xaml);
return content;
}
Secondly: Content.UpdateLayout();
Ensures that all visual child elements of this element are properly updated for layout.
MSDN Official source
After that, All elements have AdornelLayer not Null and I'm able to set the Adorner Border for preciosly selected control in designer.
After the Form is Refreshed, call Content.UpdateLayout(); to Ensures that all visual child elements of the Content were properly updated for layout. MSDN official

WPF How to remove a grid?

In my WPF application I have created and added a new Grid, I already know how to remove all Children from it:
private void ClrScr()
{
for (int i = GridName.Children.Count - 1; i >= 0; i--)
{
GridName.Children.RemoveAt(i);
}
}
But I have no idea how to kill the grid itself, my tries:
GridName.Exit/Disable/Something; /// <--- no Idea what am I doing...
this.Controls["GridName"].DIEEEE;
Sadly I have failed... I am very new to WPF, I've mostly played with WinForms... Help?
You can remove the grid using it's parent.
e.g: If you have three grids named grid1, grid2 and grid3 in a dock panel control named main, you can remove these grids like this:
main.Children.Remove(this.grid1);
main.Children.Remove(this.grid2);
main.Children.Remove(this.grid3);
All the controls in WPF has parent except form. If the Grid you are going to delete is the first then you can do this:
var parent = myGrid.Parent;
Window window = parent as Window;
if(window!=null)
window.Content = null;
Sometimes Grid can be child of a control which doesn't have Content property. If it doesn't have Content then it must have Children or Items.

TreeListControl within each NavbarControl Group

I'm very new to WPF and I'm attempting to create a treelist navigation within each navbar group. Because the number of navbar groups and treelists are dynamic I have to make them in code rather than them be pre-defined in XAML.
I have tested the following so far which is meant to define the navbar group's content rather than use the default item
private void CreateGroup2(NavBarControl navBar)
{
NavBarGroup group2 = new NavBarGroup();
group2.Header = "Custom Content";
//Specify that the group's content should be defined via the Content property
group2.DisplaySource = DisplaySource.Content;
TreeListControl tree = new TreeListControl();
tree.ItemsSource = TreeList_DataBinding.Stuff.GetStuff();
group2.Content = tree;
navBar.Groups.Add(group2);
}
This gives an Exception: Grid.InfiniteGridSizeException: By default, an infinite grid height is not allowed since all grid rows will be rendered and hence the grid will work very slowly. To fix this issue, you should place the grid into a container that will give a finite height to the grid, or you should manually specify the grid's Height or MaxHeight. Note that you can also avoid this exception by setting the TreeListControl.AllowInfiniteGridSize static property to True, but in that case the grid will run slowly.
I'm a little confused as I'm not using a grid? Can anyone give any pointers what's wrong and how I can add a treview under each navbar group?
Thank You
It feels a bit wrong answering my own question but I managed to get it working using the following
private void CreateGroup2(NavBarGroup navBarGroup)
{
System.Windows.Controls.TreeView treeview = new System.Windows.Controls.TreeView();
TreeViewItem nod = new TreeViewItem();
nod.Header = "Tree Node1";
treeview.Items.Add(nod);
TreeViewItem nod1 = new TreeViewItem();
nod1.Header = "Tree Node2";
treeview.Items.Add(nod1);
TreeViewItem nod2 = new TreeViewItem();
nod2.Header = "Tree Node3";
nod1.Items.Add(nod2);
//StackPanel stcPnl = new StackPanel(); /optiona
//stcPnl.Children.Add(treeview);
//navBarGroup.Content = stcPnl;
navBarGroup.Content = treeview;
navBarGroup.DisplaySource = DisplaySource.Content;
}

Categories

Resources