Sorry - my question is almost identical to this one but since it didn't receive a viable answer, I am hoping that someone else has some fresh ideas.
I have a WPF TreeView that is bound to a hierarchy of a single type:
public class Entity
{
public string Title { get; set; }
public ObservableCollection<Entity> Children { get; set; }
}
The Entity class implements INotifyPropertyChanged, but I have omitted this code for clarity.
The TreeView is bound to an ObservableCollection<Entity> and each Entity instance exposes a set of contained Entity instances via its Children property:
<TreeView ItemsSource="{Binding Path=Entities}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Entity}" ItemsSource="{Binding Path=Children}">
<TextBlock Text="{Binding Path=Title}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
Initially the TreeView binds as expected and correctly displays a multi-level hierarchy. Also, when the membership of one of the Children collections is programmatically modified, the changes are correctly reflected in the TreeView.
However, changes to the membership of the root member level ObservableCollection<Entity> are not reflected in the TreeView.
Any suggestions would be appreciated.
Thanks,
Tim
My initial guess is that you have something like the following for the root node:
public ObservableCollection<Entity> Entities
{
get;
set;
}
Then, instead of doing something [good] like the following:
Entities.Clear();
foreach (var item in someSetOfItems)
Entities.Add(item);
You are doing something [bad] like this:
Entities = new ObservableCollection<Entity>(someSetOfItems);
You should be able to track down the issue by making the backing field of the Entities property readonly:
private readonly ObservableCollection<Entity> _entities
= new ObservableCollection<Entity>();
public ObservableCollection<Entity> Entities
{
get
{
return _entities;
}
}
Further explanation, long time for answer to come, but I believe that if you do the binding in XAML, and then in code assign a new object to the property you break the binding, so you would have to redo the binding in code for it to work. Hence the solution with the readonly backing field. If doing like that you will not be able to assign a new ObservableCollection and you won't break the binding by assigning a new object to the backing field.
Related
I will try to simplify as much as possible so:
I have following EF6 entity:
public class ParentObject
{
ICollection<ChildObject> Children {get; set ;}
}
I have ViewModel where is
ObservableCollection<ParentObject> ParentCollection { get; set; }
ParentObject SelectedParent { get; set; }
ChildObject SelectedChild { get; set; }
Also I have two ListViews
<ListView Name="lvParents" Margin="5,5,0,5" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Path=ParentCollection}" SelectedItem="{Binding Path=SelectedParent}">
<ListView Name="lvChildren" Margin="5,5,0,5" Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Path=SelectedParent.Children}" SelectedItem="{Binding Path=SelectedChild}">
All EF6 Entities have INotifyPropertyChanged implemented,
Also my ViewModel has INotifyPropertyChanged Implemented.
Everything is working well, if I'm changing selected item in lvParents. Children in lvChildren are changing.
Once I delete the children, I cannot refresh the Children:
(item is deleted from DB, but cannot refresh just lvChildren.
Here is method I tried to use:
_dataContext.ChildrenObjects.Remove(SelectedChild);
_dataContext.SaveChanges();
SelectedParent.Children.Remove(SelectedChild);
//here the SelectedParent does not contain the value I removed but no way how to refresh the ItemsSource of lvChildren.
NotifyPropertyChanged("SelectedChild");
NotifyPropertyChanged("SelectedParent.Children");
NotifyPropertyChanged("ParentCollection");
My question is, what is the correct way to bind depended (related) collections and refresh them after they are changed. Do I need overload the EF6 ICollection and create my own ObservableCollection ???
Is it enough?
My question is, what is the correct way to bind depended (related) collections and refresh them after they are changed?
The Children property of the ParentObject should return an ObservableCollection:
public class ParentObject
{
ObservableCollection<ChildObject> Children {get; set ;}
}
Then the ItemsSource will be automatically refreshed when you remove an item from the collection:
SelectedParent.Children.Remove(SelectedChild);
That would be the most "correct" way of implementing this according the MVVM design pattern.
Using and binding to auto-generated entity types "as-is" is rarely a good idea in WPF. You should bind to ObservableCollection<T> properties rather than ICollection<T> ones if you want to be able to dynamically modify your collections at runtime.
Whenever I used TreeView I always had just few nodes and each of them usually had less than 100 items. I never really needed any kind of ui virtualization for that but now for the first time I need it.
The problem appears when using ui virtualization with recycling mode the TreeView seems to expand items even though I never expanded them manually.
I googled the issue and as far I understood recycling mode of virtualization in TreeView the containers get reused.
So I assume that the cause might be applying already expanded reused container to an item which wasn't expanded before.
Here is a simple example:
https://github.com/devhedgehog/wpf/
For those who cannot download code for whatever reason here is basically what I have tried to do with the TreeView.
This is what I have in XAML.
<Grid>
<TreeView ItemsSource="{Binding}" VirtualizingStackPanel.IsVirtualizing="True" VirtualizingStackPanel.VirtualizationMode="Recycling">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Parts}">
<TextBlock Text="{Binding Name}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
And this is code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
IList<Car> list = new List<Car>();
for (int i = 0; i < 5000; i ++)
{
list.Add(new Car() { Name = "test1" + i });
}
foreach (var car in list)
{
car.Parts = new List<string>();
for (int i = 0; i < 500; i++)
{
car.Parts.Add("asdf" + i);
}
}
this.DataContext = list;
}
}
public class Car
{
public string Name
{
get;
set;
}
public List<string> Parts
{
get;
set;
}
}
I hope somebody can provide me a solution to this issue. Is this a known bug?
I am sorry in case its a duplicate. Futhermore I hope you guys tell me what I did wrong since this is my first post before you downgrade the question.
As you probably know, this problem can be solved easily by using standard recycling mode:
<TreeView VirtualizingStackPanel.VirtualizationMode="Standard" ...>
This shouldn't have too much of an impact on your TreeView's performance, as the tree will still be virtualized and a container will only be created for visible items. The benefits of the recycling mode only come into play when scrolling (when items are both being virtualized and realized), and usually the standard virtualization mode is good enough.
However, in case performance is really critical (or if you really want a solution for this while keeping the recycling mode, or if you're looking to do things the right way), you can use backing data and data binding to solve this problem.
The reason why this problem occurs in the first place is this:
Let's say you have a TreeViewItem which has its IsExpanded property set to true. When it's being recycled, i.e. its data is replaced, its IsExpanded property remains the same because it has no way to know whether it should be expanded or not, because that data is not available anywhere. The only place where it exists is the IsExpanded property of the TreeViewItem, and it's not going to be relevant because that item is being reused along with its properties.
If however you have a viewmodel for each tree item you'll be able to bind each TreeViewItem to the IsExpanded property in your TreeViewItemViewModel (you will have a view model for each tree item) and you will always get the correct value because you've made that data available and bound each item to it.
Your TreeView's ItemsSource will be bound to a collection of TreeViewItemViewModel objects, and your TreeViewItemViewModel class will look something like this:
class TreeViewItemViewModel : INotifyPropertyChanged
{
bool IsExpanded { get; set; }
bool IsSelected { get; set; }
TreeViewItemViewModel Parent { get; }
ObservableCollection<TreeViewItemViewModel> Children { get; }
}
You can find more information on how exactly to create such view model in Josh Smith's excellent article Simplifying the WPF TreeView by Using the ViewModel Pattern.
I want to store Name Value pairs in my class. I tried the following and end up with following issues;
Use NameValeCollection : This was not success. Because I want to bind this Collection to a DataTemplate as ItemsSource. I was success in binding Name but not for the Value in the collection.
Template and XAML
<DataTemplate x:Key="MyCollectionTemplate">
<Grid>
<TextBlock Text="{Binding Mode=OneWay}"/>
<TextBox Name="CValue" Text="{Binding Path=Value}"/> //did not work this binding
</Grid>
</DataTemplate>
<ItemsControl ItemsSource="{Binding MyCollection}" x:Name="MyCollectionControl" ItemTemplate="{DynamicResource MyCollectionTemplate}" />
MyCollection is the NameValueCollection.
Use List<KeyValuePair<string,string>> : In this case I was success in binding key value pair. But unfortunately that supports only for OneWay Binding. So I cannot use that as well.
Please suggest me a way to handle this. I cannot use Dictionary as well. Because in the Name Value Pair Key is not unique.
Why dont you just create your own custom class? i.e.
public class MyClass
{
public string Key { get; set; }
public string Value { get; set; }
}
Then just have a list, or observable collection of that class.
ObservableCollection<MyClass> MyCollection
{
get;
set;
}
Note that if your collections values are likely to change, you will need to implement INotifyProperyChanged in MyClass.
For the collection, i would suggest using an ObservableCollection<T>.
The Name/Value pair sounds simple, so if you want change notifications on both name and value of that pair, you could implement a class with those two properties and change notification (e.g. via INotifyPropertyChanged) yourself and use that in your collection.
You can find an example on how to implement INotifyPropertyChanged on your class here:
How to: Implement Property Change Notification
If you do both of these, you will get change notifications on the addition and removal of name/value pairs and TwoWay binding on name/value pairs.
You can use the class Tuple.
Like
Tuple<string,string> t = Tuple.Create("key", "value");
string key = t.Item1;
string value = t.Item2;
I have an application which contains a hierarchy of View/Viewmodels.
ViewModelBase contains two ViewModels
private AViewModel _aViewModel = new AViewModel();
private BViewModel _bViewModel = new AViewModel();
My XAML binds a DataControl to
private ViewModelBase _currentView {get; set;}
public ViewModelBase CurrentView
{
get
{
return _currentView;
}
set
{
_currentView = value;
RaisePropertyChanged("CurrentView");
}
}
And decides which view to display based on DataTemplates
<DataTemplate DataType="{x:Type vm:AViewModel}">
<vw:AView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:BViewModel}">
<vw:BView />
</DataTemplate>
All this works fine but I'm not sure how to persist data between View changes. Say for example that AViewModel contains a string called "Test" and has a two way binding in AView. By changing view using CurrentView = _bviewmodel then my data won't persist when I change back to _aviewmodel - What's the best way to make sure any data stays between view changes as opposed to creating a new blank viewmodel each time.
I have to get _currentView to _aViewModel and then back to _currentView
Should have posted my AView xaml - Inside it was
<UserControl.DataContext>
<vm:AViewModel></vm:AViewModel>
</UserControl.DataContext>
So it appears I was stupidly creating a new ViewModel inside the Xaml everytime I changed the view. Thanks to everyone for pointing me in the right direction. I deleted this from the Xaml and all is working fine now.
Data should be persistent. Make sure that you don't create new ViewModels every time you change the CurrentView.
I am binding my entities to an edit form in WPF. Within a DataTemplate, I want to be able to set the background color of the root container within a DataTemplate to show it has been changed and these changes have not yet been submitted to the database.
Here's a very simple sample that demonstrates what I'm talking about (forgive errors):
<Page ...>
<Page.DataContext>
<vm:MyPageViewModel /> <!-- Holds reference to the DataContext -->
</Page.DataContext>
<ItemsControl
ItemsSource = {Binding Items}>
<ItemsControl.Resources>
<DataTemplate
DataType="Lol.Models.Item"> <!-- Item is L2S entity -->
<!-- In real life, I use styles to set the background color -->
<TextBlock Text="{Binding IsDirty, StringFormat='Am I dirty? /{0/}'}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Page>
The example just prints out "Am I dirty? yes" or "Am I dirty? no", but you get the idea.
To do this, I'll need to add a public property to my Item (partial class, simple) that can determine if the entity is dirty or not. This is the tough bit.
public partial class Item
{
public bool IsDirty
{
get
{
throw new NotImplementedException("hurf durf");
}
}
}
Outside of the entity, it's pretty simple (as long as you have the DataContext the entity is attached to). Inside, not so much.
What are my options here?
Edit: I don't think there's one good solution here, so suggestions for workarounds are welcome.
(Okay, similar questions exist, but they are all about how to determine this from outside of the entity itself and use the DataContext the entity is attached to.)
If you are using the dbml generated classes, you should be able to implement a couple of partial methods like this:
public partial class SampleEntity
{
partial void OnCreated()
{
this.IsDirty = true;
}
partial void OnLoaded()
{
this.PropertyChanged += (s, e) => this.IsDirty = true;
this.IsDirty = false;
}
public bool IsDirty { get; private set; }
}