menuItem.InputGestureText not showing shortcut text - c#

In my application i am creating menu items in code. This is the code for creating menu item
public MenuItem getMenuItem(string toolTip, string menuTitle, Uri menuIconUri, int? tagOnlyForHeaders, string shortCutKeyText ="")
{
MenuItem menuItem = new MenuItem
{
ToolTip = toolTip,
Header = menuTitle
};
if (menuIconUri != null)
{
menuItem.Icon = new Image
{
Source = new BitmapImage(menuIconUri)
};
}
if (tagOnlyForHeaders != null) {
menuItem.Tag = tagOnlyForHeaders;
}
if (shortCutKeyText != "") {
menuItem.InputGestureText = shortCutKeyText;
}
return menuItem;
}
but if I pass value for input Gesture like ctrl+n it is not displaying the shortcut text in the menu item while the application is running. what is wrong in this code. can anyone tell a solution for this.
This is how i add menu items
//Top level Op menu
var opMenuItem = utils.getMenuItem("OP", MenuName, null, 0);
//op registration
var RegistrationMenuItem = utils.getMenuItem("New Registration", "New Registration",
new Uri(baseIconUri + "newRegistration.png"), null,"ctrl+n ");
opMenuItem.Items.Add(opRegistrationMenuItem);
return opMenuItem;
and this is added to the main menu
mainMenu.Items.Add(menuItem); // in this case the "opMenuItem"

The InputGestureText only works when the MenuItem is not a direct child of the Menu(i.e Not directly inside the Items collection). If you want to see the InputGestureText, you need to add MenuItem to the Items collection of another MenuItem. To illustrate what I said, here is an example in XAML.
<Menu>
<MenuItem Header="File" InputGestureText="Ctrl+Z">
<MenuItem InputGestureText="Ctrl+C" Header="Open"></MenuItem>
</MenuItem>
</Menu>
The InputGestureText of the MenuItem (Header = "File) is not visible but that of MenuItem (Header="Open") is visible.

That was a problem with the library(Material design library) i was using to style user interface . I've reported the issue and it is fixed.

Related

TreeView auto-selecting parent after user update selected child WPF command

I have a 2 level WPF treeview. When I click on a child item, the correct selectedCommand is triggered and all works well.
But when I click on my details view and update the field of this selected item, it unselect my childItem and fired the parent command because the parent is selected, but I need to just keep my childItem selected.
I have found some topics about the same problem, but I use command for my binding and not just code behind so I don't know how to make this solution work for me.
Dispacher.BeginInvoke method : > Parent TreeView Item ghost selected event!
e.Handled : > WPF TreeviewItem parent selected event fired after selecting a child?
And a topic that said it's a focus problem : TreeView auto-selecting parent after user selects child
Trigger of my command :
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<i:InvokeCommandAction
Command="{Binding ItemSelectedCommand}"
CommandParameter="{Binding SelectedItem, ElementName=TreeView}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
My Treeview :
<TreeView x:Name="TreeView"
ItemsSource="{Binding Modules}">
<HierarchicalDataTemplate DataType="{x:Type module:ParentViewModel}" ItemsSource="{Binding ChildItems}">
<TextBlock Text="Parent"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type module:ChildViewModel}">
<TextBlock Text="{Binding Path=childName}"/>
</HierarchicalDataTemplate>
</TreeView>
And here is my command which is in my ViewModel file
public ICommand ItemSelectedCommand
{
get
{
return _itemSelectedCommand ?? (_itemSelectedCommand = new CommandHandler(param => SelectedCommand(param)));
}
}
public void SelectedCommand(object selectedItem)
{
//code to activate my details view with prism
if(selectedItem.GetType().Name == "ParentType")
{
ActivateParentView();
}
else
{
ActivateDetailsView(.....); //activate child view
}
}
So, I trigger the same command when selected an item in my treeview but when I select the child item, the parent event is also triggered so my command is triggered 2 times and activate my parent view and don't stay in my child view. How can I stop propagating the event if I have already pass through child command ? How can I make it working with my command and not in code behind ?
EDIT
Here is my function ActivateDetailsView() which is called by my SelectedCommand
I have 13 modules and I have one detailsView in each module, so when I click on my selected item, I will search using reflexivity for the view I need to activate and I use PRISM library to activate it.
private void ActivateDetailsView(string nameTypeItem,IRegion tabConfigurationRegion, ModuleItemBaseViewModel selectedItem)
{
try
{
string viewName = "ModuleItem" + nameTypeItem + "DetailsView";
string moduleName = "Module" + nameTypeItem;
string fullViewName = moduleName + ".Views." + viewName + ", " + moduleName;
var typeOfCurrentView = Type.GetType(fullViewName);
//var view = Activator.CreateInstance(typeOfCurrentView);
var view = tabConfigurationRegion.GetView(viewName);
if (view == null)
{
view = _container.Resolve(typeOfCurrentView);
// Add the view to the main region. This automatically activates the view too.
tabConfigurationRegion.Add(view, viewName);
}
// The view has already been added to the region so just activate it.
tabConfigurationRegion.Activate(view);
string viewModelName = "ModuleItem" + nameTypeItem + "DetailsViewModel";
string fullViewModelName = moduleName + ".ViewModels." + viewModelName + ", " + moduleName;
var typeOfCurrentViewModel = Type.GetType(fullViewModelName);
//equivalent to ModuleItemSiemensDetailsViewModel viewModelM = view.DataContext as ModuleItemSiemensDetailsViewModel;
var propertyInfoDataContext = view.GetType().GetProperty("DataContext");
var viewModelModuleItem = propertyInfoDataContext.GetValue(view, null);
if (viewModelModuleItem != null)
{
PropertyInfo prop = typeOfCurrentViewModel.GetProperty("CurrentModuleItem");
//equivalent to viewModelModuleItem.CurrentModuleItem = selectedItem as ModuleItemBaseViewModel;
prop.SetValue(viewModelModuleItem, selectedItem, null);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
Debug.WriteLine(ex.StackTrace);
}
}
To reproduce the problem : Start a new WPF project, Add one main project in your solution with a view with a treeview and a treeviewviewmodel with the list of ParentViewModel which will be display on your treeview. Define an other project with 2 viewmodels : one class ParentVM with an ObservableCollection and a childViewModel, let one or two property like name, and bind it to the treeview. In the second project, define a detailsView to see properties of your ChildViewModel. On your main project, in the treeviewviewmodel add a SelectedCommandFunction which use prism to activate your detailView. (You need to have a DetailsView when you click on the Parent and an other one when you click on the ChildView.
Every TreeViewItem is in the visual tree of its parent, and the event will bubble down, so if you clicked the tree leaf, and it has two parents, then the event will be called 3 times.
Ways to prevent this:
Bind the IsSelected of the TreeItem directly to IsSelected of your VM, and do your thing OnPropertyChanged of IsSelected property of your VM.
In a roundabout way: in codebehind, when you handle the event, check if the OriginalSource is the same as the sender (similar to here)
Implement your own EventTrigger that marks events as Handled

Search a collection of TabItems for a TabItem Name

I want to input the name of a TabItem to a Window containing a TabControl that contains a collection of TabItems, programatically search the collection and open the TabItem with the name that matches that input. dkozl answered a similar question Aug 16 '13 but I don't understand it (I'm new to this).
I've kicked this around for a couple of days and have come up with the following (which doesn't work)
foreach (IEnumerable<TabItem> item in tabControlList)
{
if (item.Name == "AddRskAreas")
{
item.IsSelected = true;
}
else
{
MessageBox.Show("Tab not found");
}
}
I struggle to understand how to implement IEnumerable. Can anyone help me with this please?
You can update multiple values in a list using LINQ:
tabControlList.Where(item => item.Name == "AddRskAreas").ToList().ForEach(item => item.IsSelected = true);
You need to actually compare the "Header" instead of the name.
foreach (IEnumerable<TabItem> item in tabControlList)
{
if (item.Header== "AddRskAreas")
{
item.IsSelected = true;
}
else
{
MessageBox.Show("Tab not found");
}
}
I derived a solution with a little help from Sean Sextons 2,000 Things You Should Know About C# knowledge base. I'll post the key XAML and C# fragments of my solution below in case anyone else can piece them together get value from it.
Interestingly the buttons are now faded out - anyone know why?
//The XAML fragments ....
<!-- These RoutedUICommands (in <Window.Resources>) are bound to the Process Procedure Selection Buttons. Clicking on the button
opens the corresponding process procedure TabItem -->
<RoutedUICommand x:Key="OpenPrcdrTbItm" Text="This Command opens the Process Procedure TabItem"/>
<Window.CommandBindings>
<!-- These CommandBindings (in <Window.CommandBindings>) bind the Process Procedure Selection Button Commands to the Command Handler in the code behind -->
<CommandBinding Command = "{StaticResource OpenPrcdrTbItm}" Executed="RskPrcssPrcdrs_Click"/>
<Button x:Name="ChngeRskAreas" Grid.Column="1" Content="Change Risk Areas"
Command ="{StaticResource OpenPrcdrTbItm}" CommandParameter="ChngeRskAreas"/>
//The C# code behind fragments
//Select the chosen TabItem
public void RskPrcssPrcdrs_Click(object sender, ExecutedRoutedEventArgs e)
{
RskManWndw rskManWndw = new RskManWndw(this); //Instantiate a new rskManWndw window
TabControl tabControlCollection = new TabControl();
TabItem tabItemCollection = new TabItem();
string slctdTabItem = (string)e.Parameter;
bool slctdTabItemFnd = false;
string msgBoxMsg = "";
//Open the rskAraManWndw window
rskManWndw.Show();
foreach (TabItem tabItem in rskManWndw.RskManPrcssTbCtl.Items)
{
if (tabItem.Name == slctdTabItem)
{
tabItem.IsSelected = true; //Select the chosen TabItem.
slctdTabItemFnd = true; //Flag that the TabItem was found.
break;
}
}
if (slctdTabItemFnd == false) //Was the TabItem found?
{
msgBoxMsg = "A TabItem matching the" + slctdTabItem + "Command Parameter was not found. "
+ "Please inform the system administrator.";
MessageBox.Show($" {msgBoxMsg}", "RMS Processing Error Alert");
rskManWndw.Close();
}
else
{
Hide(); //Hide the Risk_Management_System.MainWindow
}
}

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

ContentPresenter not updating display correctly

In my program's main window I have a TreeView and a ContentPresenter. The display of the ContentPresenter is determined by what node is selected in the TreeView.
The name of one of my nodes is allowed to be changed by the user via contentMenu. All the user has to do is right click the node and select the new name out of the choices. The ContentPresenter is supposed to have a null display until the user chooses a name for the node.
The problem occurs when a new name is selected from the contentMenu. The ContentPresenter's display changes, like it should, but only after the user selects a different node (changing the display), and then re-selects the original node.
How do I make it so that the display on the ContentPresenter changes right when the TreeView node's name is changed?
TreeViewViewModel:
public class TreeViewViewModel : PropertyChangedBase
{
public TreeViewViewModel()
{
Node = new Node() { NodeName = "Blank", NodeDataModel = new NodeModel(),
Commands = { new Command(nodeType_name1), new Command(nodeType_name2) } };
}
//These functions call to the NodeName property in the TreeView's Data Model
private void nodeType_name1()
{
Node.NodeName = "Name1";
}
private void nodeType_name2()
{
Node.NodeName = "Name2";
}
}
XAML for MainWindow:
<!-- Tree view items & Functions -->
<TreeView Name="Tree_One" ItemsSource="{Binding DataTree.Data}" ... >
<TreeView.Resources>
<SolidColorBrush Color="LightSkyBlue" x:Key="{x:Static SystemColors.HighlightBrushKey}" />
</TreeView.Resources>
</TreeView>
<!--- Left Widget -->
<ContentPresenter Content="{Binding LeftWidget}" />
MainWindowViewModel:
public class MainWindowViewModel : PropertyChangedBase
{
private TreeViewViewModel _dataTree;
public MainWindowViewModel()
{
_dataTree = new TreeViewViewModel();
}
public TreeViewViewModel DataTree { ... }
//This function is in charge of changing the display of the ContentPresenter
// I think that my problem can probably be solved by doing something here
public void ChangeViews()
{
if (_dataTree.SelectedItem is Node)
{
var _node = _dataTree.SelectedItem as Node;
var nodeViewModel = new NodeViewModel(_node.NodeDataModel);
if (_node.NodeName== "Unknown")
LeftWidget = null; //This is the Content Presenter **
if (_node.NodeName == "Name1")
{
LeftWidget = nodeViewModel;
}
if (_node.NodeName == "Name2") {...}
}
}
}
Duh, thats a alot of code and its pretty difficult to understand what you up to since you seem to have controls in your ViewModel.
Or at least it looks to me that you have them in ViewModel. That is not very MVVM-alike my friend. :)
"The problem occurs when a new name is selected from the contentMenu. The ContentPresenter's display changes, like it should, but only after the user selects a different node (changing the display), and then re-selects the original node."
The property changed is not being fired because the new selected value is equal to the old one.
Pretty obvious, right?... no property was actually changed
But why do you want the ContentPresenter to update itself with the value that it already has?
You said when you select a node the ContentPresenter displays it properly and when you re-select the same the ContentPresenter is not doing anything.
Its not doing anything because it think it doesnt need to. Which is true.
So the question is why would you make ContentPresenter force to refresh on each value no matter if old value is the same as new one?
Though if you want to hack/trick a little bit, you can always set ContentPresenter's Content to null before you assign another value. :)
However, post us more code and we will be able to provide you a better solution to your issue.
I was able to fix this issue by calling ChangeViews(); in my MainWindowViewModel from my TreeViewViewModel. I did this by using a delegate property in the TVVM, and adding it to my MWVM. By doing this, the display is updated whenever ChangeViews(); is called.
This is the answer that I used.

Bind Header of WPF ContextMenu

I had a problem with TreeView-Binding and ContextMenu here: Selected TreeViewItem is null
Now I'm having this problem: I have the ContextMenu
<TreeView.ContextMenu>
<ContextMenu x:Name="MyContext" ItemsSource="{Binding OCContext}" DisplayMemberPath="Text"/>
</TreeView.ContextMenu>
(The image shows how my ContextMenu looks like, don't mind about the tabItem...).
As you can see, it's just the ContetMenu, no MenuItem! If the user clicks on Close, I want to do something in my ViewModel (raise a Command?). I'd also like to know which button/Menu he clicked. The amount of Menus is dynamically, since it's ItemsSource is being binded.
This is my ViewModel:
private ObservableCollection<T_Antwort> _occontext;
public ObservableCollection<T_Antwort> OCContext
{
get
{
if (_occontext == null)
_occontext = new ObservableCollection<T_Antwort>();
return _occontext;
}
set
{
_occontext = value;
RaisePropertyChanged(() => OCContext);
}
}
So all I want to do is to bind the ContextMenu (The "items" Close and CloseOtherThankThis) to my ViewModel, so when the user clicks on one of them, I want to access them in my ViewModel. This means I don't want to bind them one by one, I want somehow to get an event (ContextMenuItemClicked (?)) being called and use this in my ViewModel.
Btw. using MenuItem under ContextMenu will create another "Menu folder", so it would be
" " -> Close
" " -> CloseOtherThankThis
And I don't want it to look like this.
Edit: I'm currently getting the item like this:
private void MyContext_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
System.Windows.Controls.Primitives.MenuBase s = sender as System.Windows.Controls.Primitives.MenuBase;
ItemCollection ic = s.Items;
T_Antwort SelectedItem = (T_Antwort)ic.CurrentItem;
}
Is there any possibility to get the selected item with binding?
Don't know if you have tried it, but there's a PlacementTarget for context menu, which gives you the object that contains the context menu.
In one project I had, I made something like this:
<MenuItem ... Visibility="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ContextMenu}},Path=PlacementTarget.SelectedItem

Categories

Resources