Adding content to a TreeViewItem programmatically - c#

I'm trying to create a new TreeViewItem with a control in it like:
<TreeViewItem>
<TreeViewItem.Header>
<StackPanel>
<Button/>
</StackPanel>
<TreeViewItem.Header>
<TreeViewItem>
Except, I'd like to do it at runtime (I'm using C#), but I can't work out how to do this. Can you help?
This is my code that I'm using to generate the node. Somewhere in here I would like to insert a numeric up/down control. I don't have that control yet, but for arguement's sake, let's say that I want to insert a button.
private void TreeView_AfterSelect(object sender, System.Windows.Forms.TreeViewEventArgs e)
{
if (TreeView.SelectedNode != null)
{
if (((vcvscompiler.DataTypes.dataObjectv)(TreeView.SelectedNode.Tag))._vcardName.re == "adr_work")
{
foreach (string k in ((vcvscompiler.DataTypes.dataObjectv)(TreeView.SelectedNode.Tag))._prefs)
{
TreeViewItem newChild = new TreeViewItem();
newChild.Header = k;
treeView1.Items.Add(newChild);
}
}
}
}
WPF:
<Window.Resources>
<DataTemplate x:Key="myTaskTemplate">
<StackPanel>
<Button content="This is a button!" />
</StackPanel>
</DataTemplate>
</Window.Resources>

new TreeViewItem {
Header = new StackPanel {
Children = {
new Button { ... }
}
}
}

Related

Why does a dynamic TabControl seem to be unable to switch between the tabs?

I'm using a dynamic TabControl in a page:
<TabControl x:Name="DynamicTabControl"
ItemsSource="{Binding TabsCollection, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:TabView}}}"
SelectionChanged="tabDynamic_SelectionChanged">
<TabControl.ItemTemplate>
<DataTemplate x:Name="TabHeader" DataType="TabItem">
<DockPanel Width="30">
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=TabItem }, Path=Header}" />
</DockPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate >
<local:TabItem/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
public ObservableCollection<TabItem> TabsCollection {get;set;}
private TabItem _tabAdd;
#region Initialization
public TabView()
{
InitializeComponent();
TabsCollection = new ObservableCollection<TabItem>();
TabsCollection.Add(new TabItem { Header = "Tab 1"});
// add a tabItem with + in header
_tabAdd = new TabItem
{
Header = "+"
};
TabsCollection.Add(_tabAdd);
DynamicTabControl.DataContext = TabsCollection;
DynamicTabControl.SelectedIndex=0;
}
#endregion
private TabItem AddSubsequentTabItem()
{
int count = TabsCollection.Count;
TabItem tab = new TabItem();
tab.Header = string.Format("Tab {0}", count);
tab.Name = string.Format("tab{0}", count);
TabsCollection.Insert(count - 1, tab);
return tab;
}
private void tabDynamic_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
TabItem tab = DynamicTabControl.SelectedItem as TabItem;
if (tab == null) return;
if (tab.Equals(_tabAdd))
{
// clear tab control binding
DynamicTabControl.DataContext = null;
TabItem newTab = this.AddSubsequentTabItem();
// bind tab control
DynamicTabControl.DataContext = TabsCollection;
// select newly added tab item
DynamicTabControl.SelectedItem = newTab;
}
else
Dispatcher.BeginInvoke((Action)(() => DynamicTabControl.SelectedIndex = DynamicTabControl.SelectedIndex));
}
But, I'm unable to get the tab control to switch between my TabItems.
I'm using an amalgamation of the dynamic tab control from https://www.codeproject.com/Articles/493538/Add-Remove-Tabs-Dynamically-in-WPF , with modifications to create the GUI objects from within xaml.
My title pretty much says it all: Why is my TabControl not switching tabs? Even besides fixing the problem, I'd like to know if subscribing a method to the "selectionChanged" event unsubscribes the native tab switching.
Here is the barebones example project of the problem

Get names of control in code behind when clicked, wpf

I have a usercontrol and I want to get the names of the control user clicks on this usercontrol.
The Usercontrol xaml is given below:
<Grid Name="Grid1">
<ListView Name ="ListView1">
<listbox.resources>
<Datatemplate>
<Togglebutton Name="Button1">
</Togglebutton>
</Datatemplate>
</listbox.resources>
</Listview>
<Border Name="Border1">
<ContentControl>
</ContentControl>
</Border>
</Grid>
Now in my code behind, I wrote:
this.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(mouseclicked);
private void mouseclicked(object sender, MouseButtonEventArgs e)
{
var source = e?.OriginalSource as FrameworkElement;
if (source != null)
{
var parent = source.Parent as FrameworkElement;
string name = string.Empty;
if (parent != null)
{
name = parent.Name;
}
else
{
name = source.Name;
}
//If clicked outside the usercontrol, then usercontrol should close down
if (!(name.Contains("Border1") || name.Contains("Grid1"))) //very strange values sometimes null or empty
{
//then do something
}
}
}
How can I get the names of the controls used in my Usercontrol in codebehind?
You have gave the grid, border and listview a name in XAML. This you should access in codebehind and check in the mouseclicked event, which element is mouse over.
private void mouseclicked(object sender, MouseButtonEventArgs e)
{
if (ListView1.IsMouseOver && Grid1.IsMouseOver)
{
MessageBox.Show("List view clicked");
}
if (Border1.IsMouseOver && Grid1.IsMouseOver)
{
MessageBox.Show("Border1 clicked");
}
if (Grid1.IsMouseOver)
{
MessageBox.Show("Grid1 clicked");
}
}
This would may be not the best, but could work to get the names of controls the user has clicked.

Reach a TextBlock from a specific ListViewItem from the ListView in Windows Phone 8.1 XAML programmatically

I am a new developer on Windows Phone 8.1, I am try to reach a specific ListView item from the ListView collection and be able to color it or color the TextBock inside of it, But I can't reach the item or reach any of items inside of ListView, Please take a look for my below code :
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
SQLiteRT db1 = new SQLiteRT();
var db_connection = await db1.Connection("MyDB.sqlite");
List<MyTBL> t_list = db1.GetTable("SELECT * FROM MyTBL LIMIT 4 ORDER BY RANDOM() ;");
db_connection.Close();
LV_Options.ItemsSource = t_list;
}
// my List View called LV_Options
private void LV_Options_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView lv1 = sender as ListView;
if (lv1 == null)
return;
MyTBL wrd = lv1.SelectedItem as MyTBL;
if (wrd == null)
return;
TextBlock tb = lv1.FindName("TB_AMean1") as TextBlock;
tb.FontSize = 17; // here I got debug error (it not worked !!!!!!!)
var item = LV_Options.Items.ElementAt(3); // this seems not work also !!!!
item.BackColor = Color.LightSteelBlue;
}
As you can see above, I tried to reach a specific item by LV_Options.Items.ElementAt(3) but it doesn't work! I also tried to reach the TextBlock from the selected List view item, but also not worked !
(Updated)
XAML code :
<!-- Title Panel -->
<StackPanel Grid.Row="0" Margin="19,0,0,0">
<TextBlock Name="TB_Rslt" Text="Here result of your answer" Style="{ThemeResource TitleTextBlockStyle}" Margin="0,12,0,0"/>
<TextBlock Text="page title" Margin="0,-6.5,0,26.5" Style="{ThemeResource HeaderTextBlockStyle}" CharacterSpacing="{ThemeResource PivotHeaderItemCharacterSpacing}"/>
</StackPanel>
<!--TODO: Content should be placed within the following grid-->
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,10,19,15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Name="TB_Question" Text="Choose Answer " Margin="0,0,25,0" HorizontalAlignment="Right" FontWeight="Bold" FontSize="22" FontFamily="Verdana" RenderTransformOrigin="0.5,0.5" />
<TextBlock Name="TB_EnWord" Text="" Margin="90,0,15,0" HorizontalAlignment="Left" FontWeight="Bold" FontSize="22" FontFamily="Verdana" RenderTransformOrigin="0.5,0.5" TextAlignment="Right" />
<StackPanel Grid.Row="1" Margin="5,22,0,0">
<ListView Name="LV_Options" SelectionChanged="LV_Options_SelectionChanged">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="6">
<StackPanel VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Name="TB_AMean1" Text="{Binding AMean1}" TextWrapping="Wrap"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
<Button Name="Btn_Answer" Content="Ansewr" HorizontalAlignment="Left" Grid.Row="1" VerticalAlignment="Bottom" Click="Btn_Answer_Click"/>
My application is a quiz application that offer 4 choices/options as answers for each question, and when user select a true answer, I want to highlight the true answer(true choice) by make its background to green, and if the user selected wrong answer/option I want to make the background of that answer (a specific List View item) with red.
Any help please ?
You're not going to be able to access an element inside a data template like that. Instead, leverage the binding to a view model to set the color and other view-related properties. First, create a wrapper view model for your data class:
public class MyTBLViewModel : INotifyPropertyChanged
{
public MyTBL Entity
{
get { return _entity; }
}
private readonly MyTBL _entity;
public Brush Highlight
{
get { return _brush; }
set
{
_brush = value;
RaisePropertyChanged("Highlight");
}
}
private Brush _highlight;
public double ItemFontSize
{
get { return _itemFontSize; }
set
{
_itemFontSize = value;
RaisePropertyChanged("ItemFontSize");
}
}
private Brush _itemFontSize;
public MyTBLViewModel(MyTBL entity)
{
_entity = entity;
_highlight = new SolidColorBrush(Colors.Transparent);
_itemFontSize = 12;
}
public event PropertyChangedEventArgs PropertyChanged;
protected void RaisePropertyChanged(string propName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propName));
}
}
Use this as your ItemsSource:
List<MyTBLViewModel> t_list = db1.GetTable("SELECT * FROM MyTBL LIMIT 4 ORDER BY RANDOM() ;")
.AsEnumerable().Select(entity => new MyTBLViewModel(entity)).ToList();
Now in your view, bind the view elements to "Highlight" and "ItemFontSize", and to any other properties you like:
<ListView.ItemTemplate>
<DataTemplate>
<Grid Margin="6" Background="{Binding Highlight}">
<StackPanel VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Name="TB_AMean1" Text="{Binding Entity.AMean1}" TextWrapping="Wrap"
FontSize="{Binding ItemFontSize}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
Finally, you can get the data item from the SelectionChangedEventArgs -- use it to update your view-related properties:
private void LV_Options_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
foreach (var item in e.AddedItems.OfType<MyTBLViewModel>())
{
item.Highlight = new SolidColorBrush(Color.LightSteelBlue);
item.ItemFontSize = 17;
}
foreach (var item in e.RemovedItems.OfType<MyTBLViewModel>())
{
item.Highlight = new SolidColorBrush(Colors.Transparent);
item.ItemFontSize = 12;
}
}
var item = LV_Options.Items.ElementAt(3);
This line is incorrect. It will not return you a TextBlock. I don't know what a .BackColor is, and it should not compile. The Items property in a ListView will return you a list of ListViewItems. If you want to access the inside element from a ListViewItem, you'll need to access the ContentTemplateRoot property.
Do not use var ever. It lets you assume that you know the type, whereas if you explicitly typed the declaration you would realize you're doing it wrong.
MyTBL wrd = lv1.SelectedItem as MyTBL;
if (wrd == null)
return;
TextBlock tb = lv1.FindName("TB_AMean1") as TextBlock;
What is a MyTBL type? FindName is only available to framework DependencyObjects so I'm assuming it's a user control? You have to provide a lot more code to show us what you're doing and what you're setting the ListView's ItemsSource and ItemTemplate with and what these errors are and how you have 2 breaking debug errors at once and what the error messages are.
Comprehending runtime error messages is a huge part of being a good developer.

Preselect Value of Treeview inside combobox

I am looking to implement a treeview inside a combobox. Basically I want it to show as a combobox when collapsed but a treeview inside combo box when expanded. When a user clicks on a node, I want it to show in the collapsed combo box. I have got this working so far.
The problem I am having is that how do I show a default value from c# when this combo box is loaded. Please help guys as I am running out of ideas :)
Thanks in advance.
Data Template
<DataTemplate x:Key="TreeViewExpanded" >
<StackPanel>
<TreeView x:Name="DPointTree" Margin="5" ItemsSource="{Binding Datapoint}"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ComboBox}}}"
>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Field}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding = "{Binding Path=SelectedFieldName, Mode=TwoWay}" Value = "">
<Setter Property = "Visibility" Value = "Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
Here is the Xaml
<ComboBox Name="cmbFieldName" Width="150" Background="White" ItemsSource="{Binding}" SelectedItem="{Binding SelectedFieldName , Mode=TwoWay}" >
<ComboBox.ItemTemplateSelector>
<local:TreeViewSelector/>
</ComboBox.ItemTemplateSelector>
</ComboBox>
Here is the DataSet passed to this.
Datapoint _datapoint2 = new Datapoint();
_datapoint2.Name = "Alpha";
_datapoint2.FieldID.Add("Contains Elements");
_datapoint2.Field.Add("1 Year");
_datapoint2.Field.Add("2 Year");
_datapoint2.Field.Add("3 Year");
Try this:
private void TreeView_Loaded(object sender, RoutedEventArgs e)
{
TreeView areaTreeView = sender as TreeView;
// Expand the treeview
if (areaTreeView != null)
{
ExpandCollapseTreeNodes(areaTreeView, true);
}
}
public static void ExpandCollapseTreeNodes(ItemsControl parentContainer, bool isExpanded)
{
foreach (Object item in parentContainer.Items)
{
TreeViewItem currentContainer = parentContainer.ItemContainerGenerator.ContainerFromItem(item) as TreeViewItem;
if (currentContainer != null) // && currentContainer.Items.Count > 0
{
//expand the item
currentContainer.IsExpanded = isExpanded;
//if the item's children are not generated, they must be expanded
if (isExpanded && currentContainer.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
{
//store the event handler in a variable so we can remove it (in the handler itself)
EventHandler eh = null;
eh = new EventHandler(delegate
{
//once the children have been generated, expand those children's children then remove the event handler
if (currentContainer.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
ExpandCollapseTreeNodes(currentContainer, isExpanded);
currentContainer.ItemContainerGenerator.StatusChanged -= eh;
}
});
currentContainer.ItemContainerGenerator.StatusChanged += eh;
}
//otherwise the children have already been generated, so we can now expand those children
else
{
ExpandCollapseTreeNodes(currentContainer, isExpanded);
}
}
}
}
it is not pretty but works to select a node in the tree
// in code behind after the tree is declared, e.g. after InitializeComponent();
SynchronizationContext.Current.Post(delegate
{
// expand the tree to the selected node after loading
treeView.ExpandAll(); // or if path is known treeView.ExpandPath(...);
SynchronizationContext.Current.Post(delegate
{
treeView.GetContainerFromItem(root.Children[0]).IsSelected = true;
}, null);
}, null);

WPF How to access control from DataTemplate

I have a datatemplate which contains a grid and inside the grid I have a combobox.
<DataTemplate x:Key="ShowAsExpanded">
<Grid>
<ComboBox Name ="myCombo" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="5"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource MyItems}">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
</Grid>
</DataTemplate>
I then I have a grid that refers to that template through styling.
<Grid>
<ContentPresenter Name="_contentPresenter" Style="{DynamicResource StyleWithCollapse}" Content="{Binding}" />
</Grid>
How can I access through code behing the myCombo to basically set its DataContext?
Three ways which I know of.
1.Use FindName
ComboBox myCombo =
_contentPresenter.ContentTemplate.FindName("myCombo",
_contentPresenter) as ComboBox;
2.Add the Loaded event to the ComboBox and access it from there
<ComboBox Name ="myCombo" Loaded="myCombo_Loaded" ...
private void myCombo_Loaded(object sender, RoutedEventArgs e)
{
ComboBox myCombo = sender as ComboBox;
// Do things..
}
3.Find it in the Visual Tree
private void SomeMethod()
{
ComboBox myCombo = GetVisualChild<ComboBox>(_contentPresenter);
}
private T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
First of all, I can't even find the relation between the Resource (ShowAsExpanded) and the usage inside the ContentPresenter. But for the moment, let's assume that the DynamicResource should point to ShowAsExpanded.
You can't and shouldn't access the combobox via code. You should bind the datacontext to the grid that uses the style. If you don't want to do that, you will have to find the content at runtime and search for the child combobox.
you need to use FindName. check out http://msdn.microsoft.com/en-us/library/system.windows.frameworktemplate.findname.aspx

Categories