This is a C# (v3.0) Winforms problem.
I have a big object that is associated with a BindingSource. When I have done with this object and the BindingSource, I want to remove the reference from the BindingSource so the object can be released. I used BindingSource.Clear(). But after that, in the memory profiler, I can still see the object alive and the only reference is from the BindingSource.lastCurrentItem.
My question is, how should I remove the reference from the BindingSource? Thanks.
What happens when you set BindingSource.DataSource = null?
BindingSource.Clear() clears all elements in the underlying list (BindingSource.List), but doesn't remove the reference to the data source. (Reference)
Related
I am implementing Undo/Redo for my .NET 5 application. I have an ObservableCollection of Activity objects (the collection is named Activities). Each Activity object contains two ObservableCollections "ActionItems" and "Notes". The Activities collection is displayed using a ListBox, and the ActionItems and Notes collections are displayed using two respective ItemsControls/ScrollViewers. The Activities ListBox is IsSynchronizedWithCurrentItem, which keeps the ItemsControls in synch.
After each change to an object anywhere in my application, I store a clone on the Undo/Redo stack. When it's time to unto an action on the higher level objects, e.g. an Activity, I replace the Activity object in the ObservableCollection directly:
Activities[0] = activityCloneFromUndoStack;
After this replacement, the UI updates and everything is just fine. However, when I subsequently add a new ActionItem or Note to their respective ObservableCollections on this particular Activity instance, their ItemsControls on the UI do not update. The items are successfully added to the model, but the ItemsControl UI is not reflecting the change.
I have tried refreshing the Items collection:
ItemsControlActivitiesActionItems.Items.Refresh();
But that has no effect. Any other ideas or advice would be appreciated.
UPDATE:
I can see that the Activity object that the new Note/ActionItem is being added to successfully is the Activity object PRIOR to the Undo operation. So even though the parent Activites ListBox control contains the latest Activity object and its synched properties are displaying correctly in the UI, the Notes and ActionItems ItemsControls are still pointing to the original Activity object before the Undo operation. So now the question is
"How do I force the 'child' ItemsControls to recognize the newly replaced object in the 'parent' (IsSynchronizedWithCurrentItem) ListBox control?"
So I was finally able to trace the problem back to the way I was cloning the objects. I was using the .MemberwiseClone method to make a shallow copy, and then turn that into a deep copy by manually copying all of the reference types. But somehow the WPF engine seemed to be holding onto a reference to the last object before the undo operation.
So I change my method of cloning to use a constructor that accepts the object to be copied as an argument. This is one of the other approaches suggested in the documentation:
There are numerous ways to implement a deep copy operation if the
shallow copy operation performed by the MemberwiseClone method does
not meet your needs. These include the following:
Call a class constructor of the object to be copied to create a second
object with property values taken from the first object. This assumes
that the values of an object are entirely defined by its class
constructor.
Call the MemberwiseClone method to create a shallow copy of an object,
and then assign new objects whose values are the same as the original
object to any properties or fields whose values are reference types.
The DeepCopy method in the example illustrates this approach.
Serialize the object to be deep copied, and then restore the
serialized data to a different object variable.
Use reflection with recursion to perform the deep copy operation.
Having to work with a legacy silverlight application I ran into a strange piece of code. The viewmodel has a List dependency property as binding source for the grid. This DP has a default value, an other List that is used globally in the app. This is used to easily share entity data between different parts of the application.
DependencyProperty MyEntitiesProperty = DependencyProperty.Register("MyEntities", typeof(List<Entity>), typeof(...), new PropertyMetadata(Global.Entities));
Now, when the list is changed (on user actions), the global list is repopulated from database but MyEntities is never set explicitly. This does not work: the grid (the binding target) never changes. So its a wrong solution.
I speculate that the idea behind all this could have been been the following: if you have a DP with a given value and you never set a local value for it then the effective value of the DP will be the default value. If the 'underlying' default value is changed, the changes are reflected in the effective value.
If it worked, it was a nice way of sharing data between independent viewmodels without fiddling with property change events and such.
What is wrong here? Is it a big misunderstanding of how DPs work or the idea was ok and some implementation details were missed?
Please comment if something is not clear.
Well, taking also your comment into account, it is a big misunderstanding of how DPs work. Let me explain:
Setting a globally known list as the default value of MyEntities might not be a pattern I recommend, but is technically not faulty and can be done to share a list. MyEntities now holds a reference to this very list.
If you now replace the global list with a new list instance, the old instance does not cease to exist. Your property MyEntities still holds a reference to the old list. The value of a DP is only updated automatically if it is bound via Binding to either an ordinary property that is wired with the INotifyPropertyChanged mechanism or another DP.
Setting a default value happens neither via a Binding to an ordinary property nor via a Binding to another DP, it is just a plain old object reference.
I can think of several ways to correct the situation:
First solution
If the global list implements INotifyCollectionChanged (e.g. ObservableCollection, DependencyObjectCollection) you can - instead of creating a new list instance - just delete the old items from the list and add the new items. The views that have a reference to the list will perform an update as soon as they receive the associated CollectionChanged event.
Second solution
Make sure the Global.Entities list is available and always up-to-date as a public property (wired with INotifyPropertyChanged) on the DataContext of the root view. Now when you want a nested view somewhere deep down inside the UI tree to be connected to this Global.Entities list you can bind it to the root view's DataContext' public list property.
<MyRootView>
... nested views spread across multiple files ...
<MyNestedEntitiesListDisplay
MyEntities="{Binding
Path=DataConext.GlobalEntities,
RelativeSource={RelativeSource AncestorType=MyRootView}}"/>
I'm working on a WPF project and have implemented a very simple way to undo one level of change which works nicely throughout the project except for one case where changes to an object's property reflects in the MemberwiseClone.
What I am doing is to do a MemberwiseClone in my object before adding or editing properties in that object, and then if the user wants to undo, I copy each property from the MemberwiseClone object back into my current object.
Because I am using WPF binding, using the MemberwiseClone is attractive to me because up until now, any change made in a property was not reflected in the MemberwiseClone. This time I have a property in my object that is an ObservableCollection of another object, and what is happening is that if I add an item to the ObservableCollection, it also gets added to the object created by MemberwiseClone and I can never truly undo.
Is there any way around this? Any thoughts you might have on this are welcomed.
Thanks.
According to Object.MemberwiseClone Remarks the object references in your ObservableCollection will be copied but not the referenced object itself. Therefore your undo collection references the same possible changed objects.
You need a deep copy, not a shallow one. Take a look at How do you do a deep copy an object in .Net (C# specifically)?
I have a list of objects which among other properties contain a Brush (property name "Color").
In a Backgroundworker I want to write the contents of these objects to a file.
Everything works fine until I try to read the color of the brush property.
//this works fine
var brush = myObject.Color;
//this fails with InvalidOperationException
var c= ((SolidColorBrush)myObject.Color).Color;
It seems as if my code is somehow modifying the color but I don't see where/how.
Is there a way to read-only access the color of the brush?
Regards,
tabina
Do i sense that you're accessing UI objects on a thread that didn't create them. You cannot access UI objects from a BGW thread. That's the golden rule not to forget using BGW class
If there is no need to modify the objects after they have been added to that list, you might try to Freeze them before they get accessed from a different thread.
Make sure that the Freeze call happens in the UI thread, where you created those objects. You might also check if freezing is possible at all by calling CanFreeze beforehand.
I have a collection of an object called Bookmarks which is made up of a collection of Bookmarks. This collection of bookmarks are bound to a treeview control.
I can get the bookmarks back out that I need, but I need a copy of the bookmarks so I can work with it and not change the original.
Any thoughts.
Thanks.
Create a new constructor for your bookmark class that takes an existing bookmark as the parameter.
Within this new constructor, copy all the property values from the existing bookmark onto the new one.
This technique is known as a "Copy Constructor".
There's an article on MSDN that goes into more detail - see How to Write a Copy Constructor.
Most collection classes in .Net provide a constructor overload that allow you to pass in another collection like
dim copyOfBookMars as New List(of BookMark)(myOriginalBookMarkList)
Been awhile with VB, but c# offers a clone() method.
You don't generally make a copy of an Object, an Object makes a copy of itself (clone). Since an object contains state inforrmation, a bitwise copy can't be counted on as being appropriate; so the defining class needs to take care of it.
You may want to implement multiple simultaneous pointers (bookmarks) in your case.