MenuItem with dynamic MenuItems - c#

I have this MenuBar in my application:
<Menu Grid.Row="0" Height="22" Name="menu1" HorizontalAlignment="Stretch" VerticalAlignment="Top" >
<MenuItem Header="File" />
<MenuItem Header="Youtube">
</MenuItem>
<MenuItem Header="Help" />
</Menu>
And i want to add items to the Youtube MenuItem dynamic ,something like this:
MenuItem menu = (MenuItem)sender;
ItemCollection items = menu.Items;
items.Clear();
if (YouTubeAuth.CreateInstance().IsLogin())
{
MenuItem refreshItem = new MenuItem();
refreshItem.Header = "Refresh";
refreshItem.Click += DidPressRefresh;
items.Add(refreshItem);
MenuItem logouttItem = new MenuItem();
logouttItem.Header = "Signout";
logouttItem.Click += DidPressLogout;
items.Add(logouttItem);
}
else
{
MenuItem loginItem = new MenuItem();
loginItem.Header = "Login";
loginItem.Click += DidPressLogin;
items.Add(loginItem);
}
It say if you login show logout and refresh otherwise shot login.
I try to add this method to Click="DidPressDeleteAllFavorites" of the Youtube MenuItem but it won't work.
Any idea how to fix it? what i do wrong?

if you are using the MVVM-pattern
<MenuItem Header="Youtube" ItemsSource="{Binding yourProperty}"/>
if you are working with code-behind
XAML
<MenuItem Header="Youtube" Name="myYoutube"/>
Code-behind
myYoutube.ItemsSource=yourMenuItems;
EDIT
the problem in your code is in my opinion you just need to call your Event Code on on startup because your Youtube has no subMenuitem on begin or you could also call UpdateLayout() in your event this could also maybe fix it
Working Example
Codebehind
public partial class MainWindow : Window
{
bool test = false;
public MainWindow()
{
InitializeComponent();
MenuItem_Click(myYouTube, null);
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
var mymenuitem = sender as MenuItem;
MenuItem menu = (MenuItem)sender;
ItemCollection items = menu.Items;
items.Clear();
if (test)
{
MenuItem refreshItem = new MenuItem();
refreshItem.Header = "Refresh";
//refreshItem.Click += DidPressRefresh;
items.Add(refreshItem);
MenuItem logouttItem = new MenuItem();
logouttItem.Header = "Signout";
//logouttItem.Click += DidPressLogout;
items.Add(logouttItem);
test = false;
}
else
{
MenuItem loginItem = new MenuItem();
loginItem.Header = "Login";
//loginItem.Click += DidPressLogin;
items.Add(loginItem);
test = true;
}
}
}
XAML
<Menu Height="23" HorizontalAlignment="Left" Margin="84,66,0,0" Name="menu1" VerticalAlignment="Top" Width="200">
<MenuItem Header="File" />
<MenuItem Header="Youtube" Name="myYouTube" Click="MenuItem_Click">
</MenuItem>
<MenuItem Header="Help" />
</Menu>

Related

How to refresh a WPF Treeview content after applying Sort on Header?

I have a treeview showing folder structure (folders and files under).
I want to add a button to allow sorting the content (files) by Alphabetic order.
Here is my implementation:
XAML
<TreeView Name="WorkspaceTree" Grid.Row="1" FontSize="13" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="200" IsTextSearchEnabled="True" SelectedItemChanged="ProjectWorkspace_SelectedItemChanged" MouseRightButtonDown="ProjectWorkspace_MouseRightButtonDown" MouseDoubleClick="ProjectWorkspace_MouseDoubleClick">
<TreeView.Resources>
<ContextMenu x:Key ="FolderContext" StaysOpen="true">
<MenuItem x:Name="ProjectWorkspace_OpenAll" Header="Open All" Click="ProjectWorkspace_OpenAll_Click"/>
</ContextMenu>
<ContextMenu x:Key="ReaderContext" StaysOpen="true">
<MenuItem x:Name="ProjectWorkspace_Open" Header="Open" Click="ProjectWorkspace_Open_Click"/>
<MenuItem x:Name="ProjectWorkspace_Build" Header="Build" Click="ProjectWorkspace_Build_Click"/>
</ContextMenu>
</TreeView.Resources>
No workspace was loaded...
</TreeView>
The tree is filled in code behind. Here is a snapshot.
...
WorkspaceTree.Items.Clear();
var rootDirectoryInfo = new DirectoryInfo(dlg.FileName);
TreeViewItem root_item = CreateDirectoryNode(rootDirectoryInfo);
if (root_item != null)
{
WorkspaceTree.Items.Add(root_item);
...
}
private static TreeViewItem CreateDirectoryNode(DirectoryInfo directoryInfo)
{
var directoryNode = new TreeViewItem { Header = directoryInfo.Name, Tag = "Folder" };
foreach (var directory in directoryInfo.GetDirectories())
{
TreeViewItem dir_recursive_item = CreateDirectoryNode(directory);
if (dir_recursive_item != null)
directoryNode.Items.Add(dir_recursive_item);
}
foreach (var file in directoryInfo.GetFiles("*.txt"))
directoryNode.Items.Add(new TreeViewItem { Header = file.Name, Tag = file.FullName });
if (directoryNode.Items.Count == 0)
return null;
return directoryNode;
}
}
Now after the tree is shown, by a UI interaction, its content should be sorted.
I've place a button which its click event is:
private void Sort_Click(object sender, RoutedEventArgs e)
{
WorkspaceTree.Items.SortDescriptions.Clear();
WorkspaceTree.Items.SortDescriptions.Add(new SortDescription("Header", ListSortDirection.Ascending));
}
While the event is running - nothing happens on the Tree itself.
I event tried sorting over Header.Name in the SortDescription, with no change in behavior.
What am i missing?

wpf textblock focus on mouse right click

hello I am pretty new to wpf c#, I have a treeview which is populated at run time and here is my xaml code
<StackPanel Orientation="Horizontal">
<Image Source="Properties\accessories-text-editor-6.ico" Margin="0,0,5,0" />
<TextBlock Text="{Binding Name}" Foreground="Green" MouseLeftButtonDown="TextBlock_MouseLeftButtonDown" MouseRightButtonDown="TextBlock_MouseRightButtonDown" >
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="HeadLine" ></MenuItem>
<MenuItem Header="Textblock" ></MenuItem>
<MenuItem Header="Author" ></MenuItem>
<MenuItem Header="PageNumber" ></MenuItem>
<MenuItem Header="RunningTitle" ></MenuItem>
<MenuItem Header="Illustration" ></MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</StackPanel>
what I want is when I right click the textblock that is inside the treeview. the textblock need to be focus. As of now what it does is show the context menu item.
so how do I get the index of the Right clicked textblock? so I can focus to that item.
Thank you
A TextBlock cannot be focused...but you can get a reference to it in the MouseRightButtonDown event handler by casting the sender argument:
private void TextBlock_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
TextBlock txt = sender as TextBlock;
//do whatever you want with the TextBlock...
}
If you are in the context of a TreeView you may want to select the parent TreeViewItem:
private void TextBlock_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
TextBlock txt = sender as TextBlock;
TreeViewItem tvi = FindParent<TreeViewItem>(txt);
if (tvi != null)
tvi.IsSelected = true;
}
private static T FindParent<T>(DependencyObject dependencyObject) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null) return null;
var parentT = parent as T;
return parentT ?? FindParent<T>(parent);
}

Cannot set InputGestureText from code behind

I am not able to set the InputGestureText from code behind.
The shortcut key works fine but the key-string is not displayed with the menu-item.
The XAML and C# code is mentioned below.
Environment:
VS2012 & .NET Framework 4.5
Any idea what I am missing?
//********************
//XAML
//********************
<DockPanel Grid.Row="0" Grid.ColumnSpan="2">
<Menu DockPanel.Dock="Top" Background="White">
<MenuItem x:Name="mnuFile" Header="_File">
<MenuItem x:Name="mnuFileNew" Header="_New..." />
<MenuItem x:Name="mnuFileOpen" Header="_Open for Editing..." />
<Separator />
<MenuItem x:Name="mnuFileExit" Header="E_xit" />
</MenuItem>
</Menu>
</DockPanel>
//********************
//********************
//Code Behind
//********************
public MainWindow()
{
InitializeComponent();
//File > New
mnuFileNew.Click += mnuFileNew_Click;
RoutedCommand cmdNewReport = new RoutedCommand();
cmdNewReport.InputGestures.Add(new KeyGesture(Key.N, ModifierKeys.Control | ModifierKeys.Shift, "Ctrl+Shift+N"));
CommandBindings.Add(new CommandBinding(cmdNewReport, mnuFileNew_Click));
}
void mnuFileNew_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("New Report not implemented!", this.Title, MessageBoxButton.OK, MessageBoxImage.Information);
}
//********************
If you want to use code behind, then try using this to display the shortcut:
mnuFileNew.Click += mnuFileNew_Click;
RoutedCommand cmdNewReport = new RoutedCommand();
var keyGesture = new KeyGesture(Key.N, ModifierKeys.Control | ModifierKeys.Shift, "Ctrl+Shift+N");
cmdNewReport.InputGestures.Add(keyGesture);
mnuFileNew.InputGestureText = keyGesture.DisplayString;
CommandBindings.Add(new CommandBinding(cmdNewReport, mnuFileNew_Click));

How to copy selected listview item by copy command in the context menu

I've a listview and I want to copy the selected item by selecting the copy command in the context menu. I'm able to create a context menu with the copy command as shown in the image below:
The problem is when I select a row item and right click it, the context menu wouldn't show and I cannot select the copy command to copy the selected item. The context menu will show when I right click at the empty space in the list view.
Here's the code:
.xaml
<ListView x: Name = "DebugLogLb" BorderBrush = "{x:Null}" SelectionMode = "Extended" MouseRightButtonDown = "DebugLogLb_MouseRightButtonDown">
<ListViewItem x: Name = "DebugLogItem">
<ListViewItem.ContextMenu>
<ContextMenu x: Name = "CommandMenu">
<MenuItem Header = "Clear log" Click = "CommandMenuItem_Click"/>
<MenuItem Header = "Copy" Command = "Copy" CommandTarget = "{Binding ElementName=DebugLogItem}"/>
</ContextMenu>
</ListViewItem.ContextMenu>
</ListViewItem>
</ListView >
.xaml.cs:
private void DebugLogLb_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
ContextMenu contextMenu = this.FindName("CommandMenu") as ContextMenu;
contextMenu.PlacementTarget = sender as ListViewItem;
contextMenu.IsOpen = true;
}
private void CommandMenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem menuItem = (MenuItem) e.OriginalSource;
switch (menuItem.Header.ToString())
{
case "Clear log":
DebugLogLb.Items.Clear();
break;
default:
break;
}
}
Something like this will let you use the ContextMenu with its MenuItems :
<ListView x:Name = "DebugLogLb" BorderBrush = "{x:Null}" SelectionMode = "Extended" ItemsSource="{Binding items}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.ContextMenu >
<ContextMenu x:Name = "CommandMenu">
<MenuItem Header = "Clear log" />
<MenuItem Header = "Copy" Command = "Copy" CommandTarget = "{Binding ElementName=DebugLogItem}"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView >
So, define it inside the ItemTemplate in order to be able to use it when selecting each item.
The items collection is defined in code behind :
public ObservableCollection<string> items { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
items = new ObservableCollection<string>();
items.Add("first item");
items.Add("second item");
}
Nothing fancy, just for the sake of example.
One more thing, if you still want it available when clicking on the white space, add a ContextMenu on the ListView too:
<ListView.ContextMenu>
<ContextMenu x:Name = "CommandMenu">
<MenuItem Header = "Clear log" />
<MenuItem Header = "Copy" Command = "Copy" CommandTarget = "{Binding ElementName=DebugLogItem}"/>
</ContextMenu>
</ListView.ContextMenu>

Custom context menu for WPF WebBrowser Control

Hi I need to create a custom context menu for a web browser control in wpf. Here is my xaml code which is not working:
<WebBrowser x:Name="EmailBox" ap:BrowserBehavior.HtmlString="{Binding Message, Mode=OneWay}">
<WebBrowser.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy" Command="ApplicationCommands.Copy"/>
<MenuItem Header="Copy to Customer Reference ID"
Command="{Binding CopyID}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}},
Path=PlacementTarget.Selection.Text}">
<MenuItem.Icon>
<Image Source="{StaticResource CopyImageSource}" Width="16" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Copy to Comments"
Command="{Binding CopyToCommentsCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}},
Path=PlacementTarget.Selection.Text}">
<MenuItem.Icon>
<Image Source="{StaticResource NoteCopyI}" Width="16" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</WebBrowser.ContextMenu>
</WebBrowser>
I copied the context menu code from somewhere else. This works in other controls but not for the webbrowser control. Is it possible to make this work?
Hi You have to add reference to Microsoft HTML Object Library and than...
XAML
<Window x:Class="WPFCustomContextMenuInWebBrowser.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFCustomContextMenuInWebBrowser"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ContextMenu x:Key="MnuCustom" StaysOpen="True">
<MenuItem Header="Custom 1"></MenuItem>
<MenuItem Header="Custom 1"></MenuItem>
<MenuItem Header="Custom 3"></MenuItem>
</ContextMenu>
</Window.Resources>
<Grid>
<WebBrowser x:Name="Wb"></WebBrowser>
</Grid>
</Window>
C#
using System.Windows.Controls;
using MSHTML;
namespace WPFCustomContextMenuInWebBrowser {
public partial class MainWindow {
private HTMLDocumentEvents2_Event _docEvent;
public MainWindow() {
InitializeComponent();
Wb.Navigate("http://google.com");
Wb.LoadCompleted += delegate {
if (_docEvent != null) {
_docEvent.oncontextmenu -= _docEvent_oncontextmenu;
}
if (Wb.Document != null) {
_docEvent = (HTMLDocumentEvents2_Event)Wb.Document;
_docEvent.oncontextmenu += _docEvent_oncontextmenu;
}
};
}
bool _docEvent_oncontextmenu(IHTMLEventObj pEvtObj) {
WbShowContextMenu();
return false;
}
public void WbShowContextMenu() {
ContextMenu cm = FindResource("MnuCustom") as ContextMenu;
if (cm == null) return;
cm.PlacementTarget = Wb;
cm.IsOpen = true;
}
}
}
No, It's not possible to make this work.
WebBrowser control is a very thin wrapper around native WebBrowser ActiveX component, which is a part of Internet Explorer subsystem. It is hosted in it's own window host (WPF window and WebBrowser have different HWNDs), so WPF knows only about focus entering and leaving WebBrowser, but has no knowledge of any keyboard/mouse events. Also, there is so called 'airspace problem': WPF-rendered and native regions of screen area cannot overlap.
Therefore you cannot use WPF ContextMenu with WebBrowser, because:
WPF doesn't recieve Mouse Right Click event to open Context Menu
WPF cannot draw Context Menu above WebBrowser
Also, I don't think there is easy way to emulate ContextMenu with html/js in browser's content - as I recall, ActiveX component uses IE5 (quirk) rendering mode, and it is not possible to change that without changing registry files.
You can try to use ActiveX API with WebBrowser.Document object to disable native context menu and draw another one yourself through WinAPI, which is not an easy task.
So, I would recommend to look for other, pure-WPF browser controls or HTML renderers, such as awesomium
The XAML as follows:
<!--WebBrowser to Display Chat Messages-->
<WebBrowser Name="webBrowser"
Source="http://stakoverflow.com"
Navigated="webBrowser_Navigated"
Navigating="webBrowser_Navigating"
LoadCompleted="webBrowser_LoadCompleted">
<WebBrowser.ContextMenu>
<ContextMenu x:Name="wbContextMenu" >
<MenuItem x:Name="menuItem1" Header="Test Item" Click="menuItem1_Click" />
</ContextMenu>
</WebBrowser.ContextMenu
</WebBrowser>
Code as follows:
using mshtml;
private mshtml.HTMLDocumentEvents2_Event documentEvents;
in constructor or xaml set your LoadComplete event:
webBrowser.LoadCompleted += webBrowser_LoadCompleted;
then in that method create your new webbrowser document object and view the available properties and create new events as follows:
private void webBrowser_LoadCompleted(object sender, NavigationEventArgs e)
{
documentEvents = (HTMLDocumentEvents2_Event)webBrowserChat.Document; // this will access the events properties as needed
documentEvents.oncontextmenu += webBrowserChat_ContextMenuOpening;
}
private bool webBrowserChat_ContextMenuOpening(IHTMLEventObj pEvtObj)
{
wbContextMenu.PlacementTarget = pEvtObj as ContextMenu; // Creates target spot where contextmenu will appear
wbContextMenu.IsOpen = true; // Opens contextmneu
return false; // ContextMenu wont open
// return true; ContextMenu will open
// Here you can create your custom contextmenu or whatever you want
}
I've taken the solution provided by #MartinHoly and i've encountered the problem: to context menu can be popped up only once (if you right-click on the scrollbar, for example, or select a custom menu item - next time you right-click the WebBrowser brings the standard IE menu). I have made the following workaround:
The xaml:
<Window x:Class="WPFCustomContextMenuInWebBrowser.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFCustomContextMenuInWebBrowser"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ContextMenu x:Key="MnuCustom" StaysOpen="True">
<MenuItem Header="Custom 1"></MenuItem>
<MenuItem Header="Custom 2"></MenuItem>
<MenuItem Header="Custom 3"></MenuItem>
</ContextMenu>
</Window.Resources>
<Grid>
<WebBrowser x:Name="Browser"></WebBrowser>
</Grid>
The code behind:
using System.Windows.Controls;
using MSHTML;
namespace WPFCustomContextMenuInWebBrowser {
public partial class MainWindow {
public MainWindow()
{
InitializeComponent();
Browser.LoadCompleted += BrowserOnLoadCompleted;
}
void BrowserOnLoadCompleted(object sender, NavigationEventArgs navigationEventArgs)
{
var mshtmlDoc = Browser.Document as HTMLDocument;
if (mshtmlDoc == null) return;
var doc2event = mshtmlDoc as HTMLDocumentEvents2_Event;
if (doc2event != null)
{
doc2event.onfocusin += FocusInContextMenu;
}
}
bool OpenContextMenu(IHTMLEventObj pEvtObj)
{
WbShowContextMenu(pEvtObj as ContextMenu);
return false;
}
void FocusInContextMenu(IHTMLEventObj pevtobj)
{
var mshtmlDoc = Browser.Document as HTMLDocument;
var doc2event = mshtmlDoc as HTMLDocumentEvents2_Event;
if (doc2event != null)
{
doc2event.oncontextmenu -= OpenContextMenu;
doc2event.onfocusin -= FocusInContextMenu;
doc2event.oncontextmenu += OpenContextMenu;
doc2event.onfocusin += FocusInContextMenu;
}
}
public void WbShowContextMenu()
{
ContextMenu cm = FindResource("MnuCustom") as ContextMenu;
if (cm == null) return;
cm.PlacementTarget = Browser;
cm.IsOpen = true;
}
}
}
I have a indirect implementation, which involves calling each other in C# and javascript.
xaml:
<WebBrowser x:Name="webbrowser">
<WebBrowser.ContextMenu>
<ContextMenu>
<MenuItem Header="item1"/>
</ContextMenu>
</WebBrowser.ContextMenu>
</WebBrowser>
c#:
using System.Runtime.InteropServices;
public MainWindow()
{
InitializeComponent();
//webbrowser.Navigate(new Uri("https://www.google.com"));
webbrowser.ObjectForScripting = new ScriptManager(this);
webbrowser.LoadCompleted += Webbrowser_LoadCompleted;
}
private void Webbrowser_LoadCompleted(object sender, NavigationEventArgs e)
{
//call C# method and disable the default contextmenu here.
webbrowser.InvokeScript("eval", new object[] { "document.oncontextmenu = function() { window.external.ShowContextMenu(); return false; };" });
}
[ComVisible(true)]
public class ScriptManager
{
private MainWindow mainWindow;
public ScriptManager(MainWindow MainWindow)
{
mainWindow = MainWindow;
}
public void ShowContextMenu()
{
mainWindow.webbrowser.ContextMenu.IsOpen = true;
}
}
But it still has a native contextmenu when page is loading because LoadCompleted event hasn't triggered.
So if the html page is writed by self, you can add this line directly to the script section in html and don't need the LoadCompleted event:
document.oncontextmenu = function() { window.external.ShowContextMenu(); return false; };

Categories

Resources