Copy object on Drag and Drop - c#

I'm having two ListBoxes, both having their ItemsSource bound to separate ObservableCollection<ICustomObject>. ICustomObject is an interface defining some base-properties for different types of CustomObject.
I want one ListBox to be the static, non changing source of possible elements from where the user can drag them to the other ListBox multiple times. Also elements in the target should be rearrangeable.
Result should be a toolbox from where the user can build a document composed of multiple CustomObject.
I'm using the GongSolutions.WPF.DragDrop library for this, commit c680fcf.
<ListBox ItemsSource="{Binding AvailableElements}" dd:DragDrop.IsDragSource="True" dd:DragDrop.DragDropCopyKeyState="ControlKey">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox ItemsSource="{Binding SelectedElements}" dd:DragDrop.IsDropTarget="True" dd:DragDrop.IsDragSource="True">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Having this I can copy elements from source to target by holding the ControlKey.
But there are two problems with this:
Is there any way to default to copy-action, so that no key needs to be pressed?
How can I do a real copy? Currently all list-elements of the target-list are pointing to the same object with the result that changing the properties of individual elements is not possible.
I already tried it with a custom DropHandler but this makes reordering the elements impossible:
public void Drop(IDropInfo dropInfo)
{
IFormElement data = Activator.CreateInstance(dropInfo.Data.GetType()) as IFormElement;
if (data != null)
{
SelectedElements.Add(data);
}
}
Any help and hints appreciated.

"but this makes reordering the elements impossible" Why? It shouldn't, unless you're doing something wrong. To do what you want, a custom handler is the way to go, so you should detail why it wasn't working.
The collection into which you are dropping objects should implement INotifyCollectionChanged in order for the UI to update when you add new items into it. If that isn't your issue, please edit your question to add details. Assuming that isn't the problem, I'll suggest an alternative, simpler and sometimes much better alternative.
I recently added drag/drop using this library to my application. I had to create a custom drop handler as the collection being dropped on contained relationships to, not instances of, the class being dragged.
Think of it like a relational database. You have two tables-- People and Pets. Pets can be owned by multiple people, and people can have multiple pets. In a database, you would have a many-to-many table.
People -> PeoplePets <- Pets
PeoplePets describes the relationship between people and their pets, and pets to their people.
In your design, you're literally cloning a pet. Which means you and your girlfriend have two dogs now, both with the same name and both with a strong desire to roll in the neighbor cat's poop. That's weird (the cloning part, not the poop part), although I'm sure many folks would be happy with this arrangement.
Instead of your collection holding clones of pets, have your collection hold objects which define the relationship.
public ObservableCollection<PeoplePets> Pets {get;} =
new ObservableCollection<PeoplePets>();
And so, to the point of your question, in your custom drop handler, when someone drops a pet on your listbox, simply create a new instance of PeoplePets, drop the pet in it, and add the relationship object to the collection. You don't have to worry about cloning anything, and you won't be adding new instances of the same thing (this can be extremely helpful, depending on what you're doing with the data down the road--detecting and merging dupes is a PITA).

Followed Wills answer and created my own drop handler based on the default drop handler. This way I could overwrite the default behavior to be always copy but not within the same list.
Looking at the default code I also found that its trying to clone the objects on copy, if they implement ICloneable. So I made them cloneable returning a new instance of them self.
Here are the relevant parts of the code (DropHandler-code mostly based on original DefaultDropHandler.cs):
View.xaml:
<ListBox ItemsSource="{Binding AvailableElements}" dd:DragDrop.IsDragSource="True">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox ItemsSource="{Binding SelectedElements}" dd:DragDrop.IsDropTarget="True" dd:DragDrop.IsDragSource="True" dd:DragDrop.DropHandler="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ViewModel.cs (must implement IDropTarget)
public void DragOver(IDropInfo dropInfo)
{
DragDrop.DefaultDropHandler.DragOver(dropInfo);
}
public void Drop(IDropInfo dropInfo)
{
if (dropInfo == null || dropInfo.DragInfo == null)
{
return;
}
var insertIndex = dropInfo.InsertIndex != dropInfo.UnfilteredInsertIndex ? dropInfo.UnfilteredInsertIndex : dropInfo.InsertIndex;
var destinationList = dropInfo.TargetCollection.TryGetList();
var data = ExtractData(dropInfo.Data);
// default to copy but not if source equals target
var copyData = (!Equals(dropInfo.DragInfo.SourceCollection, dropInfo.TargetCollection))
&& !(dropInfo.DragInfo.SourceItem is HeaderedContentControl)
&& !(dropInfo.DragInfo.SourceItem is HeaderedItemsControl)
&& !(dropInfo.DragInfo.SourceItem is ListBoxItem);
if (!copyData)
{
var sourceList = dropInfo.DragInfo.SourceCollection.TryGetList();
foreach (var o in data)
{
var index = sourceList.IndexOf(o);
if (index != -1)
{
sourceList.RemoveAt(index);
if (Equals(sourceList, destinationList) && index < insertIndex)
{
--insertIndex;
}
}
}
}
var tabControl = dropInfo.VisualTarget as TabControl;
// clone data but not if source equals target
var cloneData = !Equals(dropInfo.DragInfo.SourceCollection, dropInfo.TargetCollection);
foreach (var o in data)
{
var obj2Insert = o;
if (cloneData)
{
var cloneable = o as ICloneable;
if (cloneable != null)
{
obj2Insert = cloneable.Clone();
}
}
destinationList.Insert(insertIndex++, obj2Insert);
if (tabControl != null)
{
var container = tabControl.ItemContainerGenerator.ContainerFromItem(obj2Insert) as TabItem;
if (container != null)
{
container.ApplyTemplate();
}
tabControl.SetSelectedItem(obj2Insert);
}
}
}
public static IEnumerable ExtractData(object data)
{
if (data is IEnumerable && !(data is string))
{
return (IEnumerable)data;
}
else
{
return Enumerable.Repeat(data, 1);
}
}
#Will: thanks for your answer, it pointed me in the right direction. To help others I will answer my own question, but I upvoted your answer.

I can answer your second question. To do a real copy you can use the following class or just use the cloning part in your code:
public class GenericCloner<T> where T : class
{
public T Clone(T obj)
{
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
return (T)formatter.Deserialize(ms);
}
}
}

Related

WPF DataTemplateSelector is not used

I am trying to modify a existing WPF application in a way so that a newer version of some data object can be used alongside the old one. And so i avoid redundant code by extending the existing ViewModel with new fields where the old ones cannot be reused.
public IList<G1VU.PDR> VuG1 { get; set; }
public IList<G2VU.PDR> VuG2 { get; set; }
public PlacesCompound VuP
{
get
{
if (VuG1 != null && VuG2 == null)
{
return new PlacesCompound {
G1 = VuG1,
G2 = null
};
}
if (VuG2 != null && VuG1 == null)
{
return new PlacesCompound {
G1 = null,
G2 = VuG2
};
}
throw new Exception("G1 and G2 data present or no data present");
}
}
VuG1 has existed before and i have added a new property VuG2 for the new data. As you can see these are not the same class so i cannot interchange them. For that reason i've added a property that will return either of the two in a PlacesCompound class, which is just a class with two properties and nothing else.
In the corresponding usercontrol (lets call it ActivitiesView) we have a DataGrid which binds to the ViewModel and somewhere a TabItem that will display a custom UserControl places which binds to VuG1 on the ViewModel. I have copied it and changed it so it will work with VuG2 Data.
And i created a custom DataTemplateSelector which will decide what Template to use based on which variable of PlacesCompound isnt null.
In VUActivitiesResources.xaml i have then declared 2 DataTemplates one for each places UserControl and the DataTemplateSelector.
<activities:VUActivitiesViewDataTemplateSelector x:Key="PlacesTemplateSelector"/>
<DataTemplate x:Key="VuG2Template">
<places:VUPViewG2 DataContext="{Binding VuG2}" HorizontalAlignment="Left"/>
</DataTemplate>
<DataTemplate x:Key="VuG1Template">
<places:VUPViewG1 DataContext="{Binding VuG1}" HorizontalAlignment="Left"/>
</DataTemplate>
VUActivitiesResources.xaml is being referenced in ActivitiesView as UserControl.Resources.
In the ActivitiesView i placed a ItemsControl into the TabItem replacing the custom places UserControl (ive also tried a ListBox instead of a ItemsControl, but neither works)
<TabItem IsEnabled="{Binding PlacesIsVisible}">
...
<ItemsControl
ItemsSource="{Binding VuP}"
ItemTemplateSelector="{StaticResource PlacesTemplateSelector}"></ItemsControl>
</TabItem>
My question: why PlacesTemplateSelector is never used and how do i make it being used? Because right now while debugging i can see that in ViewModel VuP returns a PlacesCompound object correctly but the Selector is never entered. I want one of the two DataTemplates to show up in the TabItem and right now none is showing.
ItemsSource must be a collection (which your PlacesCompound is not), in your case this would be either VuG1 or VuG2. If the two item classes have no common base class, you can still use IEnumerable:
public IEnumerable VuP => (IEnumerable)VuG1 ?? VuG2;
Then, instead of writing a TemplateSelector, just let the WPF DataTemplating mechanism do its work:
<DataTemplate DataType="{x:Type namespace1:PDR}">
<places:VUPViewG2 HorizontalAlignment="Left"/>
</DataTemplate>
<DataTemplate DataType="{x:Type namespace2:PDR}">
<places:VUPViewG1 HorizontalAlignment="Left"/>
</DataTemplate>
<ItemsControl ItemsSource="{Binding VuP}" />

How to Update ComboBox ItemsSource Without Flickering

I am struggling with an update to a ComboBox that previously worked. I originally had its ItemsSource bound to a read-only ObservableCollection<char> property in the ViewModel. When the user instigates changes (which is done with mouse strokes, so dozens of times per second in some cases), the get rebuilds the collection from the Model and returns it.
When I changed to my own object in the ObservableCollection, the ComboBox started flickering during updates. I'm not sure what's going wrong. Here's the code that works, starting with the XAML:
<ComboBox ItemsSource='{Binding FromBins}' SelectedValue='{Binding SelectedFromBin, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}' />
ViewModel:
public ObservableCollection<char> FromBins
{
get
{
ObservableCollection<char> tempBins = new ObservableCollection<char>();
foreach (var item in Map.BinCounts)
{
tempBins.Add(item.Key);
}
return tempBins;
}
}
I simply raise a property change with every mouse movement and the interface works as expected (there is some other logic to ensure the SelectedItem is valid).
To make the interface more useful, I decided to add more information to the ComboBox, using my own class:
public class BinItem : IEquatable<BinItem>
{
public char Bin { get; set; }
public SolidColorBrush BinColor { get; set; }
public string BinColorToolTip { get {...} }
public BinItem( char bin )
{
Bin = bin;
BinColor = new SolidColorBrush(BinColors.GetBinColor(bin));
}
public bool Equals(BinItem other)
{
return other.Bin == Bin ? true : false;
}
}
If I swap char out for BinItem in the working code ViewModel I get flickering as the mouse is moved. Here is the updated XAML:
<ComboBox ItemsSource='{Binding FromBins}' SelectedValue='{Binding SelectedFromBin, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}'>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" ToolTip='{Binding BinColorToolTip}'>
<Rectangle Fill='{Binding BinColor}' Width='10' Height='10' HorizontalAlignment='Center' VerticalAlignment='Center' Margin='0,0,4,0' Stroke='#FF747474' />
<TextBlock Text="{Binding Bin}" Width='16' />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I have tried numerous things, including but not limited to:
-Using a List instead of the ObservableCollection but even though the Get fires every time and returns the correct collection of items, the interface does not always update (though the flickering disappears).
-Leaving all possible bins in the items source and adding a Visibility property to the BinItem class that I bound to (couldn't get it to update).
I suspect I am doing something fundamentally wrong, but no amount of searching SO or otherwise has helped thus far. Any help is appreciated.
I was able to solve this using the ideas from Clemens and Chris. Not sure if this is the most elegant solution, but it works as intended with no measurable performance hit.
Instead of replacing the collection with each refresh, I go through the logic of finding out what's changed (with each update there could be an addition AND a removal simultaneously). Code below:
private ObservableCollection<BinItem> _FromBins = new ObservableCollection<BinItem>();
public ObservableCollection<BinItem> FromBins
{
get
{
if (_FromBins.Count > 0)
{
List<char> BinsToRemove = new List<char>();
foreach (var item in _FromBins)
{
if (!Map.BinCounts.ContainsKey(item.Bin))
{
BinsToRemove.Add(item.Bin);
}
}
foreach (var item in BinsToRemove)
{
_FromBins.Remove(new BinItem(item));
}
}
foreach (var item in Map.BinCounts)
{
if (!_FromBins.Contains(new BinItem(item.Key)) && item.Value > 0) {
_FromBins.Add(new BinItem(item.Key));
}
}
return _FromBins;
}
}
Hope this can help someone else too.

WPF value converter making 1000s of database trips when it could be done in one

I have a list view that is bound to an Items collection in the view model behind. This works quite well, in that it shows the data correctly and groups it properly.
<!-- The list view for the shoeprint images -->
<ListView Grid.Row="0"
Margin="3,-3,-3,3"
Background="Transparent"
BorderThickness="0"
ItemsSource="{Binding Items"}
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectionMode="Single">
</ListView
/// <summary>
/// The collection that handles the grouping for the list view
/// </summary>
public ICollectionView Items
{
get { return _items; }
private set
{
_items = value;
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Items"));
}
}
private void UpdateDisplay()
{
var lst = new CollectionViewSource { Source = new ObservableCollection<ShoeprintImage>(concatList) };
lst.GroupDescriptions.Add(new PropertyGroupDescription("Type"));
/*OK UNTIL THIS LINE*/ Items = lst.View;
}
Up until the third line in Update display there are minimal database calls to generate the items collection and it is all quite speedy and efficient.
However each list view item consists of an image and a text block which holds a text string depending on certain properties of the item.
When I bind to the text block I use a value converter that takes the item, and checks these properties to see what string it should display.
The problem is that for every item it makes a round trip to the database to get its information. I would like to have it preload this information for every item before the converter as I believe it could be done in one SQL statement instead of 1000s of round trips.
could someone give me an idea of how to proceed? if you need more information I will post it but trying to keep it as short as possible as I hate reading long essay questions :-)
EDIT - Converter code
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//do nothing if value is null
if (value == null) return Binding.DoNothing;
//get the value
var image = (ShoeprintImage)value;
bool isPartofGroup = true;
//we only want the word Group to appear on the search results
if (image.Type == Enumerations.ImageType.GroupMembers || image.Type == Enumerations.ImageType.Shortlist) isPartofGroup = false;
else
{
var group = image.Item.ItemGroups.First().Group;
if (group.GroupID == 0) isPartofGroup = false;
else
{
if (group.ItemGroups.Count() <= 1) isPartofGroup = false;
}
}
//get the string
if (isPartofGroup) return Strings.GroupString;
else return string.Empty;
}
EDIT - binding (this is in the list view item template
<!-- put a stack panel so the image is above the text -->
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="White">
<!-- The image to display -->
<Image x:Name="MainImage"
Width="75"
Height="125"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MouseLeftButtonDown="Image_MouseLeftButtonDown"
MouseRightButtonDown="Image_MouseRightButtonDown"
Source="{Binding ImageFromFile}" />
<!-- Displays the word "Group" if the image is part of a group -->
<TextBlock HorizontalAlignment="Center"
FontStyle="Italic"
FontWeight="Bold"
Foreground="ForestGreen"
Padding="2"
Text="{Binding Converter={StaticResource GroupConverter},
Mode=OneWay}"
TextAlignment="Center" />
</StackPanel>

Can I access XAML elements in an array in the codebehind?

I've been looking around but I haven't been able to find anything on this. I am trying to get started making Windows 8.1 apps in C# with Visual Studio 2013 Pro. I want to be able to access multiple elements (particularly buttons or text blocks) in an array because this is more convenient for developing things like board games. For instance, if I were developing tic-tac-toe, I might use a series of buttons like this:
<Grid>
<Button Name="Cell00"/>
<Button Name="Cell01"/>
<Button Name="Cell02"/>
<Button Name="Cell10"/>
<Button Name="Cell11"/>
<Button Name="Cell12"/>
<Button Name="Cell20"/>
<Button Name="Cell21"/>
<Button Name="Cell22"/>
<Grid/>
Now for the function that would check for a win, I would have to check all possible combinations like this is in the code behind:
private bool CheckForWin()
{
if((Cell00 == Cell01) && (Cell01 == Cell02) && isNotBlank(Cell02)) return true;
if((Cell10 == Cell11) && (Cell11 == Cell12) && isNotBlank(Cell12)) return true
...
return false; //if none of the win conditions pass
}
This type of code would be extremely cumbersome. I would like to write it instead in a way that lets me check the array with for loops.
I realize that with tic-tac-toe, it is fairly easy to code it using brute force, but this was the first example that came to my head. Other games like Reversi or Go would not work well like this because of either the sheer size or the fact that pieces placed can change other cells than the one they were placed on.
Any help with this would be greatly appreciated.
This is not the correct way to use WPF. WPF is designed to use data binding....creating and manipulating UI elements directly is bad form. There are more posts/discussion/questions about this than you can imagine and I'll leave you to research them for yourself. In the mean time this is how you use WPF "properly":
First use NuGet to add MVVM lite to your project so that you get the ViewModelBase class and create a view model for a single cell:
public class Cell : ViewModelBase
{
private string _Text;
public string Text
{
get { return _Text; }
set { _Text = value; RaisePropertyChanged(() => this.Text); }
}
}
One level up you'll want a main model to encapsulate an array of these, this is where you will typically do all your game logic:
public class MainModel : ViewModelBase
{
private ObservableCollection<Cell> _Cells;
public ObservableCollection<Cell> Cells
{
get { return _Cells; }
set { _Cells = value; RaisePropertyChanged(() => this.Cells); }
}
public MainModel()
{
this.Cells = new ObservableCollection<Cell>(
Enumerable.Range(1, 100)
.Select(i => new Cell { Text = i.ToString() })
);
}
}
Notice that all I'm doing at the moment is creating a 100-element collection of cells. This main view model becomes the one that you assign to your window's data context:
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainModel();
}
Now your XAML controls need to bind to this data. ItemsControl is used to render a collection of elements so use one of those and bind it to your array. You want them displayed in a 2D grid so replace the ItemsPanelTemplate with a WrapPanel. Finally add a DataTemplate for your Cell class so that a button gets drawn for each cell:
<Window.Resources>
<DataTemplate DataType="{x:Type local:Cell}">
<Button Width="32" Height="32" Content="{Binding Text}"/>
</DataTemplate>
</Window.Resources>
<ItemsControl ItemsSource="{Binding Cells}" Width="320" Height="320" HorizontalAlignment="Center" VerticalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
That's how you use WPF. Your logic is stored entirely in the view model and it's completely decoupled from the view. Here's what this particular code displays, it should be pretty self-evident how flexible this code is and easy to change:
That's very possible. Simply declare an array variable :
private Button[] _buttonArray;
populate the array once, maybe in constructor :
_buttonArray = new[] {Cell00, Cell01, .... , Cell22};
And all of the buttons are now accessible through _buttonArray.

UI slow at updating ObservableCollection<T> in TreeView control

Scenario
I have a TreeView that is bound to ObservableCollection<T>. The collection gets modified every time the end-user modifies their filters. When users modify their filters a call to the database is made (takes 1-2ms tops) and the data returned gets parsed to create a hierarchy. I also have some XAML that ensures each TreeViewItem is expanded, which appears to be part of the problem. Keep in mind that I'm only modifying ~200 objects with a max node depth of 3. I would expect this to instant.
Problem
The problem is that whenever filters get modified and the TreeView hierarchy gets changed the UI hangs for ~1 second.
Here is the XAML responsible for create the TreeView hierarchy.
<TreeView VerticalAlignment="Top" ItemsSource="{Binding Hierarchy}" Width="240"
ScrollViewer.VerticalScrollBarVisibility="Auto" Grid.Row="1">
<TreeView.ItemTemplate>
<!-- Hierarchy template -->
<HierarchicalDataTemplate ItemsSource="{Binding Stations}">
<TextBlock Text="{Binding}" />
<!-- Station template -->
<HierarchicalDataTemplate.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Locates}">
<TextBlock Text="{Binding Name}" />
<!-- Locate template -->
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding TicketNo}" />
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And here is the code for updating the list.
public ObservableCollection<HierarchyViewModel> Hierarchy
{
get { return _hierarchy; }
set { _hierarchy = value; }
}
public void UpdateLocates(IList<string> filterIds)
{
_hierarchy.Clear();
// Returns 200 records max
var locates = _locateRepository.GetLocatesWithFilters(filterIds);
var dates = locates.Select(x => x.DueDate);
foreach (var date in dates)
{
var vm = new HierarchyViewModel
{
DueDate = date
};
var groups = locates.Where(x => x.DueDate.Date.Equals(date.Date)).GroupBy(x => x.SentTo);
// Logic ommited for brevity
_hierarchy.Add(vm);
}
}
I also have <Setter Property="IsExpanded" Value="True" /> as a style. I have tried using a BindingList<T> and disabling notifications, but that didn't help.
Any ideas as to why my UI hangs whenever changes are made to the ObservableCollection<T>?
Partial Solution
With what H.B. said and implementing a BackgroundWorker the update is much more fluid.
The problem is probably the foreach loop. Every time you add an object the CollectionChanged event is fired and the tree is rebuilt.
You do not want to use an ObservableCollection if all you do is clear the whole list and replace it with a new one, use a List and fire a PropertyChanged event once the data is fully loaded.
i.e. just bind to a property like this (requires implementation of INotifyPropertyChanged):
private IEnumerable<HierarchyViewModel> _hierarchy = null;
public IEnumerable<HierarchyViewModel> Hierarchy
{
get { return _hierarchy; }
set
{
if (_hierarchy != value)
{
_hierarchy = value;
NotifyPropertyChanged("Hierarchy");
}
}
}
If you set this property bindings will be notified. Here i use the IEnumerable interface so no-one tries to just add items to it (which would not be noticed by the binding). But this is just one suggestion which may or may not work for your specific scenario.
(Also see sixlettervariable's good comment)
Just a side note, this code:
public ObservableCollection<HierarchyViewModel> Hierarchy
{
get { return _hierarchy; }
set { _hierarchy = value; }
}
is bad, you could overwrite the list and the binding would break because there is no PropertyChanged event being fired in the setter.
If you use an ObservableCollection it normally is used like this:
private readonly ObservableCollection<HierarchyViewModel> _hierarchy =
new ObservableCollection<HierarchyViewModel>();
public ObservableCollection<HierarchyViewModel> Hierarchy
{
get { return _hierarchy; }
}
The easiest thing to do is detach the item you are bound to, make all the changes you need to the list, then reattach it.
For example, set the treeviews ItemsSource to NULL/NOTHING, run through your for each, then set the ItemsSource back to _hierarchy. Your adds will be instant.

Categories