We have memory leaks in a program that we are trying to stop.
We have isolated the form, and this form has several BindingList Properties.
Currently, these properties are written in the following format:
private BindingList<DataModel1> _dataModel1List;
public BindingList<DataModel1> DataModel1List
{
get
{
var temp = from r in dbo_datamodel1 orderby r.label select r;
_dataModel1List= ((IListSource)temp).GetList() as BindingList<DataModel1>;
return _dataModel1List;
}
set
{
_dataModel1List= value;
}
}
I am no expert on the BindingList class, but it looks to me like we are getting a new set of data every time we call this.
Further, if we get the data using the getter and then set it later using the setter, it looks like we are going to overwrite what we had before with the incoming value.
Could this be the source of our memory leaks? I count 52 of these BindingList properties on this one form.
If I were to rewrite it like below, would that fix the memory leak or cause problems with the BindingList? I am particularly concerned with the Clear() call in the setter.
private BindingList<DataModel1> _dataModel1List;
public BindingList<DataModel1> DataModel1List
{
get
{
if (_dataModel1List== null)
{
_dataModel1List= new BindingList<DataModel1>(
(from r in dbo_datamodel1
orderby r.name
select r).ToList());
}
return _dataModel1List;
}
set
{
if (_dataModel1List!= null)
{
_dataModel1List.Clear();
}
_dataModel1List= value;
}
}
Since there are so many of these properties that could be contributing to the memory leak, I want to make sure I am not going to cause problems before I make this change all over.
Related
This question is a result of the fix to this problem. After getting the sort behavior to properly sort the ObservableCollection, I now notice that the first time the collection is added to, the CustomSorter handler fires with only the first item in it, and that same item is passed in as both parameters to the method. That is producing a duplicate item in the list.
Here are the pertinent parts of the view model code:
public ObservableCollection<PermissionItemViewModel> PermissionItems { get; private set; }
private void FetchRoleData()
{
PermissionItems.Clear();
if (SelectedRole != null)
{
using (var context = new myDataContext(new myDbFactory().GetConnectionString()))
{
foreach (PermissionsEnum permission in Enum.GetValues(typeof(PermissionsEnum)))
PermissionItems.Add(new PermissionItemViewModel(permission, SelectedRole[permission]));
}
}
}
All subsequent manipulations of that collection do not do this...it only happens the first time through the FetchRoleData method. Why?
EDIT:
Some additional information. The CustomSort property is set when the CollectionViewSource fires its Filter event (the only event it has AFAIK). I couldn't come up with any better trigger to get it set. The OnAttached override is too soon, as the View member of the CollectionViewSource is not set yet by that point. Catch 22, I guess. That is happening immediately after that first collection item is added. If this is due to the order in which things are being set, then are there any recommendations for a change?
I don't know how or where you're setting up the filter handler. Here's an example of how to set a custom sort on a CollectionViewSource when its View property changes. That's when you want to do it. This assumes that it's in the resources for a Window (or at least someplace where the Window can find it). But once you have cvs, wherever it comes from and however you got your mitts on it, the rest is the same.
public MainWindow()
{
InitializeComponent();
var cvs = FindResource("MyCollectionViewSource1") as CollectionViewSource;
var dpdView = DependencyPropertyDescriptor.FromProperty(
CollectionViewSource.ViewProperty, typeof(CollectionViewSource));
dpdView.AddValueChanged(cvs, (s, e) =>
{
if (cvs.View is ListCollectionView lvc)
{
lvc.CustomSort = new CustomSorter();
}
});
}
I'm baffled by your claim that the first item in the collection is being duplicated. No code you've shown, and no code I've given you, could have that effect. You'll have to share code that demonstrates that issue.
Dear fellow enthusiasts (and professionals),
I am relatively new to WPF and novice in C#. I am trying to improve my understanding of these, and therefore working on a small application to replace the excel-based tools for inventory- and order management of the family company.
The code itself is pretty simple, so none of the objects require a lot of computation or memory.
The core of the inventory in this application is an ObservableCollection<ProductStack> inventory_hardcopy in a ProductInventory singleton class.
The ProductStack class has a ProductStackCount (int) property and a Prod (<Product>) property.
A Product object has name, buyprice, sellprice and deliveryCost properties.
When the user begins to record the details of a new order, the UI should load the inventory data. The problem is that any changes are immediately reflected in any ObservableCollection, even if the user eventually cancels the dialog window.
I estimate that reverting these changes depending on dialogresult would be way more complex than other solutions.
One such solution would be to clone the inventory_hardcopy to inventory, while depending on the user feedback it is possible to replace the original inventory_hardcopy with the altered clone. This can be implemented via a for cycle and a copy ctor in the ProductStack class. Considering the low complexity and short range of different product objects, these are O(n) processes, but duplications nevertheless.
My current approach is similar, in which, rather then cloning I reference all the products of the ProductStacks of the inventory_hardcopy in a temporary ObservableCollection, called inventory, then I copy the StackCounts of the inventory_hardcopy stacks to inventory (this time the order of the stacks matches, therefore the process is O(n)). Finally, when the dialogresult is true, I copy back the ProductStackCounts to the inventory_hardcopy from inventory via an O(n2) process (O(n2), since the temporary collection can be reordered through drag-drop, therefore the corresponding ProductStack needs to be looked up before overwriting quantities).
However, to make this work, initiating an inventory must be done "manually" by calling OpenInventory() method of the ProductInventory class, and overwriting inventory_hardcopy must be done through calling SaveAndCloseInventory() of the same class.
My question is: is there a more elegand/more appropriate way to make this?
My product code fragments:
public sealed class ProductInventory
{
public ObservableCollection<ProductStack> Inventory
{
get { return inventory; }
set { inventory = value; }
}
ObservableCollection<ProductStack> Inventory_hardcopy
{
get { return inventory_hardcopy; }
set { inventory_hardcopy = value; }
}
public void OpenInventory()
{
Inventory.Clear();
for (int i = 0; i < Inventory_hardcopy.Count; i++)
{
Inventory.Add(new ProductStack(Inventory_hardcopy[i].Prod, Inventory_hardcopy[i].ProductStackCount));
}
}
public void SaveAndCloseInventory()
{
for (int i = 0; i < Inventory.Count; i++)
{
int idx = 0;
while (idx < Inventory_hardcopy.Count && Inventory[i].Prod.ProductName != Inventory_hardcopy[idx].Prod.ProductName)
{
idx++;
}
Inventory_hardcopy[idx].ProductStackCount = Inventory[i].ProductStackCount;
}
// //write to disk here...
}
//other stuffs are implemented here...
}
I just noticed that I can't refresh the values fetched from the DB. Storage (i.e. from the client to the DB) works as supposed to. Loading the first time works as charm as well.
However, if someone deletes a row in the DB (say, using SQL Management Studio), that change isn't in effect in my client until I reinstantiate the whole view model. Only calling Refresh() doesn't fetch the change. The same goes for altering the values of loaded in records.
However, additions to the table are brought in...
I (re)load the values shown in the view from the DB by calling the following method in the view model.
public ViewModel()
{
Reload();
...
}
public void Reload()
{
_data.Set<Order>().Load();
_data.Set<TimeFrame>().Load();
Orders = _data.Set<Order>().Local;
TimeFrames = _data.Set<TimeFrame>().Local;
...
}
readonly Data _data;
private ObservableCollection<Order> _orders;
private ObservableCollection<TimeFrame> _timeFrames;
public ObservableCollection<Order> Orders
{
get { return _orders; }
set { _orders = value; OnPropertyChanged("Orders"); }
}
public ObservableCollection<TimeFrame> TimeFrames
{
get { return _timeFrames; }
set { _timeFrames = value; OnPropertyChanged("TimeFrames"); }
}
What am I missing?
Here the problem is that you load your values, then you use the local property.
The Local collection contains all the values that have been loaded in your context.
if the first time, you load values (1,2,3,4) your Local collection will contain values (1,2,3,4). The second time you load it, you will perhaps load values (1, 4, 5) your Local collection will contain values (1,2,3,4,5)
you should do something like
Orders = new ObservableCollection(_data.Set<Order>());
if it is a read only scenario and you don't need to update and save your data, you should even load your data AsNoTracking, so you won't have caching issues and you will have less EF overhead as your entities won't be tracked
Orders = new ObservableCollection(_data.Set<Order>().AsNoTracking());
Each time when pressing tree node then selectegNodeChanged event triggered and page is reloaded.So, i lost some data stored in Dictionary,ArrayList...How to prevent those data losing?
So, I stored those Dictionary and ArrayList as "static".It is now resolved my problem.
Is it good way to do that?
No. Do not use static. Try to store these in ViewState or Session instead.
You can consider ViewState if it is not a large amount of data.
static will be accessible across Session and is not a good practice.
You can create properties and avoid code duplication like shown below.
public ArrayList TreeNodeDataList
{
set
{
ViewState["TreeNodeDataList"] = value;
}
get
{
if (ViewState["TreeNodeDataList"] == null)
{
return (new ArrayList());
}
else
{
return (ViewState["TreeNodeDataList"] as ArrayList);
}
}
}
Now, when you want to re-assign data, read TreeNodeDataList property. If count of that ArrayList is 0, fetch from DB, else use it.
Hope I am clear enough.
I'm using a PagedCollectionView to bind an ObservableCollection<T> to a DataGrid in my Silverlight app. In this case, the source collection can incur an unbounded number of updates during its lifespan. It seems, however, that if I'm using a PropertyGroupDescription to group the elements in the DataGrid then I need to reapply that grouping using PagedCollectionView.GroupDescriptions.Add(...) every time the source collection is updated with an element that doesn't fit into an existing grouping. Is there any way to make the groupings refresh/recalculate automatically?
public ObservableCollection<DataItem> DataItems
{
get { return _dataItems; }
private set { _dataItems = value; }
}
public PagedCollectionView ItemsView
{
get { return _itemsView; }
private set { _itemsView = value; }
}
public MyGridControl()
{
// Initialize data members
DataItems = new ObservableCollection<DataItem>();
ItemsView = new PagedCollectionView(GridDataItems);
Loaded += new RoutedEventHandler(MyGridControl_Loaded);
}
private void MyGridControl_Loaded(object sender, RoutedEventArgs e)
{
_myGrid.ItemsSource = ItemsView;
}
public void RegisterDataItem(DataItem item)
{
DataItems.Add(item);
/* To get grouping to work like I want, I have to add this:
* ItemsView.GroupDescriptions.Clear();
* ItemsView.GroupDescriptions.Add(new PropertyGroupDescription("GroupName"));
*/
}
public void UnregisterError(DataItem item)
{
DataItem.Remove(item);
}
Thanks, and let me know if I can provide any additional information.
So, as far as I can tell, the grouping operation appears to be a once-off operation that is performed on the data as it exists at the time that the PagedCollectionView.GroupDescriptions collection is manipulated. It seems that the desired groupings do indeed need to be re-applied when the source collection is changed.
I did find one alternative approach for my specific case. Since I'm using an ObservableCollection for the source collection of the view, I can wire up something to the CollectionChanged event of that source collection wherein the PagedCollectionView.GroupDescriptions collection gets cleared and the desired GroupDescription is then reapplied. This doesn't seem totally compliant with good OO practices, nor is it usable if the source collection for the view doesn't implement INotifyCollectionChanged.
I'll leave this open a little longer in case anyone can offer another approach, otherwise I'll just concede.
Thanks again.
Looking at the code in Reflector, the Groupings are correctly processed but if you use view in a DataGrid with IsReadOnly == true you won't see the groupings reflected while changing the source collection.