Im trying to make a custom treeview with an itemtemplate, so I can show the headertext + a type of the item in the treeview.
My inspiration comes from this answer;
https://stackoverflow.com/a/33119107/9156219
So, my problem is that I cant make the Itembindings work.
Here's my code;
XAML:
<TreeView x:Name="treeView" ItemsSource="{Binding treeList}" Grid.Column="0" IsVisibleChanged="treeView_IsVisibleChanged" SelectedItemChanged="treeView_SelectedItemChanged">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Type}" Margin="2,2,2,2" Background="LightBlue" FontSize='8'/>
<TextBlock Text="{Binding SystemName}" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
C#
public class CustomTreeViewItem : TreeViewItem
{
public String SystemName { get; set; }
public String Type { get; set; }
public String ParentItem { get; set; }
public String Path { get; set; }
}
public List<CustomTreeViewItem> treeList = new List<CustomTreeViewItem>();
public void SetRootNode()
{
int itmNumber = datSet.Rows.Count;
for (int i = 0; i < itmNumber; i++)
{
treeList.Add(new CustomTreeViewItem
{
SystemName = (string)datSet.Rows[i].ItemArray[1].ToString(),
Type = (string)datSet.Rows[i].ItemArray[2].ToString(),
ParentItem = (string)datSet.Rows[i].ItemArray[3].ToString(),
Path = (string)datSet.Rows[i].ItemArray[4].ToString(),
});
treeList[i].Header = treeList[i].SystemName;
}
foreach (CustomTreeViewItem item in treeList.Where(treeList => treeList.ParentItem == ""))
{
treeView.Items.Add(item);
}
foreach (CustomTreeViewItem item in treeList.Where(treeList => treeList.ParentItem != "").Where(treeList => treeList.Type != "Signal"))
{
var test = treeList.Find(treeList => treeList.SystemName == item.ParentItem);
test.Items.Add(item);
}
}
SetRootNode() is being called in the beginning of the program. datSet is being filled with a OleDBDataAdapter.
In the treeview, only the SystemName is being showed and not the type. What am I doing wrong?
Thank you in advance!
You are trying to create a TreeView whose TreeViewItems contain yet again TreeViewItems, if you remove the inheritance for your model, it works:
do this:
public class CustomTreeViewItem
instead of
public class CustomTreeViewItem : TreeViewItem
is this good enough for your needs?
Related
I'm working on a small WPF project,
for now it contains one window which should display as much checkboxes are many values in lists are.
For testing purposes, before I get values from database I tried something like this:
public class StatusOption
{
public string name { get; set; }
public bool IsSelected { get; set; }
}
public void GetSerialNumbers()
{
List<StatusOption> serialNumbers = new List<StatusOption>();
for(int i = 0; i<10;i++)
{
StatusOption x = new StatusOption();
x.name = "Random name" + i;
x.IsSelected = false;
serialNumbers.Add(x);
}
}
And my xaml looks like this:
<ListBox x:Name="SerialNumbersListBox"
AllowDrop="True"
Grid.ColumnSpan="2"
Grid.Row="2"
ItemsSource="{Binding GetSerialNumbers}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding serialNumbers}"
IsChecked="{Binding IsSelected}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But unfortunatelly nothing is displayed below textbox...
But for now everything is empty, and I can not find out why..
Thanks guys
Cheers
You could not bind a method. Please use property instead.
<ListBox HorizontalAlignment="Left" Height="171" Margin="334,96,0,0" VerticalAlignment="Top" Width="248" AllowDrop="True" x:Name="SerialNumbersListBox"
ItemsSource="{Binding SerialNumbers}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding name}"
IsChecked="{Binding IsSelected}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
public class SerialNumbersListBoxViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public class StatusOption
{
public string name { get; set; }
public bool IsSelected { get; set; }
}
private ObservableCollection<StatusOption> _SerialNumbers;
public ObservableCollection<StatusOption> SerialNumbers
{
get
{
return _SerialNumbers;
}
set
{
if (value != _SerialNumbers)
{
_SerialNumbers = value;
OnPropertyChanged(nameof(SerialNumbers));
}
}
}
public void GetSerialNumbers()
{
if (_SerialNumbers == null)
_SerialNumbers = new ObservableCollection<StatusOption>();
for (int i = 0; i < 10; i++)
{
StatusOption x = new StatusOption();
x.name = "Random name" + i;
x.IsSelected = false;
_SerialNumbers.Add(x);
}
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public SerialNumbersListBoxViewModel()
{
GetSerialNumbers();
}
}
You can refer this link for more details
Regard!
You cannot bind to methods, you can only bind to Properties or DependencyProperties.
So you need to create a Property for your serialNumbers. You should also implement INotifyPropertyChanged, so that the ListBox can know when your property changed.
public List<object> SerialNumbers
{
get => this._serialNumbersProperty;
set
{
if (!List<object>.Equals(value, this._serialNumbersProperty))
{
this._serialNumbersProperty = value;
OnPropertyChanged(nameof(this.SerialNumbers));
}
}
}
<ListBox x:Name="SerialNumbersListBox"
AllowDrop="True"
Grid.ColumnSpan="2"
Grid.Row="2"
ItemsSource="{Binding SerialNumbers}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding name}"
IsChecked="{Binding IsSelected}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have a ListBox consist of Datatemplate.
Here is my code:
<ListBox x:Name="MyListBox" SelectionChanged="MyListBox_SelectionChanged_1" SelectionMode="Single" IsItemClickEnabled="True">
<ListBox.ItemTemplate>
<DataTemplate>
<Rectangle Width="50" Height="45" Name="myRectangle" Fill="DodgerBlue" />
<Ellipse Grid.Column="0" Fill="LightGreen" Height="22" Width="22" />
<TextBlock Text="{Binding Initials}" x:Name="PersonInitials" />
<stackpanel>
<TextBlock TextWrapping="Wrap" x:Name="person_lbl" Text="{Binding SourceLink}" Foreground="DodgerBlue"/>
<TextBlock TextWrapping="Wrap" x:Name="detail_lbl" Text="{Binding ConversationId}" Foreground="DarkSlateGray"/>
</StackPanel>
<ToggleSwitch OffContent=" "/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Datatemplate consist of rectangle,Ellipse,3 textblocks and toggle switch which finally give an output like this as shown in image.
This is the template I will get.Please ignore the search bar.
Now Here is my Query:
I want to perform action on the basis of selection of a data_template in the listBox in which it should fetch the text of the textbox and display it somewhere.After searching about it on internet I have only found the examples like
<listbox>item1</listbox>
My ListBox does not consist of only one item it has many items that is making a template.
How can I fetch one textbox element on selection of whole template.Does anyone has idea how do so?
This is my code in c#:
class ReadJsonData
{
public List<Information> information { get; set; }
public ReadJsonData()
{
information = new List<Information>();
}
}
public class Information
{
public string SourceLink { get; set; }
public int ConversationId { get; set; }
public string Initials { get; set; }
}
and this is on MainPage.xaml
public MainPage()
{
this.InitializeComponent();
JsonReader();
}
public async void JsonReader()
{
Information infos = new Information();
infos.ConversationId = 1122;
infos.Initials = "LM";
infos.SourceLink = "abc";
Information info1 = new Information();
info1.ConversationId = 111;
info1.Initials = "MM";
info1.SourceLink = "pqr";
Information info2 = new Information();
info2.ConversationId = 1113;
info2.Initials = "NM";
info2.SourceLink = "vrl";
ReadJsonData rb = new ReadJsonData();
rb.information.Add(infos);
rb.information.Add(info1);
rb.information.Add(info2);
var rb1 = JsonConvert.SerializeObject(rb);
var rootobject = JsonConvert.DeserializeObject<ReadJsonData>(rb1);
foreach (var info in rootobject.information)
{
MyListBox.ItemsSource = rootobject.information;
}
private void MyListBox_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
var selectedItem = sender as Information;
string initials = selectedItem.Initials;
show_initial = initials;
}
}
Assuming you are setting the item source of the ListBox (MyListBox) to a list of items of your model class (List<SomeClassName>)..
If your model class is something like this :
public class SomeClassName
{
public string Initials { get; set; }
public string SourceLink { get; set; }
public string ConversationId { get; set; }
}
You can add this code to the OnSelectionChanged or Tapped event of your listbox to get the data members of the class:
private void MyListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedItem = sender as SomeClassName;
//Gives you the Initials of the selected list item
string intials = selectedItem.Initials;
}
Hope this helps..!
EDIT 1 :
First of all why are you iterating rootobject.information? you can simply use it as item source without the for loop..
foreach (var info in rootobject.information)
{
MyListBox.ItemsSource = rootobject.information;
}
Second, what does this line do ? show_initial = initials; is show_initial a variable ? what does it do ?
Edit 2 :
private void MyListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(MyListBox.SelectedItem != null)
{
var selectedItem = sender as Information;
//Gives you the Initials of the selected list item
string intials = selectedItem.Initials;
} else {
Debug.WriteLine("No item Selected");
}
}
Please check if the MyListBox.SelectedItem != null condition is satisfied or not.
I've been trying to get my first TreeView to work, at first without the ViewModel. But no matter what I do, it doesn't show any children, even though the binding is correct.
I'm using two Item templates, one for hierarchical and another for data. You can see the important parts for both below:
class GrupoTag
{
public string Nome { get; set; }
public GrupoTag Pai { get; set; }
public List<Tag> ListaFilhos { get; set; }
public List<GrupoTag> SubGrupos { get; set; }
public GrupoTag(string nome)
{
Nome = nome;
ListaFilhos = new List<Tag>();
SubGrupos = new List<GrupoTag>();
}
public List<object> Filhos
{
get
{
List<object> lista = new List<object>();
foreach (GrupoTag subGrupo in SubGrupos)
lista.Add(subGrupo);
foreach (Tag filho in ListaFilhos)
lista.Add(filho);
}
}
}
class Tag
{
public GrupoTag Pai { get; set; }
public string Nome { get; set; }
public Tag(string nome)
{
Nome = nome;
}
public Tag(GrupoTag pai, string nome)
{
Pai = pai;
Nome = nome;
}
}
The XAML binding to all of this:
<TreeView Name="MenuTags">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="xml2Excel2:GrupoTag" ItemsSource="{Binding Filhos}">
<TextBlock Text="{Binding Nome}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="xml2Excel2:Tag">
<TextBlock Text="{Binding Nome}" />
</DataTemplate>
</TreeView.Resources>
</TreeView>
But the property "Filhos" on the GrupoTag class is never accessed. I've tried putting a breakpoint in there, throwing an exception, but it's simply never called. And the TreeView only displays the names of the collection of GrupoTags I assigned to it as its ItemSource in code-behind.
MenuTags.ItemsSource = arvoreTeste.SubGrupos;
I've read all the related questions and corrected the code for the respective errors, but I'm still lost here.
EDIT 1: So I modified the code of the classes to conform to the simple interface below:
class ITag
{
public string Nome { get; set; }
public ObservableCollection<ITag> Filhos { get; set; }
}
As per Benin comment, GrupoTag now uses a single property stored in a ObservableCollection to represent its children. And, as per Adnan answer removed the DataType from the XAML TreeView. Now it looks like this:
<TreeView Name="ArvoreTags">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Nome}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
It works, the TreeViewis functional. But I don't know why.
You need to give your TreeView ItemSource and DataType. And I would agree to Berins comment , you should avoid making new list to each access to Filhos property. TreeView works very good with Polymorphis, I mean with data types.
<TreeView DockPanel.Dock="Top"
DataContext="{Binding ProjectNodes_DataContext}"
ItemsSource="{Binding Nodes}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:ProjectNode}" ItemsSource="{Binding SubItems}">
<StackPanel>
<TextBlock Text="{Binding Header}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.Resources>
My Property is:
private ProjectNodesVM mProjectNodes_DataContext;
public ProjectNodesVM ProjectNodes_DataContext
{
get { return mProjectNodes_DataContext; }
protected set
{
SetProperty(ref mProjectNodes_DataContext, value);
}
}
and class ProjectNodesVM has:
public ObservableCollection<ProjectNode> Nodes {
get { return mNodes; }
protected set { SetProperty(ref mNodes, value); }
}
inside ProjectNode class i have:
private string mHeader;
public string Header
{
get { return mHeader; }
set { SetProperty(ref mHeader, value); }
}
I found a simple answer to my specific case, not yours it seems
The children must be a "property".
So:
public ObservableCollection<Tag> ListaFilhos ; //doesn't work
public ObservableCollection<Tag> ListaFilhos {get; set;} //works
I have the following task:
create Tree which user can modify through app UI - add new Items, delete existing one. TreeView control should be binded to appropriate List in code behind.
Items in tree are CriteriaItem objects.
public class Subcriteria
{
public Subcriteria(string header)
{
Title = header;
subcriterias = new ObservableCollection<Subcriteria>();
}
public string Title { get; set; }
public ObservableCollection<Subcriteria> subcriterias { get; set; }
}
public class Criteria
{
public Criteria(string header)
{
Title = header;
criterias = new ObservableCollection<Subcriteria>();
}
public string Title { get; set; }
public ObservableCollection<Subcriteria> criterias { get; set; }
}
public MainWindow()
{
InitializeComponent();
public ObservableCollection<Alternative> _alt = new ObservableCollection<Alternative>();
Criteria root = new Criteria("root");
criteriaBundle.Add(root);
trvMenu.DataContext = _alt;
}
XAML:
<TreeView Name="trvMenu" Grid.Row="2" ItemsSource="{Binding criteriaBundle}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding criterias}">
<TextBlock Text="{Binding Title}" />
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding subcriterias}">
<TextBlock Text="{Binding Title}" />
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Title}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
But it doesn't work. Could you please assist me with binding?
You should change your code-behind like this:
1) You should set DataContext, if you use binding
2) You can use only Properties in binding, not fields
My personal advice that You should read about binding basic and MVVM
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
criteriaBundle = new ObservableCollection<CriteriaItem> {new CriteriaItem("root")};
}
public ObservableCollection<CriteriaItem> criteriaBundle { get; set; }
}
EDIT:
<FlipView Name="flipView"
AutomationProperties.AutomationId="ItemsFlipView"
AutomationProperties.Name="Item Details"
TabIndex="1"
Grid.Row="1"
ItemsSource="{Binding}" Style="{StaticResource FlipViewStyle1}">
<TextBlock Name="answerT" Text="{Binding question}"/>
<FlipView.ItemContainerStyle>
<Style TargetType="FlipViewItem">
<Setter Property="Margin" Value="0,137,0,0"/>
</Style>
</FlipView.ItemContainerStyle>
<FlipView.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="flipTxt" Text="{Binding question}"/>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
I have the above FlipView in XAML defined. I want to get the information that i have in the "flipTxt" TextBlock in a string in C#.
Tried with VisualTreeHelper but i can't seem to understand exactly how it works.
As well, tried to create another textblock (answerT) that would read the same info and get the text from that one. Didn't work either.
Thanks
LE:
This is how i did the binding, i get the data from MobileService.
private IMobileServiceTable<myObj> obj_tb = App.MobileService.GetTable<myObj>();
private ObservableCollection<myObj> obj_it;
var res= await obj_tb.ToListAsync();
obj_it = new ObservableCollection<myObj(res);
flipView.ItemsSource = obj_it;
Providing your FlipView is binding to its collection correctly you can access the SelectedItem in code on different events eg SelectionChanged event
private void flipView_SelectionChanged(object sender, Windows.UI.Xaml.Controls.SelectionChangedEventArgs e)
{
if (flipView != null)
{
var item = flipView.SelectedItem as MyObj;
string question = item.Question;
string answer = item.Answer;
}
}
Possible MyObj
public class MyObj
{
public string Question { get; set; }
public string Answer { get; set; }
}
or as youre using MobileServices you would probably have this if using Json.NET
public class MyObj
{
[JsonProperty(PropertyName = "Question")
public string Question { get; set; }
[JsonProperty(PropertyName = "Answer")]
public string Answer { get; set; }
}