C# TreeView and binding to a database - c#

I'm querying a database via a stored procedure and getting data back. which I'm populating into an object as such:
private GetMiscVars _batchVariablesData;
public GetMiscVars BatchVariablesData
{
get { return _batchVariablesData; }
set
{
if (_batchVariablesData == value) return;
_batchVariablesData = value;
OnPropertyChanged("BatchVariablesData");
}
}
In my XAML I have this:
<TreeView>
<TreeViewItem Header="Batch Data" ItemsSource="{Binding BatchVariablesData}">
<TreeViewItem.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding VarDesc}" />
</DataTemplate>
</TreeViewItem.HeaderTemplate>
</TreeViewItem>
</TreeView>
What I am seeing when my code runs is my parent node is blank instead of saying "Batch Data" like I hoped and my child nodes return the name of the ObservableCollection used in calling the database, in this DgTabData.
So, where am I screwing things up?
Thanks
To clarify a little bit more, I have a stored procedure that returns data that looks like this:
VarId EventId VarDesc
11 2489 WP1.WC01 Quantity
1196 2489 WP1.WC01 Brew Number
1197 2489 WP1.WC01 Operator Name
1216 2489 WP1.WC01 Integer Test
1217 2489 WP1.WC01 Logical Test
1218 2489 WP1.WC01 Float-4 Test
1219 2489 WP1.WC01 Float-3 Test
I want to take the VarDesc field and place it in a TreeView under a heading of Batch Data. Once I can figure this out I want to be able to grab the VarId and EventId values from the item selected in the TreeView as I will use those to populate a datagrid.

Firstly, a TreeView displays hierarchical data. That means that your data must have a public collection property to use for the child nodes. Next, to display hierarchical data, you need to declare a HierarchicalDataTemplate, rather than trying to define the controls as you were. Try this XAML instead:
<TreeView ItemsSource="{Binding CollectionOfParents}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type YourPrefix.YourClassName}"
ItemsSource="{Binding CollectionOfChildrenPropertyInParent}">
<TextBlock Text="{Binding PropertyInParent}" /><!-- Parent DataTemplate -->
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate> <!-- Child DataTemplate -->
<TextBlock Text="{Binding PropertyInChild}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
UPDATE >>>
As I said, a TreeView displays hierarchical data. That means that your data must have a public collection property to use for the child nodes. So create a class, let's say named Data, with a property to hold the Batch Data text, let's say named Name, and an ObservableCollection<string> property, let's say named Items to hold the VarDesc column values.
Then you need to populate the collection with the items returned from the stored procedure in any way you see fit. Finally, create an ObservableCollection<Data> property, let's say named DataItems in the code behind or view model and add your Data class, complete with collection items. Then you could bind to the TreeView like this:
<TreeView ItemsSource="{Binding DataItems}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type YourXamlNamespacePrefix.Data}"
ItemsSource="{Binding Items}">
<TextBlock Text="{Binding Name}" /> <!-- Parent DataTemplate -->
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate> <!-- Child DataTemplate -->
<TextBlock Text="{Binding}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>

Related

WPF TreeView data binding to custom objects

I am having trouble understanding how to Bind Data to a WPF TreeView when custom objects are used. I have researched and watched tutorials but I am still stuck.
BACKGROUND: consider three classes (I have simplified my problem to this). They represent a database which has a Table, each table can have Fields. There are a list of Tables.
1) TableList Class
With Property
List<Table Objects>
2) Table Class:
Property Name
With a TableFields Property
SortedDictionary <Name, Field Object>
3) Field Class:
With a Name Property
An example of my current attempt to bind a field (lowest level) to Table.TableFields.Key
<DataTemplate x:Key="fieldTemplate">
<TextBlock Text="{Binding Path=Table.TableFields.Key}"/>
</DataTemplate>
DESIRED OUTPUT - a hierarchical view of the table list, containing tables and their fields.
Table 1
Field 1
Field 2
Field 3
Table 2
Field 1
Field 2
Field 3
Table N
Field N
I am after guidance so I can better understand how to bind this data, an example of my issue is the data binding has to look at a Table Objects TableFields property which is a SortedDictionary, in which I want to get the Key which will be a Field Name.
I am confused with how to bind custom objects and access the information like this.
This will display your TreeView
<TreeView ItemsSource="{Binding TableList}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource = "{Binding Path=TableFields}">
<TextBlock Text="{Binding Path=Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Key}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>

Binding a List within a bound ObservableCollection

In my WPF application I have an Observablecollection "CollOfPersons" of Persons, where each Person Object has a property "NotesOnPerson" of type
List<Notes>
(among other properties). Now I bind "CollOfPersons" to a listbox lb in code via
lb.ItemsSource = CollOfPersons;
Now I have set up a template how to display a person, namely I wrap each person in a 'Expander' and display the basic properties (e.g., Name, Age) in Expander.header, and this works fine, e.g.,
<Expander.Header>
<StackPanel Orientation="Horizontal">
...
<TextBlock Text="{Binding Path=Name}"/>
...
</StackPanel>
</Expander.Header>
However, now I'd like to bind the NotesOnPerson list of notes to the Expander.Content. But since this is again a list of varying size I don't know how to do it. Same strategy as above does not work, because I don't know the name of the Expander (as I knew the name 'lb' of the big listbox in which all the stuff is). Something like
<Expander.Content>
<ListBox ItemTemplate="{StaticResource NoteTemplate}"
ItemsSource="{Binding Path=NotesOnPerson}"/>
</Expander.Content>
doesn't seem to work. I seem to be confused about code and XAML binding. How should I solve this?
Is this what you're looking for?
<Expander.Content>
<ListBox ItemTemplate="{StaticResource NoteTemplate}"
ItemsSource="{Binding NotesOnPerson}"/>
</Expander.Content>
I'm not familiar with the Expander, but since NotesOnPerson is (presumably) a property of Person and not of Name, that's the syntax you should use. (the Path= is optional, since just putting it in like that is another way to declare the Path)

Collection View Source Grouping in Tree View - Binding Error

I have a TreeView databound to a CollectionViewSource Groups collection. This is so that I can display the data using the power of the CollectionViewSource, and the data itself also has a hierarchical structure, which is why I need the TreeView. I have a second control which is bound to the SelectedItem of the TreeView.
The problem is that when the group header is selected, the program crashes with the following exception.
{"A TwoWay or OneWayToSource binding cannot work on the read-only property 'Name' of type 'MS.Internal.Data.CollectionViewGroupInternal'."}
The objects in my TreeView contain a Name property that is two way bound in another control. The binding engine seems to find the Name property for the Group and attempt to bind to that. How can I prevent this exception from occuring? I would like for the rest of my program to treat it as if nothing is selected when the group header is selected, or disallow selecting the group header all together. Below is a simplified version of the code.
<TreeView
x:Name="CustomersTree"
ItemsSource="{Binding CustomersViewSource.Groups}"
ItemTemplate="{StaticResource CustomerGroupsTemplate}">
<MyUserControl DataContext="{Binding ElementName=CustomersTree, Path=SelectedItem, Mode=OneWay}" />
<HierarchicalDataTemplate x:Key="CustomerGroupsTemplate" ItemsSource="{Binding Path=Items}" ItemTemplate="{StaticResource CustomerTreeItemTemplate}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="CustomerTreeItemTemplate" ItemsSource="{Binding Customers}">
<StackPanel>
<Image Source="{Binding ImageSource}" />
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</HierarchicalDataTemplate>
To be clear, the error is a result of the binding in the CustomerGroupsTemplate as far as I can tell, and changing this binding to OneWay results in the same error. The information in the tree shows up the way it is expected to, it is only when a group header is selected that the exception occurs.
The problem was the result of a two way binding within the user control. I ended up using a converter to check for the type of the object being bound, ignoring it if it wasn't the object I wanted.

WPF Databinding Treeview to List does not update

I have a List bound to a TreeView like:
XAML:
<TreeView Name="browserTree"
BorderBrush="DarkSlateGray"
BorderThickness="1"
Grid.Row="2"
Margin="0,3,0,0"
ItemsSource="{Binding UpdateSourceTrigger=PropertyChanged}">
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
C#:
browserTree.DataContext = treeList;
I update the list via:
void QueryChange(string s)
{
rCM.SetCommand(s);
treeList.Clear();
SqlDataReader sr = rCM.ExecuteReader(System.Data.CommandBehavior.Default);
while (sr.Read())
{
treeList.Add((string)sr["tree_hdr"]);
}
sr.Close();
}
The List<string> is just a placeholder at the moment for a more meaningful data class I have yet to implement. But right now I need to know why the TreeView is not updating to reflect the changes made to the list.
Try making the treelist an ObservableCollection.
Please check the type of your treeList which you set as DataContext. It has to be an ObservableCollection to reflect your collection changes in the UI
Or else for quick workaround, just set the DataContext again after you filled the List.

How to create a template to display data from a class in WPF

I have a data layer which is returning lists of classes containing data. I want to display this data in my form in WPF. The data is just properties on the class such as Class.ID, Class.Name, Class.Description (for the sake of example)
How can i create a custom control or template an existing control so that it can be given one of these classes and display its data in a data-bound fashion.
Thanks :)
You could use a ListBox and set its ItemsSource property to the list containing your data items. Then you define a DataTemplate for your type like this:
<DataTemplate x:Key="MyDataTemplate" DataType="{x:Type MyType}">
<StackPanel>
<TextBlock Text="{Binding ID}"/>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Description}"/>
</StackPanel>
</DataTemplate>
...and tell the ListBox to use this DataTemplate by setting the ItemTemplate property.
It is also sufficient to just define the DataTemplate as above and give it no key. Then it will be used for all items which have the respective type.
BTW: You can find a more detailed example in MSDN on the page for the ItemTemplate property.

Categories

Resources