I have defined my binding thus:
<TreeView
ItemsSource="{Binding UsersView.View}"
ItemTemplate="{StaticResource MyDataTemplate}"
/>
The CollectionViewSource is defined thus:
private ObservableCollection<UserData> users;
public CollectionViewSource UsersView{get;set;}
UsersView=new CollectionViewSource{Source=users};
UsersView.SortDescriptions.Add(
new SortDescription("IsLoggedOn",ListSortDirection.Descending);
UsersView.SortDescriptions.Add(
new SortDescription("Username",ListSortDirection.Ascending);
So far, so good, this works as expected: The view shows first the users that are logged on in alphabetical order, then the ones that are not.
However, the IsLoggedIn property of the UserData is updated every few seconds by a backgroundworker thread and then the code calls:
UsersView.View.Refresh();
on the UI thread.
Again this works as expected: users that log on are moved from the bottom of the view to the top and vice versa. However: Every time I call the Refresh method on the view the application hoards 3,5MB of extra memory, which is only released after application shutdown (or after an OutOfMemoryException...)
I did some research and below is a list of fixes that did NOT work:
The UserData class implements INotifyPropertyChanged
Changing the underlying users collection does not make any difference at all: any IENumerable<UserData> as a source for the CollectionViewSource causes the problem.
-Changing the ColletionViewSource to a List<UserData> (and refreshing the binding) or inheriting from ObservableCollection to get access to the underlying Items collection to sort that in place does not work.
I am out of ideas! Help?
EDIT:
I found it:
The Resource MyDataTemplate contains a Label that is bound to a UserData object to show one of its properties, the UserData objects being handed down by the TreeView's ItemsSource. The Label has a ContextMenu defined thus:
<ContextMenu Background="Transparent" Width="325" Opacity=".8" HasDropShadow="True">
<PrivateMessengerUI:MyUserData IsReadOnly="True" >
<PrivateMessengerUI:MyUserData.DataContext>
<Binding Path="."/>
</PrivateMessengerUI:MyUserData.DataContext>
</PrivateMessengerUI:MyUserData>
</ContextMenu>
The MyUserData object is a UserControl that shows All properties of the UserData object. In this way the user first only sees one piece of data of a user and on a right click sees all of it.
When I remove the MyUserData UserControl from the DataTemplate the memory leak disappears! How can I still implement the behaviour as specified above?
Step one in troubleshooting memory leaks is to find the definitive source. This is not always obvious and can occasionally defy your intuition. Based on your explanation, if you remove your user control, the issue disappears, but when you put it back, you start leaking again. This very likely points to a memory leak within that control (though not necessarily). Perhaps your control fits into one of the many types of WPF memory leaks, or you have a more classic problem of subscribing to an event but not properly unwiring it when no longer needed (the weak event pattern is useful here).
Your best bet is to grab a tool like .NET Memory Profiler or ANTS Memory Profiler (both are excellent and have free trials). Use one of these tools find the objects that are hanging around after they should be gone. These tools provide help in tracing the chain of objects that are hanging onto your object. There are many good articles on memory profiling on their sites, here on SO and on the wide open web.
You could try 2 things:
First of all the DropShadow has some problems, as it hold some references that should be garbadge collected.. see this article for more information:
http://blog.ramondeklein.nl/index.php/2009/02/20/memory-leak-with-wpf-resources-in-rare-cases/
You can try to set th HasDropShadow to false, and see what happens with your memory.
Secondly, if we have multiple ContextMenu objects of the same menu, you might want to create a ContextMenu resource in your Resources, and reference it with the StaticResource extension like so:
<ContextMenu Background="Transparent" Width="325" Opacity=".8" x:Key="MyAwesomeContextMenu">
<PrivateMessengerUI:MyUserData IsReadOnly="True" >
<PrivateMessengerUI:MyUserData.DataContext>
<Binding Path="."/>
</PrivateMessengerUI:MyUserData.DataContext>
</PrivateMessengerUI:MyUserData>
</ContextMenu>
And use it where you defined your context menu:
ContextMenu="{StaticResource MyAwesomeContextMenu}"
Hope this helps a bit!
Related
I am working on UWP app that has Order Class, which bind to UWP UI. It was all working fine then it start to crash at point with Access Violation error. I debug and found that I have a TextBox in XAML, that bind to Order Class's one of property. [there are several other in exact same manner from same class]. Now when I update the property of order by calculating value (it is been a float value) it is set to update on UI, it update other fields well but crash when I try to set this one field with memory violation. I have 10 different field that bind fine in same function to same UI from same object,
<TextBox x:Name="Margin" IsReadOnly="True" MinWidth="100" MaxWidth="120" MaxLength="10" Header="Margin (£)" Padding="5"
Margin="5,5,5,0" Text="{x:Bind ViewModel.SelectedOrder.Margin, Mode=TwoWay}" RelativePanel.Below="GridOrderItems"
RelativePanel.RightOf="Parcel" />
<TextBox x:Name="txtMarginP" IsReadOnly="True" MinWidth="100" MaxWidth="120" MaxLength="5" Header="Margin (%)" Padding="5"
Margin="5,5,5,0" Text="{x:Bind ViewModel.SelectedOrder.MarginP, Mode=TwoWay}"
RelativePanel.RightOf="Margin" RelativePanel.Below="GridOrderItems" />
The field above pass through without error, but field txtMarginP fails with error.
private async Task UpdateCart(float shippingprice = 0)
{
...
ViewModel.SelectedOrder.Margin = calculatedMargin;
ViewModel.SelectedOrder.MarginP = calculatedMarginP;
...
}
This is the function call that generate error. I calculate margin (as float) and pass. It used to work. Then it start to fail, while I work on other screen not related to it, and order class and this UI is unchanged.
Excat error is Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt. Which comes in Orderdetail.g.cs file [that is autogenerated by XAML compiler. Object value get set properly.
I try to use DispatcherQueue to set value in my object but it doesn't solve the problem. Since nothing is knowningly change I believe either some compiler setting changed in one of update to Visual Studio or something else. But I cannot figure it out nor can I debug it to solve it. Any Idea is appreciated. Thank you.
EDIT ---
After some code changes [to rearrange there call in symmetric order], I nail down to situation where application stop crashing, if I change my ObservableCollection to List, but then it do not bind, and item is not visible even if I change I assign the Grid.Itemsource
It turns out that I was binding some data in my combobox in my listview. Since I have to bind that Combobox multiple time when we reload product. it was causing the XAML to leak memory and go crazy. I have to remove ComboBox from ListView and replace it with AutoSuggestBox or [depending on your workflow] or limit the number of item you can load in combobox. I cannot limit them as I have around 700 record to bind... with 2-3 rows they duplicated to 1400-2100 element to manage, with observable for all.
Rewriting some major portion to avoid it.
In my DB I have a table that contains different items of userControls with the attributes "ClassName", "AssemblyName" and "NameSpace" which are necesarry to init the instances using reflection.
My Idea was To get this collection from the DB, set the collection as the data-context and dynamically load these usercontrols into a tabcontrol. I could use a "tabItem" which would contain it and in runtime in the code behind load it. I guess it would be really handy and fancy if it could be done directly from XAML in a template.
I've been googleling for something similar, but found nothing without using code behind.
I was thinking something like the following
<TabControl.ContentTemplate>
<DataTemplate>
<xxxControl ClassName="{Binding ClassName}" AssemblyName="{Binding AssemblyName}" NameSpace="{Binding NameSpace}" />
</DataTemplate>
</TabControl.ContentTemplate>
I could make such a custom "xxxControl" but it would be a waste of time if something like that already exists. This way The GUI could be completly generated by the parameters in the DB.
You can do a lot of things in XAML using markup extensions, in this case you could create one that instantiates controls from the given information. For that it needs some dependency properties that can be bound, and in ProvideValue it would then get the assembly, construct the full name and instantiate it.
Usage:
<DataTemplate>
<me:Instance Assembly="{Binding AssemblyName}"
NameSpace="{Binding NameSpace}"
Class="{Binding ClassName}"/>
</DataTemplate>
Obviously you still have code-behind, but that is how it should be, imperative code does not belong in XAML at all.
Also i doubt that your data-base should contain information about UI controls...
Ugh. Don't control your UI from the database directly. The closest you should come (assuming you can't make significant architecture changes) IMO would be to load your DB entries into an IObservable in your VM and use a DataTemplateSelector to translate your collection into UI controls.
I have a boolean property in my ViewModel, named lets say IsNotSupported that is used to show some warning information if a sensor is not supported. Therefore I use a BooleanToVisibilityConverter, that is added in the ressources:
<phone:PhoneApplicationPage.Resources>
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</phone:PhoneApplicationPage.Resources>
and bind it to the stackpanel containing the warning:
<StackPanel x:Name="NotSupportedWarning" Visibility="{Binding IsNotSupported,
Converter={StaticResource BooleanToVisibilityConverter}}">
That works all quite well, but when loading the page, and the sensor is supported, the warning appears for just a fraction of a second and disappears afterwards. I know that this flickering is caused by the binding not having happened yet and therefore defaulting to visible.
That flicker it is annoying as hell... It should rather default to collapsed and be made visible only after it is clear that the warning should be shown. Also, this would avoid a second layouting pass after the binding and could therefore have positive performance impacts.
I had this problem over and over, and found nothing about it in the internet until I found this SO question, that is closely related, but is not found if searched for windows phone instead of silverlight. Both the problem and the solution might seem simple, but I really bugged me quite a long time, so I thought it might be a good idea to write a Q&A-style question about it to help others that are facing the same issue.
The solution is simple after you have seen it. You can control the default value of the binding (if the binding didnt happen yet) with FallbackValue. Your stackpanel XAML would look like:
<StackPanel x:Name="NotSupportedWarning" Visibility="{Binding IsNotSupported,
FallbackValue=Collapsed,
Converter={StaticResource BooleanToVisibilityConverter}}">
This way you get rid of the flicker and it does not have to be relayouted after the binding, if the warning stays hidden.
you can bind directly to a Visibility type of property instead of boolean and keep that property to collapsed by default plus you can implement INotifyPropertyChanged
I'm a Java developer whose inherited a web project from another team whose sole developer upped and quit. The majority of the project is written in Spring, JSPs and jQuery, however there is one component that is a Silverlight XAP.
Of course, the project was just handed to me and we're already having production issues with it. I've never written a lick of C# before, and am scrambling to try and fix this as fast as humanly possible.
This is a Microsoft VS 2010 (Pro Edition) C# solution project, with a presentation layer consisting of XAML and CS files. The component that needs to be fixed is a file uploader that allows users to choose 1+ files from their machine and place them in a "queue" (a listbox). Then, with the click of a single button, all the "queued" files get uploaded to our server.
I need to be able to tell when the listbox is empty (when there are 0 files queued/populating it). This feature is represented in the code by a XAML/CS pair of files named ListItemControl.xaml and ListItemControl.xaml.cs respectively.
Here is the beginning of the CS file:
namespace silv.Uploader
{
public partial class ListItemControl : UserControl
{
private UserFile UserFile { get { return (UserFile)this.DataContext; } }
public ListItemControl()
{
InitializeComponent();
...
Applying Java concepts to its C# cousin (as I've been told), this looks like my ListItemControl is an in-house class extending a Microsoft built-in type of System.Windows.Controls.UserControl. Furthermore, that it has a UserFile property (which I assume is another homegrown type) representing a file or set of files that needs to be uploaded.
So first, as a sanity check: can someone please confirm those assumptions above?!?
First off, in Java/Swing-land, you would instantiate or subclass a JList (listbox) type, which has built-in methods/models to determine the number of items populating it at any given time.
What is the ListItemControl/UserControl equivalent in C#-land?
In this CS file I don't see any methods for determining size/length/counts of data. Same when I F12 (Go to Definition) UserControl.
Essentially, I'm asking here: how do I query this listbox component for the number of items it is populated with? Thanks in advance for any nudges in the right direction...
Edit: Below is a snippet of the XAML that I believe is responsible for initializing this listbox:
<ScrollViewer Grid.Row="1" x:Name="svFiles" IsEnabled="True" ScrollViewer.VerticalScrollBarVisibility="Visible" Margin="0,1,18,252">
<StackPanel x:Name="stkMain" Orientation="Vertical" VerticalAlignment="Stretch">
<ItemsControl x:Name="icFiles" KeyUp="ScrollViewer_KeyUp" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ListItemControl ScrollViewer.VerticalScrollBarVisibility="Visible" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
The updated snippet seems to be the right piece of XAML - you have the ItemsControl, which is a generic repeater control for rendering a collection of items. What I still don't see is where the items are added, but that might be somewhere else in the code.
However, since the ItemsControl is given a name, so you can access that:
ItemCollection items = icFiles.Items;
int count = items.Count;
Where exactly are you trying to access the queue length? This needs to be in the code-behind of the window that the ItemsControl is defined in.
Normally a List<Something> control would implement or inherit from a control which implements the IEnumerable interface.
You don't provide the full mark-up but I expect there's more code below which exposes some property such as .Length or .Items.Count which would tell you the number of elements in the array, dictionary, etc.
I'm being necessarily general here, as the precise property names will depend on how it implemented in the code we can't see.
After wasting hours on this, following on the heels of my Last Problem, I'm starting to feel that Framework 4 is a master of subtle evil, or my PC is haunted.
I have three comboboxes and a textbox on a WPF form, and I have an out-of-the-box Subsonic 3 ActiveRecord DAL.
When I load this "edit record" form, the comboboxes fill correctly, they select the correct items, and the textbox has the correct text. I can change the TextBox text and save the record just fine, but the comboboxes CANNOT BE CHANGED. The lists drop down and highlight, but when you click on an item, the item selected stays the same.
Here's my XAML:
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<TextBlock Width="80">Asset</TextBlock>
<ComboBox Name="cboAsset" Width="180"
DisplayMemberPath="AssetName"
SelectedValuePath="AssetID"
SelectedValue="{Binding AssetID}" ></ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<TextBlock Width="80">Status</TextBlock>
<ComboBox Name="cboStatus" Width="180"
DisplayMemberPath="JobStatusDesc" SelectedValuePath="JobStatusID"
SelectedValue="{Binding JobStatusID}" ></ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<TextBlock Width="80">Category</TextBlock>
<ComboBox Name="cboCategories" Width="180"
DisplayMemberPath="CategoryName"
SelectedValuePath="JobCategoryID"
SelectedValue="{Binding JobCategoryID}" ></ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,0,0">
<TextBlock Width="80">Reason</TextBlock>
<TextBox Name="txtReason" Width="380" Text="{Binding Reason}"/>
</StackPanel>
Here are the relevant snips of my code (intJobID is passed in):
SvcMgrDAL.Job oJob;
IQueryable<SvcMgrDAL.JobCategory> oCategories = SvcMgrDAL.JobCategory.All().OrderBy(x => x.CategoryName);
IQueryable<SvcMgrDAL.Asset> oAssets = SvcMgrDAL.Asset.All().OrderBy(x => x.AssetName);
IQueryable<SvcMgrDAL.JobStatus> oStatus = SvcMgrDAL.JobStatus.All();
cboCategories.ItemsSource = oCategories;
cboStatus.ItemsSource = oStatus;
cboAsset.ItemsSource = oAssets;
this.JobID = intJobID;
oJob = SvcMgrDAL.Job.SingleOrDefault(x => x.JobID == intJobID);
this.DataContext = oJob;
Things I've tried:
Explicitly setting IsReadOnly="false" and IsSynchronizedWithCurrentItem="True"
Changing the combobox ItemSources from IQueryables to Lists.
Building my own Job object (plain vanilla entity class using INotifyPropertyChanged).
Every binding mode for the comboboxes.
ItemsSource="{Binding}"
The Subsonic DAL doesn't implement INotifyPropertyChanged, but I don't see as it'd need to for simple binding like this. I just want to be able to pick something from the dropdown and save it.
Comparing it with my last problem (link at the top of this message), I seem to have something really wierd with data sources going on. Maybe it's a Subsonic thing?
EDIT: For some reason the set accessor is hit only on the AssetID property and only the first time. WPF is now heading for WTF :)
EDIT 2: You gotta be kidding me- I've removed the binding (ie it only has a displaymemberpath, a valuememberpath and an itemssouce) and it's STILL doing it! It accepts your first selection, and then won't change.
WPF Combo Boxes will not change the selected item if the currently selected item and the item that was just selected are considered equal by the object.Equals() method called on the newly selected object (i.e newlyslected.Equals(previoslySelected) ).
Overriding the Equals method on the class your binding the combobox items, should resolve the issue your are seeing.
I've narrowed it down to the Subsonic objects used as ComboBoxItems.
If you create a new class that uses exactly the same code as the relevant parts of the Subsonic one, it works.
If you use POCOs/datatables for the combos and Subsonic for the record being edited, it works.
But if you use Subsonic for both, it doesn't.
I had hoped to extend the subsonic objects and not have to code a full-blown BLL tier. Looks like I'm faced with doing that or throwing out Subsonic for the DAL. I might post a more specific question for the Subsonic folks.
Many thanks to all who contributed.
Old topic but I had the same problem and difficulty finding solution. This might help someone else.
Clue is above in WPF not detecting a different item has been seleted by user. (Symptom - event ComboBox_SelectionChanged only fires on first selection)
My scenario - lookup combo populated from IList built from a DISTINCT query. In this case the result of using NHibernate ICriteria.SetResultTransformer which only returns SOME fields, importantly NOT including the unique entity ID.
Solution - loop thru' IList after retrieval and give each entity a unique ID. WPF sees them as individuals and behaves appropriately.
Its only a value lookup - its the value content I was after.
The 'temporary' entities are never persisted. In this case it was a better approach than messing with overriding the object's Equals method for the sake of a simple GUI issue. An alternative would be to just copy or tranform the list into a format where WPF uses the value field to determine 'difference'...
Sounds like the field is somehow readonly, or that your change isn't being persisted. After the binding sets the new value, it will re-read the property to ensure that it was actually changed. If your property returns the old value, then it'll be re-selected in the combo box, giving the appearance that the value never changed.
I don't know that DAL, but can you step through the property setter code? You might also have an issue with type conversion.
EDIT reading your comment about the red rectangle -- it sounds as though your property (or something to do with the binding) is raising an exception. Unless, of course, you're using data validation in your UI. You might turn 'Break on all exceptions' in the debugger's settings, assuming you're using Visual Studio.
EDIT 2 You should check the VS Output pane for any error messages related to binding. You can also read this blog post which gives more info on debugging bindings.
It's hard to tell from a small sample of your code but try commenting out the line:
//this.DataContext = oJob;
and see if this helps.
Setting the DataContext and ItemsSource might be causing a conflict.
Did you write any global style for your combo box which may have a bug or something missing? Or are you using pure default styles for your combobox? Try removing any default styles applied.
Are you wiring up any events? If your code hooks up for event like PreviewMouseLeftButtonUp and marks event as handled then probably combobox may ignore and wont select anything.