Collection View Source Grouping in Tree View - Binding Error - c#

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.

Related

Treeview "Items collection must be empty before using ItemsSource."

If I use the following code without <DataTemplate> it is working and I can see only the SoftwareVersion, when I add <DataTemple> throws an exception. Any idea why?
ObservableCollection<Note> AllNotes
<TreeView Name="tree" ItemsSource="{Binding Path=AllNotes}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type m:Note}" ItemsSource="{Binding Path=ListIssuesType}">
<TextBlock Text="{Binding Path=SoftwareVersion}" Margin="2" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<DataTemplate DataType="{x:Type m:IssueType}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=IssueTypeName}" />
</StackPanel>
</DataTemplate>
</TreeView>
It is not clear at all why you are adding a DataTemplate to your control. But it is clear why you are getting the exception: you have added the DataTemplate object as a direct child of the TreeView, which makes it an item in the Items collection. It's "template" nature becomes completely irrelevant; you are telling the control to just include that object as an element in the view.
And of course, now that the Items collection is non-empty, you are not allowed to send the ItemsSource property.
So the fix for the exception is "don't do that". I.e. stop trying to make the DataTemplate element an item in the tree view's list of items.
Beyond that, there's not enough context in your question to say how you should be using the DataTemplate object. Presumably you intend it to be used as part of the template for an actual data item in the tree view; maybe it needs to be incorporated into the HierarchicalDataTemplate somehow.
But without more specifics regarding exactly what the code does now and what you want it to do instead, as well as you providing a good Minimal, Complete, and Verifiable example clearly illustrating your scenario, I don't see a good way to address that part of your problem. If you still need help with defining the data template, please post a new question that provides the necessary details.

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)

TextBlock Text-Binding from ListView items

How can I change the Text of a TextBlock when the selection in my ListView changes?
I don't want do this manually...
All Items of the ListView are LogEntry's (class)... Can I use Binding in the Text-Attribute of the TextBlock to get a specific property of the selected Item?
Yes, in fact there are multiple solutions, i give you the most "WPF" like answer, but imo also the least flexible.
First you need to set the IsSynchronizedWithCurrentItem="True" property
Now if you select an item, the bound CollectionView will set the item as the CurrentItem.
Now your TextBox/Block can bind to this specific item via a special binding syntax using a '/'.
For Example:
<TextBlock Text="{Binding LogEntries/}"/>
of course you can get a specific property from the current item via binding aswell
<TextBlock Text="{Binding LogEntries/WarningMessage}"/>
Hope that helps.
assuming you have a listview like this:
<ListView ItemSource="{Binding LogEntries}" Name="logs" IsSynchronizedWithCurrentItem="True">
</ListView>
<ContentControl Content="{Binding ElementName=logs, Path=SelectedItem}" ContentTemplate="{StaticResource logTemplate}"/>
Now you need to provide that logTemplate in the Resources.
<UserControl.Resources>
<DataTemplate DataType="{x:Type local:LogEntry}">
<TextBlock Text="{Binding Path=LogText}"/> <-- This is a Property-Binding of your custom class
</DataTemplate>
</UserControl.Resources>
The last thing missing is to provide the namespace to your local class LogEntry. If you use an awesome tool like Resharper, it will insert the namespace for you. Otherwise, here a sample declaration:
<UserControl xmlns:local="clr-namespace:My.App.Namespace.LogEntry;assembly=My.App"
... (rest of namespace declarations)

Why the need for Binding in this context

i am starting WPF, looking at How do o: Getting started with Entity Framework
i am abit confused why the need for
<ListBox Name="ListBox1" ItemsSource="{Binding Source={StaticResource CustomerSource}}" >
why cant i do
<ListBox Name="ListBox1" ItemsSource="{StaticResource CustomerSource}" >
how do i know when i need Binding. because on 1st thought, just like i use a static resource in Styles
<Button Style="{StaticResource someStyle}"
why not
<Button Style="{Binding Source={StaticResource someStyle}}"
This example assigns a value retrieved from the resources using the specified key to the Text property:
<TextBox Text="{StaticResource SomeText}" />
This examples binds the Text property to a property on an object retrieved from the resources using the specified key:
<TextBox Text="{Binding Source={StaticResource SomeObject}, Path=SomeProperty}" />
The Binding class is used for data binding that is a way to surface data retrieved from a data source on the GUI, allowing users to interact with it. Without data binding values are simply assigned to the controls on the UI.
Bindings add a layer of abstraction between the UI controls and the underlying data source associated with it, providing a bounce of services. Here are some of the most important ones:
Automatic propagation of changes in the data between the UI and the data source in either or both directions
Conversion/formatting of values
Notification through events
Related resources:
Data Binding Overview
Binding Class
You could also set the DataContext on the parent control instead.
If the ListBox is contained in for example a StackPanel for example.
<StackPanel x:Name="parentControl" DatContext="{StaticResource CustomerSource}">
<ListBox x:Name="ListBox1" ItemSource="{Binding}">
...
</ListBox>
</StackPanel>

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