I've a simple TreeView in my WPF application. The content is build up programmatically adding several TreeViewItems (and sub items).
Now I want to integrate links in each TreeViewItem like
"Text of Item 1 (http://google.de)"
The links should be clickable.
How can I achieve this in the code and how can I assign a handler to perform the "Hyperlink Action" ( e.g. Process.Start(linkStr) )?
XAML file:
<Window x:Class="SOTree.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TreeView Grid.Row="0" Grid.Column="1" Margin="30">
My Treeview Title
<TreeViewItem IsExpanded="True">
<TextBlock IsEnabled="True">Wikipedia
<Hyperlink NavigateUri="http://www.wikipedia.org" RequestNavigate="Hyperlink_RequestNavigate">
Wikipedia
</Hyperlink>
</TextBlock>
</TreeViewItem>
</TreeView>
</Grid>
Code behind:
using System.Windows;
using System.Windows.Navigation;
namespace SOTree
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
System.Diagnostics.Process.Start(e.Uri.ToString());
}
}
}
Hope this helps.
XAML for Tree view
<TreeView Name="trvMenu" Margin="367,29,0.2,154.6">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type l:MenuItem}" ItemsSource="{Binding Items}">
<TextBlock>
<Hyperlink NavigateUri="{Binding Title}"
RequestNavigate="Hyperlink_RequestNavigate">
<InlineUIContainer>
<TextBlock Text="{Binding Title}" />
</InlineUIContainer>
</Hyperlink></TextBlock>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
Code Behind with binding
public class MenuItem
{
public MenuItem()
{
this.Items = new ObservableCollection<MenuItem>();
}
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
public string Title { get; set; }
public ObservableCollection<MenuItem> Items { get; set; }
}
public partial class MainWindow : Window
{
private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
{
Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri));
e.Handled = true;
}
public MainWindow()
{
InitializeComponent();
MenuItem root = new MenuItem() { Title = "Menu" };
MenuItem childItem1 = new MenuItem() { Title = "http://www.google.com" };
childItem1.Items.Add(new MenuItem() { Title = "http://www.google.com" });
childItem1.Items.Add(new MenuItem() { Title = "http://www.google.com" });
root.Items.Add(childItem1);
root.Items.Add(new MenuItem() { Title = "http://www.google.com" });
trvMenu.Items.Add(root);
}
}
Related
A usercontrol should get added every time when the shortcut is clicked. But when i close the usercontrol and try to open it again the shortcut doesn't work. The focus is somewhere else.when some button is clicked on the window and shortcut is clicked usercontrol is added.
UserControl 1:
<Window
x:class="Class1"
...>
<Window.InputBindings>
<KeyBinding Command="ButtonCLickCommand "
Modifiers="Control" Key="Q"/>
</Window.InputBindings>
<Grid x:Name="Grid">
<Button Content = "OpenUserControl" Command="ButtonCLickCommand "/>
<Button Content = "Temp"/>
</Grid>
</Window>
CodeBehind:
public ICommand ProfileCommand { get; set; }
ProfileCommand = new RelayCommand(Button_Click());
public void Button_Click()
{
_control = new Class2();
_control.CloseAction = CloseClass2;
Panel.SetZIndex(_control, 10);
_control.Width = 200;
_control.Height = 200;
Grid.Children.Add(_control);
}
public void CloseClass2(Class2 control)
{
Grid.Children.Remove(control);
}
UserControl 2:
<UserControl
x:class="Class2"
...>
<StackPanel>
<Button Content="x" Click="Button_Click" Height="30">
<Rectangle Fill="Black" Height="70"/>
</StackPanel>
</UserControl>
Code Behind:
namespace namespace1.UserControls
{
/// <summary>
/// Interaction logic for ProfileUserControl.xaml
/// </summary>
public partial class Class2 : UserControl
{
public Action<Class2> CloseAction;
public Class2()
{
InitializeComponent();
}
private void Button_Click(object sender, System.Windows.RoutedEventArgs e)
{
CloseAction(this);
}
}
}
This is outline of My code. Help me i'm new to WPF.
Thanks for every answer i get. :)
In Your KeyBinding ... you should bind to your command in View model
....
to make it easier .. it omit the view model ... here is the solution,note that i made some tweaks for simplicity:
in mainWindow.xaml :
<Window.InputBindings>
<KeyBinding Command="{Binding ButtonClickCommand,RelativeSource{RelativeSource FindAncestor,AncestorType=Window}}" Modifiers="Control" Key="Q"/>
</Window.InputBindings>
<StackPanel x:Name="MainGrid">
<Button Content = "OpenUserControl" Command="{Binding ButtonClickCommand,RelativeSource={RelativeSource FindAncestor,AncestorType=Window}}"/>
</StackPanel>
and here is the code behind:
public partial class MainWindow : Window
{
public RelayCommand ButtonClickCommand { get; set; }
public MainWindow()
{
InitializeComponent();
ButtonClickCommand= new RelayCommand(MyButtonClickExcute);
}
private void MyButtonClickExcute()
{
UserControl1 userControl1 = new UserControl1 {Width = 50, Height = 50,CloseAction = RemoveUserControlFromPanel };
Panel.SetZIndex(userControl1, 10);
MainGrid.Children.Add(userControl1);
}
public void RemoveUserControlFromPanel(UserControl1 userControl1)
{
MainGrid.Children.Remove(userControl1);
}
}
and for userControl.xaml:
<Grid>
<Border Background="Blue" CornerRadius="10" BorderThickness="15" BorderBrush="Green">
<Button VerticalAlignment="Center" HorizontalAlignment="Center" Width="125" Height="75" Content="Close" Click="ButtonBase_OnClick"></Button>
</Border>
</Grid>
finally, code behind for userControl:
public partial class UserControl1 : UserControl
{
public Action<UserControl1> CloseAction { get; set; }
public UserControl1()
{
InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
CloseAction(this);
}
}
I have a BindingList binded to a DataGrid, but when I add item to that list, UI is not updating.
Here is a minimal version of code that can reproduce this problem:
MainWindow.xaml
<Window x:Class="WpfTestApp1.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:WpfTestApp1"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<CollectionViewSource x:Key="SessionSource" Source="{Binding Sessions}" />
</Window.Resources>
<Grid>
<DockPanel LastChildFill="True">
<StackPanel DockPanel.Dock="Right">
<Button x:Name="BtnTest" Click="BtnTest_OnClick" Content="Test"></Button>
</StackPanel>
<DataGrid x:Name="DG" DockPanel.Dock="Right" ItemsSource="{Binding Source={StaticResource SessionSource}}">
<DataGrid.Columns>
<DataGridTextColumn x:Name="UserName" Width="Auto" Header="Title" Binding="{Binding Title}"/>
<DataGridTextColumn x:Name="UserAction" Width="Auto" Header="Host" Binding="{Binding Host}"/>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfTestApp1
{
public class Session : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _title;
public string Title { get => _title; set { _title = value; OnPropertyChanged(); } }
private string _host;
public string Host { get => _host; set { _host = value; OnPropertyChanged(); } }
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public partial class MainWindow : Window
{
public BindingList<Session> Sessions { get; set; }
public MainWindow()
{
InitializeComponent();
Sessions = new BindingList<Session>();
}
private void BtnTest_OnClick(object sender, RoutedEventArgs e)
{
Sessions.Add(new Session(){Title = "test1", Host="test2"});
}
}
}
Tried to implement INotifyPropertyChanged interface in MainWindow class, but seems not working too.
Initialize the Sessions property before calling InitializeComponent:
public partial class MainWindow : Window
{
public BindingList<Session> Sessions { get; } = new BindingList<Session>();
public MainWindow()
{
InitializeComponent();
}
private void BtnTest_OnClick(object sender, RoutedEventArgs e)
{
Sessions.Add(new Session { Title = "test1", Host = "test2" });
}
}
In WPF it isn't necessary to use BindingList, especially not when the element type implements INotifyPropertyChanged. ObservableCollection is more common:
public ObservableCollection<Session> Sessions { get; }
= new ObservableCollection<Session>();
This is my scenario: My DataTemplate of a ListView contains a TextBox and some buttons, one of the buttons is used to select and highlight all of the text in the TextBox. I can find many solutions for select and highlight text in TextBox from code behind, but none of them define the TextBox and the Button in DataTemplate. Anyone can help?
Thanks
You can do something like this below :
XAML :
<Window x:Class="SOWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListView Width="200" Height="300" ItemsSource="{Binding FriendList}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Width="100" Margin="2" Text="{Binding Name}"></TextBox>
<Button Content="Select" Click="Button_Click"></Button>
<Button Content="Delete"></Button>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
Code Behind :
using System.Windows;
using System.Windows.Controls;
namespace SOWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var friendViewModel = new FriendViewModel();
friendViewModel.AddFriends();
this.DataContext = friendViewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var parent = (StackPanel)((sender as Button).Parent);
var children = parent.Children;
foreach(var child in children)
{
if (child.GetType().Equals(typeof(TextBox)))
{
var tb = child as TextBox;
tb.Focus();
tb.SelectAll();
break;
}
}
}
}
}
ViewModel :
using System.Collections.ObjectModel;
namespace SOWPF
{
public class FriendViewModel
{
public ObservableCollection<Friend> FriendList
{ get; set; }
public void AddFriends()
{
FriendList = new ObservableCollection<Friend>();
FriendList.Add(new Friend() { Name = "Arpan" });
FriendList.Add(new Friend() { Name = "Nrup" });
FriendList.Add(new Friend() { Name = "Deba" });
}
}
public class Friend
{
public string Name { get; set; }
}
}
Probably would be a good using an Attached property set on the button, and in the attached code using a code something like written by cvraman.
Using this way you absolutely avoid the code behind structure, and better way to using mvvm
I have a program that searches a directory for files matching certain criteria. This search process takes a long time, so I have to call it asynchronously. When the search algorithm finds a file, it triggers an event. My MainWindow instance listens for this event and needs to update the GUI. How can I bind these "added" files to a ListView? I figured that I could use an ObservableCollection<FileInfo> instance, but I can't figure out how to bind it.
I've stripped out all of the irrelevant controls and code. Here are the two relevant files.
MainWindow.xaml:
<Window x:Class="Example.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CR Search" Height="395" Width="525">
<Grid>
<ListView x:Name="Results">
<ListView.View>
<GridView>
<GridViewColumn Header="Filename"/>
<GridViewColumn Header="Directory"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
MainWindow.xaml.cs:
using System.IO;
using System.Threading.Tasks;
public partial class MainWindow
{
private SearchLogic _backgroundSearch;
private async void Search(object sender, RoutedEventArgs e)
{
// TODO: clear Results
_backgroundSearch = new SearchLogic("", new DirectoryInfo("C:\"));
_backgroundSearch.FileAdded += FileAdded;
await Task.Run(new Action(_backgroundSearch.Search));
}
private void FileAdded(object sender, FileAddedEventArgs eventArgs)
{
// TODO: add eventArgs.File to Results
// eventArgs.File is an instance of FileInfo
}
}
Here is a simple example
Your XAML
<Window x:Class="WpfApplication10.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Width="525"
Height="350"
Loaded="Window_Loaded">
<Grid>
<ListBox ItemsSource="{Binding FileNames}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Label>Name</Label>
<TextBlock Text="{Binding Name}"/>
<Label>Modified</Label>
<TextBlock Text="{Binding LastModified}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
Your Code Behind
public partial class MainWindow : Window
{
public class FileInfo
{
public string Name { get; set; }
public DateTime LastModified { get; set; }
public FileInfo(string name)
{
Name = name;
LastModified = DateTime.Now;
}
}
ObservableCollection<FileInfo> mFileNames = new ObservableCollection<FileInfo>();
public ObservableCollection<FileInfo> FileNames
{
get
{
return mFileNames;
}
}
public MainWindow()
{
DataContext = this;
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ThreadPool.QueueUserWorkItem((x) =>
{
while (true)
{
Dispatcher.BeginInvoke((Action)(() =>
{
mFileNames.Add(new FileInfo("X"));
}));
Thread.Sleep(500);
}
});
}
}
If you run this problem you will notice that the listbox updates every half a second with a new item. Basically the key thing to note is that the ObservableCollection can only be updated from the UI thread so if you refactor the above code you need need to somehow use the Dispatcher of the current UI thread to update it
I have been trying to reproduce this example: http://www.codeproject.com/KB/WPF/TreeViewWithViewModel.aspx
For some reason my treeview isn't showing any data at all and I can't figure out why (I get no errors and don't really know how to debug this decently). This is my code:
MainWindow.XAML
<Window x:Class="Tryout.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Tryout.domain"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<TreeView Name="treeViewFiles" HorizontalAlignment="Left" Margin="10,10,0,12" Width="200" ClipToBounds="True">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Directory}" ItemsSource="{Binding Children}">
<Label Content="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType ="{x:Type local:File}">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
MainWindow.XAML.cs
namespace Tryout
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Directory rootDirectory = new Directory("Root");
rootDirectory.Children.Add(new Directory("Subdirectory 1"));
rootDirectory.Children.Add(new Directory("Subdirectory 2"));
((Directory)rootDirectory.Children[1]).Children.Add(new File("The only file"));
treeViewFiles.ItemsSource = rootDirectory.Children;
}
}
}
File.cs
namespace Tryout.domain
{
public class File : INotifyPropertyChanged
{
public string Name;
public File(String _name)
{
Name = _name;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
Directory.cs
namespace Tryout.domain
{
public class Directory : File
{
public List<File> Children = new List<File>();
public Directory(String _name) : base(_name) { }
}
}
Your File and Directory classes expose their data through fields, not properties, and you can't bind to fields. Change public fields Name and Children to properties and your code will work.