WPF Databinding Treeview to List does not update - c#

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.

Related

set Treeview HierarchicalDataTemplate via a property

This one is a bit complicated. I'm trying to create a usercontrol which has a treeview and a few other controls, to create a reusable control that will be useful for other implementations.
The problem that I'm having is that I cannot figure out how to insert a HierarchicalDataTemplate defined outside the control into the treeview that is inside the control.
Outside the control here is my WPF
<Grid>
<Grid.Resources>
<HierarchicalDataTemplate x:Key="HierarchicalDataTemplate" ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
</Grid.Resources>
<masterTreeUserControl:MasterTreeUserControl
HierarchicalDataTemplate="{StaticResource HierarchicalDataTemplate}"
ItemsSource="{Binding Path=SelectiveListViewModel.Items, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Path=SelectiveListViewModel.SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
EnableAdd="False"
ItemType="{x:Type viewmodels:LocationItem}"
/>
</Grid>
The MasterTreeUserControl has a HierarchicalDataTemplate DependencyProperty
HierarchicalDataTemplateProperty = DependencyProperty.Register("HierarchicalDataTemplate",typeof(HierarchicalDataTemplate),typeof(MasterTreeUserControl));
private static readonly DependencyProperty HierarchicalDataTemplateProperty;
public HierarchicalDataTemplate HierarchicalDataTemplate
{
get
{
return (HierarchicalDataTemplate)GetValue(HierarchicalDataTemplateProperty);
}
set
{
SetValue(HierarchicalDataTemplateProperty, value);
}
}
And so far the Treeview inside the control looks like this.
<TreeView Name="ItemListView"
Grid.Row="2"
Margin="0,5,0,0"
ItemsSource="{Binding Source={StaticResource ItemsCvs}}">
In other implementations of a treeview what I would normally do is HierarchicalDataTemplate in a fashion similar to this.
<TreeView.Resources>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="Hello"/>
</HierarchicalDataTemplate>
</TreeView.Resources>
However, as the HierarchicalDataTemplate is inside a dependency property, I need to somehow bind it into the Treeview. I've had a look a round the internet (and will continue doing so) but can't having found anything relevent.
How can you inject a dependency property containing the HierarchicalDataTemplate into the treeview?
Following should work:
Add PropertyChangedCallback to your HierarchicalDataTemplateProperty.
In the handler, add template to control resources:
private static void OnTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var This = d as MasterTreeUserControl;
var template = e.NewValue as HierarchicalDataTemplate;
if(template != null)
{
This.ItemListView.Resources[new DataTemplateKey(template.DataType)] = template;
}
}
The only issue is that your HierarchicalDataTemplate has to have DataType set to your templated type. You should enforce that in some way (validation etc..)

Windows Phone 8.1 - ListView binding issue mvvm light

I have a strange issue with my binding...
Currently I'm just trying to binding a list of objects in a list view
XAML
ExtendedListView is just an extension of the basic listview.
<refresh:ExtendedListView PullToRefreshRequested="listView_InfoRefresh" IsPullToRefreshEnabled="True" ItemsSource="{Binding MyList, Mode=OneWay}" >
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock FontSize="16">
<Run x:Uid="TheChallenge" />
<Run Text="{Binding Title}"/>
<Run x:Uid="ExpireChallenge" />
</TextBlock>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</refresh:ExtendedListView>
C#
private List<Challenge> myList;
public List<Challenge> MyList
{
get { return myList; }
set
{
if (myList!= value)
{
myList= value;
RaisePropertyChanged(() => MyList);
}
}
}
The list is retrieve from a Wep Api application.
When I put a breakpoint, the list is not empty ( currently I have 3 elements in my list ), and after the binding of MyList, I am able to see the items 2 sec before they dissapear....
If someone have an idea.
Using a List and setting it every time data changes is not a good idea. Just use an ObservableCollection, set it juts once in ctor and then only add and remove items.

Binding from one ListBox to another ListBox?

I am trying to bind a ListBox to another ListBox within the same window. The left hand sided Listbox has data in it that one can select. But I want a user to be able to click on the item(s) in the left hand listbox and those same item(s) would be displayed in the other listbox on the right hand side.
EDITED: Of course you can bind a UI property to another UI property (Dependency Property actually) using ElementName, but I recommend to bind the properties to one view model. See a simplified example below.
View model:
public ObservableCollection<ItemObject> Items { get; set; }
public ObservableCollection<ItemObject> SelectedItems { get; set; }
Left:
<ListBox ItemsSource="{Binding Items}" SelectedItems="{Binding SelectedItems}" />
(Note that there is no SelectedItems dependency property actually. See question like: Select multiple items from a DataGrid in an MVVM WPF project)
Right:
<ListBox ItemsSource="{Binding SelectedItems}" />
This works fine. Furthermore, with this approach, the list on the right hand can be customized with ease (eg order, filter, ... by using CollectionView).
private ICollectionView _collectionView;
private ICollectionView _CollectionView {
get { return _collectionView
?? (_collectionView = CollectionViewSource.GetDefaultView(SelectedItems)); }
}
public ICollectionView FilteredItems {
get { _CollecitionView.Filter(...); }
}
<ListBox ItemsSource={"Binding FilteredSelectedItems"} />
Such an MVVM approach is sometimes laborious, but eventually found as beneficial.
You name the first listbox, then any other control on the xaml will bind to that control using it's name in the ElementName attribute of the binding.
For example there are two listboxes and one text box. The top listbox has multiselections and those selection(s) are shown on the lower listbox. While the textbox only gets the first item selected.
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<converters:PathToFilenameConverter x:Key="FilenameConverter" />
<x:Array x:Key="FileNames" Type="system:String">
<system:String>C:\Temp\Alpha.txt</system:String>
<system:String>C:\Temp\Beta.txt</system:String>
<system:String>C:\Temp\Gamma.txt</system:String>
</x:Array>
</StackPanel.Resources>
<ListBox Name="lbFiles"
SelectionMode="Multiple"
ItemsSource="{StaticResource FileNames}"
Margin="10"/>
<ListBox ItemsSource="{Binding SelectedItems, ElementName=lbFiles }" Margin="10" />
<TextBlock Text="{Binding SelectedItem,
ElementName=lbFiles,
Converter={StaticResource FilenameConverter}}"
Margin="10" />
</StackPanel>
Note...the code is binding using the SelectedItems property for the lower list box and not SelectedItem used by the TextBlock.
As an aside, another answer has the use of an ObservableCollection, that is not needed unless the array is dynamically changing; otherwise any array can be used. Depending on loading, say from a VM, it may need to adheres to the INotifyPropertyChanged.

C# TreeView and binding to a database

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>

Adding custom control to listbox

I have a custom control ListItem. I need to display five such items in a window and these items could change during runtime; items could be added or deleted, or content could change in ListItem.
ListBox appears to be a good solution to display items. But what I have seen is we can add items and style them, and can handle updates with data trigger.
myListBox.Items.Add(new { FileName = "SomeFile", State="Uploaded" });
But we can not do something like
ListItem curItem = new ListItem();
myListBox.Items.Add(new { curItem });
Even if I do it shows empty item in the list.
So if I want to add my custom control to some listbox, how could that be possible. That is using ListBox just as a container so we can get away from the pain of positioning and all that after list changes. Or is there a better way to do that?
You are in luck - this is the bread and butter of WPF! Set the ItemsSource of your ListBox (possible in XAML or cs):
myListBox.ItemsSource = myEnumerableCollection;
or
<ListBox ItemsSource="{Binding MyItemsProperty}">
Use a DataTemplate (you do not need a UserControl) to style each item in XAML:
<ListBox ItemsSource="{Binding MyItemsProperty}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding FileName}"/>
<TextBlock Text="{Binding State}"/>
<!--Whatever you want-->
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If your collection is an ObservableCollection<T> changes to that collection (e.g. items added or removed) will be reflected in the ListBox automatically. If T implements INotifyPropertyChanged changes to properties on each item will also automatically show up on the UI.
For more see the WPF Binding Overview.
Don't create or manipulate UI elements in procedural code in WPF.
<ListBox ItemsSource="{Binding SomeCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<my:MyControl/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
where my:MyControl is a UserControl with whatever UI you want.

Categories

Resources