How do I change the control used by TreeViewItem in C# - c#

When I declare a TreeView in the XAML, I can use a control of my choice (here, a StackPanel) for the elements that are immediately added to it:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<DockPanel Name="dockPanel1">
<TreeView Name="treeView1">
<TreeView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ProgressBar Height="15" Width="160" />
<TextBlock Foreground="Red" Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</TreeView.ItemTemplate>
<sys:String>Foo</sys:String>
<sys:String>Bar</sys:String>
</TreeView>
</DockPanel>
</Window>
How can I achieve the same thing when adding elements from the C# code?
namespace WpfApplication5
{
public partial class MainWindow : Window
{
public MainWindow() {
InitializeComponent();
// I want something more complex than just "Quux".
var item = new TreeViewItem { Header = "Quux" };
treeView1.Items.Add(item);
}
}
}

When I declare a TreeView in the XAML, I can use a control of my choice (here, a StackPanel) for the elements that are immediately added to it
That goes for all items, in code just do:
treeView1.Items.Add("Text");
Or
treeView1.ItemsSource = new[]
{
"One", "Two"
};
Unless you add UI elements the defined DataTemplate will be used.
Might want to read some references...

Just do it :) .
namespace WpfApplication5
{
public partial class MainWindow : Window
{
public MainWindow() {
InitializeComponent();
var stackPanel = new StackPanel { Orientation = Orientation.Horizontal };
stackPanel.Children.Add(new ProgressBar { Height = 15, Width = 160 });
stackPanel.Children.Add(new TextBlock { Foreground = new SolidColorBrush(Colors.Red), Text = "Quux" });
var item = new TreeViewItem { Header = stackPanel };
treeView1.Items.Add(item);
}
}
}

Related

Adding TabItems (with UserControl) to TabControl in C#: Binding Header is always empty

I am trying to create a TabControl that can have multiple pages with the same input fields.
I have combined the input fields in a UserControl called TabContent. I have created a TabControl in the MainWindow, and I want to integrate TabItem headers from the MainWindow via the ItemTemplate resp. DataTemplate. In the MainWindow, I create a list of TabContents in which text is assigned to the headers. This text is not displayed by the running programme as shown in the screenshot. Any idea why and how to fix this?
Screenshot with empty TabItem Headers
Below is a minimal code example that reproduces the error.
// UserControl1.xaml.cs
namespace Nur_Test
{
public partial class TabContent : UserControl
{
public TabContent()
{
InitializeComponent();
}
public string Header { get; set; }
}
}
// UserControl1.xaml
<UserControl x:Class="Nur_Test.TabContent"
// xmlns= [omitted...]
xmlns:local="clr-namespace:Nur_Test"
mc:Ignorable="d"
d:DesignHeight="200" d:DesignWidth="400">
<Grid>
<TextBox>Test</TextBox>
</Grid>
</UserControl>
// MainWindow.xaml
<Window x:Class="Nur_Test.MainWindow"
// xmlns= [omitted...]
xmlns:local="clr-namespace:Nur_Test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TabControl x:Name="tabControl1">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</Grid>
</Window>
// MainWindow.xaml.cs
namespace Nur_Test
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<TabContent> items = new List<TabContent>()
{
new TabContent() { Header = "Item 1" },
new TabContent() { Header = "Item 2" },
new TabContent() { Header = "Item 3" },
};
tabControl1.ItemsSource = items;
}
public class TabItemData
{
public string Header { get; set; }
}
}
}
It seems the binding that does not work. Putting a fixed string into the TextBlock instead of {Binding Header} in MainWindow.xaml works fine (but does not solve my problem):
<TextBlock Text="12345" />
Sorry if this is a stupid question, I'm still a beginner when it comes to C# :-)
Set the ItemsSource to a List<TabItemData>:
List<TabItemData> items = new List<TabItemData>()
{
new TabItemData() { Header = "Item 1" },
new TabItemData() { Header = "Item 2" },
new TabItemData() { Header = "Item 3" },
};
tabControl1.ItemsSource = items;
...and then use the UserControl in the ContentTemplate:
<TabControl x:Name="tabControl1">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<local:TabContent />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
You are not supposed to set or bind the ItemsSource to a collection of UI elements such as UserControls.
Your advice helped me a lot, mm8. In order for the code to finally work the way I wanted, I also had to create the class TabItemData. And I also needed to bind the content so that it would change when the tabs were clicked. Now it seems to work. Thank you very much!
Here are the final code modifications that worked for me:
// MainWindow.xaml.cs
public class TabItemData
{
public string Header { get; set; }
public TabContent Content { get; set; }
}
and
// MainWindow.xaml.cs
List<TabItemData> items = new List<TabItemData>()
{
new TabItemData() { Header = "Item 1", Content = new TabContent() },
new TabItemData() { Header = "Item 2", Content = new TabContent() },
new TabItemData() { Header = "Item 3", Content = new TabContent() }
};
tabControl1.ItemsSource = items;
plus
// MainWindow.xaml
<TabControl x:Name="tabControl1" Grid.Row="2" Grid.Column="0" Margin="10,0">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding Content}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>

Binding to SelectedItem of ComboBox in UserControl

I have a UserControl consisting of a ComboBox with a Label. I am looking to update a screen with an instance of this ComboBox and dynamically create UserControls in a StackPanel, based on the SelectedItem value.
I currently have a screen with an instance of this ComboBox and have it binding the following way:
Pseudocode Example (removing unrelated code):
<!-- MyComboBoxExample.xaml -->
<ComboBox x:Name="myComboBox" SelectedValuePath="Key" DisplayMemberPath="Value" ItemsSource="{Binding MyBoxItems}/>
/* MyComboBoxExample.xaml.cs */
public static readonly DependencyProperty MyBoxItemsProperty = DependencyProperty.Register("MyBoxItems", typeof(Dictionary<string, string>),
typeof(MyComboBoxExample), new PropertyMetadata(null));
<!-- MyScreen.xaml -->
<local:MyComboBoxExample x:Name="MyComboBoxExampleInstance" MyBoxItems="{Binding Descriptions}"/>
I am new to WPF and databinding, so not sure the best way to implement this. Basically, on the screen: when MyComboBoxExampleInstance selection changes, dynamically set the controls of a StackPanel on the screen. I am not sure how to properly hook in to the SelectionChanged event of a child object of a UserControl.
Any thoughts, corrections, and (constructive) criticism is appreciated. Thanks for any help in advance.
There are several ways to go about this. Here's one way. It's not necessarily the best way but it's easy to understand.
First, the user control xaml. Note the binding of the ItemsSource property on the user control, which specifies MyComboBoxItems as the items source. More on where that comes from in a bit.
<UserControl x:Class="WpfApp1.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<ComboBox Height="Auto" ItemsSource="{Binding MyComboBoxItems}" SelectionChanged="OnSelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</UserControl>
Now the code-behind, MyUserControl.xaml.cs. We provide an combobox selection changed event handler that in turn raises a custom event, MyComboBoxSelectionChanged, which is defined by the event argument class and delegate handler at the bottom of the code. Our OnSelectionChanged method simply forwards the selection change event via the custom event we've defined.
using System;
using System.Windows.Controls;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MyUserControl.xaml
/// </summary>
public partial class MyUserControl : UserControl
{
public event MyComboBoxSelectionChangedEventHandler MyComboBoxSelectionChanged;
public MyUserControl()
{
InitializeComponent();
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
MyComboBoxSelectionChanged?.Invoke(this,
new MyComboBoxSelectionChangedEventArgs() {MyComboBoxItem = e.AddedItems[0]});
}
}
}
public class MyComboBoxSelectionChangedEventArgs : EventArgs
{
public object MyComboBoxItem { get; set; }
}
public delegate void MyComboBoxSelectionChangedEventHandler(object sender, MyComboBoxSelectionChangedEventArgs e);
}
Now we go to our MainWindow.xaml, where we define an instance of MyUserControl and set a handler for the custom event we defined. We also provide a StackPanel to host the items that will be created on a selection changed event.
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<local:MyUserControl Width="140" Height="32" DataContext="{Binding}" Grid.Row="0" MyComboBoxSelectionChanged="OnSelectionChanged"></local:MyUserControl>
<StackPanel Grid.Row="1" x:Name="MyUserControls"/>
</Grid>
</Window>
Now the code-behind for MainWindow.xaml. Here we define a public property containing a list of objects of type MyComboBoxItem (defined at the bottom of the file), and we initialize the array with some values.
Recall that we set the ItemsSource property of the ComboBox inside MyUserControl to "{Binding MyComboBoxItems}", so the question is, how does the property defined in the MainWindow magically become available in MyUserControl?
In WPF, DataContext values are inherited from parent controls if they aren't explicitly set, and since we did not specify a data context for the control, the instance of MyUserControl inherits the DataContext of the parent window. In the constructor we set the MainWindow data context to refer to itself, so the MyComboBoxItems list is available to any child controls (and their children, and so on.)
Typically we'd go ahead and add a dependency property for the user control called ItemsSource and in the user control we'd bind the ComboBox's ItemsSource property to the dependency property rather than to MyComboxItems.
MainWindow.xaml would then bind it's collection directly to the dependency property on the user control. This helps make the user control more re-usable since it wouldn't depend on specific properties defined in an inherited data context.
Finally, in the event handler for the user control's custom event we obtain the value selected by the user and create a UserControl populated with a text box (all with various properties set to make the items interesting visually) and we directly add them to the Children property of the StackPanel.
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public List<MyComboBoxItem> MyComboBoxItems { get; set; } = new List<MyComboBoxItem>()
{
new MyComboBoxItem() {Text = "Item1"},
new MyComboBoxItem() {Text = "Item2"},
new MyComboBoxItem() {Text = "Item3"},
};
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void OnSelectionChanged(object sender, MyComboBoxSelectionChangedEventArgs e)
{
if (e.MyComboBoxItem is MyComboBoxItem item)
{
MyUserControls.Children.Add(
new UserControl()
{
Margin = new Thickness(2),
Background = new SolidColorBrush(Colors.LightGray),
Content = new TextBlock()
{
Margin = new Thickness(4),
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
FontSize = 48,
FontWeight = FontWeights.Bold,
Foreground = new SolidColorBrush(Colors.DarkGreen),
Text = item.Text
}
});
}
}
}
public class MyComboBoxItem
{
public string Text { get; set; }
}
}
Finally, I'd consider using an ItemsControl or a ListBox bound to an ObservableCollection rather than sticking things into a StackPanel. You could define a nice data template for the user control to display and maybe a DataTemplateSelector to use different user controls based on settings in the data item. This would allow me to simply add the reference to the MyComboBoxItem obtained in the selection changed handler to that collection, and the binding machinery would automatically generate a new item using the data template I defined and create the necessary visual elements to display it.
So given all that, here are the changes to do all that.
First, we modify our data item to add a color property. We'll use that property to determine how we display the selected item:
public class MyComboBoxItem
{
public string Color { get; set; }
public string Text { get; set; }
}
Now we implement INotifyPropertyChanged in MainWindow.xaml.cs to let the WPF binding engine update the UI when we change properties. This is the event handler and a helper method, OnPropertyChanged.
We also modify the combo box initializer to add a value for the Color property. We'll leave on blank for fun.
We then add a new ObservableCollect, "ActiveUserControls" to store the MyComboBoxItem received in the combo box selection changed event. We do that instead of creating user controls on the fly in code.
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public List<MyComboBoxItem> MyComboBoxItems { get; set; } = new List<MyComboBoxItem>()
{
new MyComboBoxItem() {Text = "Item1", Color = "Red"},
new MyComboBoxItem() {Text = "Item2", Color = "Green"},
new MyComboBoxItem() {Text = "Item3"},
};
private ObservableCollection<MyComboBoxItem> _activeUserControls;
public ObservableCollection<MyComboBoxItem> ActiveUserControls
{
get => _activeUserControls;
set { _activeUserControls = value; OnPropertyChanged(); }
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void OnSelectionChanged(object sender, MyComboBoxSelectionChangedEventArgs e)
{
if (e.MyComboBoxItem is MyComboBoxItem item)
{
if (ActiveUserControls == null)
{
ActiveUserControls = new ObservableCollection<MyComboBoxItem>();
}
ActiveUserControls.Add(item);
}
}
}
Now let's look at some changes we made to MyUserControl. We've modified the combo box ItemsSource to point at a property, ItemsSource defined in MyUserControl, and we also map the ItemTemplate to an ItemTemplate property in MyUserControl.
<UserControl x:Class="WpfApp1.MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<Grid>
<ComboBox Height="Auto"
ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyUserControl}}}"
ItemTemplate="{Binding ItemTemplate, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyUserControl}}}"
SelectionChanged="OnSelectionChanged">
</ComboBox>
</Grid>
</UserControl>
Here's were we define those new properties in MyUserControl.cs.
public partial class MyUserControl : UserControl
{
public event MyComboBoxSelectionChangedEventHandler MyComboBoxSelectionChanged;
public MyUserControl()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource",
typeof(System.Collections.IEnumerable),
typeof(MyUserControl),
new PropertyMetadata(null));
public System.Collections.IEnumerable ItemsSource
{
get => GetValue(ItemsSourceProperty) as IEnumerable;
set => SetValue(ItemsSourceProperty, (IEnumerable)value);
}
public static readonly DependencyProperty ItemTemplateProperty =
DependencyProperty.Register("ItemTemplate",
typeof(DataTemplate),
typeof(MyUserControl),
new PropertyMetadata(null));
public DataTemplate ItemTemplate
{
get => GetValue(ItemTemplateProperty) as DataTemplate;
set => SetValue(ItemTemplateProperty, (DataTemplate)value);
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
MyComboBoxSelectionChanged?.Invoke(this,
new MyComboBoxSelectionChangedEventArgs() {MyComboBoxItem = e.AddedItems[0]});
}
}
}
Let's look at how we bind to those in MainWindow.xaml:
<local:MyUserControl Width="140"
Height="32"
Grid.Row="0"
MyComboBoxSelectionChanged="OnSelectionChanged"
ItemsSource="{Binding MyComboBoxItems}"
ItemTemplate="{StaticResource ComboBoxItemDataTemplate}" />
So now we can bind our items directly and provide our own data template to specify how the combobox should display the item.
Finally, I want to replace the StackPanel with an ItemsControl. This is like a ListBox without scrolling or item selection support. In fact, ListBox is derived from ItemsControl. I also want to use a different user control in the list based on the value of the Color property. To do that, we define some data templates for each value in MainWindow.Xaml:
<DataTemplate x:Key="ComboBoxItemDataTemplate"
DataType="local:MyComboBoxItem">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="4"
Text="{Binding Text}" />
<TextBlock Margin="4"
Text="{Binding Color}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="GreenUserControlDataTemplate"
DataType="local:MyComboBoxItem">
<local:GreenUserControl DataContext="{Binding}" />
</DataTemplate>
<DataTemplate x:Key="RedUserControlDataTemplate"
DataType="local:MyComboBoxItem">
<local:RedUserControl DataContext="{Binding}" />
</DataTemplate>
<DataTemplate x:Key="UnspecifiedUserControlDataTemplate"
DataType="local:MyComboBoxItem">
<TextBlock Margin="4"
Text="{Binding Text}" />
</DataTemplate>
Here's RedUserControl. Green is the same with a different foreground color.
<UserControl x:Class="WpfApp1.RedUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<Grid Background="LightGray"
Margin="2">
<TextBlock Margin="4"
Foreground="DarkRed"
TextWrapping="Wrap"
Text="{Binding Text}"
FontSize="24"
FontWeight="Bold" />
</Grid>
</UserControl>
Now the trick is to use the right data template based on the color value. For that we create a DataTemplateSelector. This is called by WPF for each item to be displayed. We can examine the data context object a choose which data template to use:
public class UserControlDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (container is FrameworkElement fe)
{
if (item is MyComboBoxItem cbItem)
{
if (cbItem.Color == "Red")
{
return fe.FindResource("RedUserControlDataTemplate") as DataTemplate;
}
if (cbItem.Color == "Green")
{
return fe.FindResource("GreenUserControlDataTemplate") as DataTemplate;
}
return fe.FindResource("UnspecifiedUserControlDataTemplate") as DataTemplate;
}
}
return null;
}
}
We create an instance of our data template selector in xaml in MainWindow.xaml:
<Window.Resources>
<local:UserControlDataTemplateSelector x:Key="UserControlDataTemplateSelector" />
...
Finally we replace our stack panel with an Items control:
<ItemsControl Grid.Row="1"
x:Name="MyUserControls"
ItemsSource="{Binding ActiveUserControls}"
ItemTemplateSelector="{StaticResource UserControlDataTemplateSelector}" />

wpf tabcontrol set different text to textblock in content template for all tabs on window load from code behind using C#

I am working on a WPF application where I want to use TabControl with ItemTemplate and ContentTemplate. I have a textblock in the ItemTemplate (for Header) and another textblock in the ContentTemplate(for Content).
I am binding a list to TabControl in constructor from code behind.
In Window loaded event, I want to loop over all tab items, access textblock in content template (Name="txt1") for each tab and set different text to textblock in each tab.
Below is my XAML:
<TabControl x:Name="mainTabs">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Name="txt1"></TextBlock>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Code behind:
public MainWindow()
{
InitializeComponent();
// Read tabs data from xml file
List<MyTab> lst = ReadTabsFromXml();
mainTabs.ItemsSource = lst;
}
public class MyTab
{
public string Header { get;set; }
public string Content { get; set; }
}
Please help me with this.
You don't need to do that. Just store your MyTab classes in an ObservableCollection<MyTab> instead of a List<MyTab>, and set the ItemsSource to the collection. The TabControl will update automatically.
EDIT: I've updated the answer, with a solution regarding using Gecko browser. You can wrap the Gecko browser in a UserControl that you use in each TabItem. Also note that a TabControl recreates the the TabItem every time you switch tabs, which also causes the webpage to reload. You can get around this by using e.g. TabControEx instead of TabControl (https://stackoverflow.com/a/9802346/6839733). But this is not related to the Gecko browser or to binding URL's from a collection - it's just how the TabControl works.
MainWindow.xaml:
<TabControl x:Name="MyTabControl">
<TabControl.Resources>
<DataTemplate x:Key="MyTabContentTemplate" x:Shared="False">
<local:GeckoBrowser Uri="{Binding Path=Uri}" />
</DataTemplate>
<Style TargetType="{x:Type TabItem}">
<Setter Property="ContentTemplate" Value="{StaticResource MyTabContentTemplate}" />
</Style>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public ObservableCollection<WebPageInfo> WebPageCollection { get; set; }
public MainWindow()
{
InitializeComponent();
// Read tabs data from xml file
//lst = ReadTabsFromXml();
WebPageCollection = new ObservableCollection<WebPageInfo>();
WebPageCollection.Add(new WebPageInfo() { Header = "Tab1", Uri = "https://www.amazon.com/" });
WebPageCollection.Add(new WebPageInfo() { Header = "Tab2", Uri = "https://www.cnn.com/" });
WebPageCollection.Add(new WebPageInfo() { Header = "Tab3", Uri = "https://www.microsoft.com/" });
WebPageCollection.Add(new WebPageInfo() { Header = "Tab4", Uri = "https://www.facebook.com/" });
MyTabControl.ItemsSource = WebPageCollection;
MyTabControl.SelectedIndex = 0;
}
public class WebPageInfo
{
public string Header { get; set; }
public string Uri { get; set; }
}
}
GeckoBrowser.xaml:
<UserControl x:Class="WpfApplication1.GeckoBrowser"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Border x:Name="MyBorder" Background="Black" Margin="0" />
</UserControl>
GeckoBrowser.xaml.cs:
public partial class GeckoBrowser : UserControl
{
public static readonly DependencyProperty UriProperty =
DependencyProperty.Register("Uri", typeof(string), typeof(GeckoBrowser), new FrameworkPropertyMetadata(null));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
public GeckoBrowser()
{
InitializeComponent();
Xpcom.Initialize("Firefox");
Loaded += GeckoBrowser_Loaded;
}
private void GeckoBrowser_Loaded(object sender, RoutedEventArgs e)
{
WindowsFormsHost host = new WindowsFormsHost();
GeckoWebBrowser browser = new GeckoWebBrowser();
browser.Navigate(Uri);
host.Child = browser;
MyBorder.Child = host;
}
}

How can I get data from user Control in MainWindow

I have user control which has two controls
Label and TextBlock
<UserControl x:Class=MyClass....
d:DesignHeight="300" d:DesignWidth="300" x:Name="MyUsrCtrl">
<StackPanel>
<Label Content={Binding MyLabelContent} x:Name="MyLabel"...../>
<TextBlock Content={Binding MyTextBlockContent} x:Name="MyTextBlock"...../>
</StackPanel>
</UserControl>
and In my MainWindow I have a ListBox whose ItemSource is binded to collection of this usercontrol
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
<Grid>
<ListBox x:Name="myListBox" Grid.Row="0"
ItemsSource="{Binding Path=_myControl}"> // collection of user controls
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<local:MyUserControl x:Name="myUserControl" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
How can I get the value of the Textblock and Label when any item is selected in Listbox?
this worked for me:
Created a model called MyControl that "represents" the data in MyUserControl
Created an ObservableCollection That "represents" the data in the Listbox
This way you can also the delete all the x:Name
Seperates Data from UI
MainWindow.xaml
<ListBox x:Name="MyListBox" Grid.Row="0"
ItemsSource="{Binding MyControls}"
SelectionChanged="MyListBox_OnSelectionChanged"
SelectionMode="Single"
>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<local:MyUserControl></local:MyUserControl>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MainWindow.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
MyControls = new ObservableCollection<MyControl>();
var a = new MyControl { MyLabelContent = "label content 1", MyTextBlockContent = "Text content 1" };
var b = new MyControl { MyLabelContent = "label content 2", MyTextBlockContent = "Text content 2" };
MyControls.Add(a);
MyControls.Add(b);
}
private void MyListBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listBox = sender as ListBox;
if (listBox != null)
{
var selectedItem = listBox.SelectedItems[0] as MyControl;
var textBlockContent = selectedItem.MyTextBlockContent; //text in textblock
var labelContent = selectedItem.MyLabelContent; //text in label
}
}
private ObservableCollection<MyControl> _myControls;
public ObservableCollection<MyControl> MyControls
{
get { return _myControls; }
set
{
_myControls = value;
NotifyPropertyChanged("MyControls");
}
}
#region PropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
MyUserControl.XAML
<StackPanel>
<Label Content="{Binding MyLabelContent}" />
<TextBlock Text="{Binding MyTextBlockContent}" />
</StackPanel>
MyControl.cs
public class MyControl
{
public string MyLabelContent { get; set; }
public string MyTextBlockContent { get; set; }
}
Hopes this works for you :)
Here is a link to a working sample:
https://drive.google.com/file/d/0B8O-XH0V_o1hNXprX2c0S0xJUFU/view?usp=sharing
If you want to get TextBlock from selected item, you can do like this:
var selectedUserControl = myListBox.SelectedItem as MyUserControl;
TextBlock textBlock = selectedUserControl.MyTextBlock;
Hope helps!

How to find DataTemplate-Generated UIElement

Hi i try to find the generated UIElement from a DataTemplate
but i'm not able to find my UserControl which should be somewhere in my ContentPresenter
i looked in to the control via a Breakpoint and Snoop but i cant find the UserControl
can someone please enlight where i can find it?
Here my Test Project:
App XAML
<Application.Resources>
<DataTemplate x:Name="TESTTemplate" DataType="{x:Type vmv:VM}">
<vmv:MyView/>
</DataTemplate>
</Application.Resources>
View
<UserControl ...>
<DataGrid ItemsSource="{Binding MyItems}"/>
</UserControl>
VM
public class VM
{
private ObservableCollection<MyRow> myItems;
public ObservableCollection<MyRow> MyItems
{
get { return myItems; }
set { myItems = value; }
}
public VM()
{
myItems = new ObservableCollection<MyRow>();
myItems.Add(new MyRow { Text = "a", Number = 1 });
myItems.Add(new MyRow { Text = "s", Number = 2 });
myItems.Add(new MyRow { Text = "d", Number = 3 });
myItems.Add(new MyRow { Text = "f", Number = 4 });
}
}
public class MyRow
{
public string Text { get; set; }
public int Number { get; set; }
}
MainWindow XAML
<Window x:Class="MyPrinterAPI.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">
<StackPanel>
<ContentPresenter Name="CPresenter">
</ContentPresenter>
</StackPanel>
</Window>
CodeBehind
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
var vm = new VM();
DataContext =vm;
CPresenter.Content = vm;
}
}
VisualTreeHelper can get you UserControl but like you mentioned in another answer, you want to know where exactly this property is set.
You can get that using Reflection which is set on private field _templateChild like this. But still i would suggest use VisualTreeHelper to get that.
var userControl = typeof(FrameworkElement).GetField("_templateChild",
BindingFlags.Instance | BindingFlags.NonPublic).GetValue(CPresenter);
You shouldn't be using a ContentPresenter directly in your MainWindow XAML... that is not what they are for. Instead of that, it is more common to use a ContentControl (which has its own ContentPresenter inside):
<Window x:Class="MyPrinterAPI.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">
<StackPanel>
<ContentControl Name="ViewControl" Content="{Binding ViewModel}"
ContentTemplate="{StaticResource TESTTemplate}" />
</StackPanel>
</Window>
...
Also, you'd need to name your UserControl:
<DataTemplate x:Name="TESTTemplate" DataType="{x:Type vmv:VM}">
<vmv:MyView name="View" />
</DataTemplate>
Using this setup, you should be able to do the following (adapted from the How to: Find DataTemplate-Generated Elements page on MSDN):
// Getting the ContentPresenter from the ViewControl
ContentPresenter myContentPresenter =
VisualTreeHelper.GetChild(ViewControl, 0) as ContentPresenter;
if (myContentPresenter != null)
{
// Finding View from the DataTemplate that is set on that ContentPresenter
DataTemplate myDataTemplate = myContentPresenter.ContentTemplate;
MyView myView = (MyView)myDataTemplate.FindName("View", myContentPresenter);
// Do something to the myView control here
}
The technique is explained on MSDN How to: Find DataTemplate-Generated Elements
It uses VisualTreeHelper to search through the visual tree.

Categories

Resources