wpf textblock focus on mouse right click - c#

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);
}

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?

Modifying image height from context menu in XAML

In my XAML page I define an image with a context menu:
<Image Height="{Binding Image.Height, Mode=TwoWay}" MaxHeight="2000" HorizontalAlignment="Left" StretchDirection="Both" Stretch="Uniform"
Source="{Binding Image.ImageData, Converter={StaticResource ImageByteConverter}}"
x:Name="Image1">
<Image.ContextMenu>
<ContextMenu>
<MenuItem Header="200" Click="ImageHeight200_Click" />
<MenuItem Header="400" Click="ImageHeight400_Click" />
<MenuItem Header="600" Click="ImageHeight600_Click" />
<MenuItem Header="800" Click="ImageHeight800_Click" />
<MenuItem Header="1000" Click="ImageHeight1000_Click" />
</ContextMenu>
</Image.ContextMenu>
</Image>
Now I want to add code to resize the image.
When I write something like this
private void ImageHeight200_Click(object sender, RoutedEventArgs e)
{
var img = (Image)e.Source;
img.Height = 200;
}
It accesses the MenuItem but not the image and I get an error message:
The object of type "System.Windows.Controls.MenuItem" cannot be converted to type "System.Windows.Controls.Image".
My Question is:
How can I access the image object?
You can just reference the image by name
private void ImageHeight200_Click(object sender, RoutedEventArgs e)
{
Image1.Height = 200;
}
You could use PlacementTarget property of ContextMenu
private void ImageHeight200_Click(object sender, System.Windows.RoutedEventArgs e){
MenuItem mnu = sender as MenuItem;
Image sp = null;
if(mnu!=null)
{
sp = ((ContextMenu)mnu.Parent).PlacementTarget as Image;
}}

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>

MenuItem with dynamic MenuItems

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>

ContextMenu disappears immediately after appearing

I have a context menu but it is disappearing instantly after it shows up.
<TextBlock Name="InputtedAddress" Text="{Binding Path=InputtedAddress}" MouseDown="InputtedAddress_MouseDown"/>
System.Windows.Controls.ContextMenu thisMenu;
private void InputtedAddress_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.RightButton == MouseButtonState.Pressed)
{
thisMenu = new System.Windows.Controls.ContextMenu();
MenuItem thisMenuItem = new MenuItem() { Header = "Zoom to Incident" };
thisMenuItem.Click += new RoutedEventHandler(thisMenuItem_Click);
thisMenu.Items.Add(thisMenuItem);
thisMenu.IsOpen = true;
}
}
It's likely because you're not marking the MouseDown event to handled. Set e.Handled to true and it will no longer propagate and your ContextMenu will stay open.
That said, this is an awful way to assign a ContextMenu in the first place. Why not just do this:
<TextBlock ...>
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="Zoom to Incident" Click="thisMenuItem_Click"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
You should assign your menu to the ContextMenu property of your TextBlock so that the opening and positioning will be taken care of for you. You also don't need to create the menu in each MouseDown; just create it once and assign it to the ContextMenu property.
In XAML:
<TextBlock
Name="InputtedAddress"
Text="{Binding Path=InputtedAddress}"
>
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem
Header="Zoom to Incident"
Click="ContextMenu_Click"
/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
If you do want to show it manually you will need to position it before showing it by setting the PlacementTarget property, something like this:
private void InputtedAddress_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.RightButton == MouseButtonState.Pressed)
{
thisMenuPlacementTarget = InputtedAddress;
thisMenu.IsOpen = true;
}
}
P.S. "Inputted" is not a word :)

Categories

Resources