Combobox performance after changing ItemsSource - wpf

I have a datagrid with a column of comboboxes defined like this:
<DataGridTemplateColumn x:Name="AssortmentQualitySettingsDataGridColumn" Header="Kvaliteter">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Path=QualityInfoAssortmentCollection}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Path=ActiveQuality}"></CheckBox>
<TextBlock Text="{Binding Path=QualityName}" IsEnabled="False"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
The ItemsSource is an ObservableCollection of objects. The texts for the textbox in the combobox is however editable in another datagrid and put in another ObservableCollection called QualityItemCollection, and to get the comboboxes in the grid above updated I have an event on the datagrid that fires when that collection is changed. This even causes the QualityInfoAssortmentCollection to be re-read (the combobox needs to be set again also, so there is some looping to get it working).
Now, what happens is that the first time when data is loaded, everything is nice and dandy, however, when the event updating QualityInfoAssortmentCollection has fired the comboboxes above takes 5-7 seconds to drop down when trying to get into it. The refresh itself I have timed and it takes less than a tenth of a second to do. The combobox doesn't have more than 8-10 rows and there no difference trying to use a virtualizingstackpanel as suggested elsewhere. The datagrid displaying it has around 10 rows so it's not even close to any enormous amounts of data that needs to be shuffled.
Edit: More explanation about not being able to use the defining QualityItemCollection. The QualityItemColletion is the same for all items in the above datagrid, but the information about which checkboxes should be checked is set per item in the grid above. Therefore I make a copy of QualityItemCollection into QualityItemAssortmentCollection which also has a bool for the checkbox. There might be a better way to do this?
Edit 2:
Tried the WPF profiler now and it locks up just as the program does and doesn't display anything during the time the program is doing strange things. However, it turns it's something Visual Studio does, since if I run the program alone and not through Visual Studio there is no delay. Yay.

Problem was with the VS debugger. It for some reason makes the combobox excruciatingly slow.

So, fix it?
when the event updating QualityInfoAssortmentCollection has fired the comboboxes above takes 5-7
seconds to drop down when trying to get into it.
Where does it spend time? It is not like there are no profilers around. It is totally possible that this is WPF related, in which case this link:
http://msdn.microsoft.com/en-us/library/aa969767.aspx
also gets you staretd with a WPF level profiler (i.e. you can see where WPF spends the time, which may be some mistake in some WPF definitions).
It is also possible you send too many nonsensible update events (ou should always c hange whether a value HAS vchanged before sending an update notification). So, an upate may updatea property to teh same value triggering another update. A profiler will allow you to find these occurances.
Noone here can help yuo - without code etc. But a profiler should make it QUITE obvious where the time is spent.

Related

why is constructor called in my WPF when binding?

I have a WPF application with the following XAML in my MainWindow.xaml.
I don't understand why the DxTaskList constructor is called when I make a call to OnPropertyChanged("Sequences");. As you see below, my tab control is bound to a Sequences list. In the related view model class, I have a Sequences property that I modify, so naturally I need to let the view know, so I make the call to the OnPropertyChanged("Sequences") but I'm trying to understand how WPF works.
Does the entire visual tree get rebuilt when you refresh the binding of a parent? How does that work? Please note that my app uses Prism, so I'm not sure if this makes a difference.
<dxdo:LayoutPanel Caption="TaskList">
<dx:DXTabControl x:Name="TabControl"
ItemsSource="{Binding Sequences}"
SelectionChanged="TabControl_OnSelectionChanged">
<dx:DXTabControl.View>
<dx:TabControlMultiLineView HeaderLocation="Bottom"/>
</dx:DXTabControl.View>
<dx:DXTabControl.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Name}"></TextBlock>
<views:DxTaskList x:Name="Tasklst"/>
</Grid>
</DataTemplate>
</dx:DXTabControl.ItemTemplate>
</dx:DXTabControl>
</dxdo:LayoutPanel>
Everything appears to be working as it should.
If your Sequences property is an ObservableCollection it will fire NotifyPropertyChanged on any change to the size of the collection. If Sequences is not an ObservableCollection, but is some other IEnumerable then your OnPropertyChanged("Sequences") will do it.
Back to your question:
Let's assume it is not an ObservableCollection. You insert an item and then fire OnPropertyChanged("Sequences"), a new DxTaskList is created (ctor is called) and gets inserted into the visual tree.
Try calling OnPropertyChanged("Sequences") twice in a row, I bet you are only hitting the constructor after the first, and not on the second. If not, it could be because of how the DxTabControl is internally implemented.

A way to bind differents values of a property in each DataGrid cell

I've searched on many threads but I couldn't find anything to solve my problem, and I don't really know what keywords I should use. I have a DataGrid which is populated by a DataSet, with columns that I specify manually.
In my column, 'Total', the Binding is set by :
<DataGridTextColumn Header="Total"
Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type UserControl}},
Path=DataContext.Total}"
The Total property is not in the DataSet, which is bound to my DataGrid, that's why I use this kind of Binding.
The problem is that when I set a value inside a cell of 'Total', like any user would do, this value is repeated in each cell of my column.
So, I know it is because of this property, since everytime I change my cell's value the Total property gets this new value and sends it back to my column. I could use a condition which uses the selected row's number or something, I have some ideas of what I should do, but I can't do that in my Xaml code.
I tried to create a Binding() in the code behind part but it doesn't really work and I'm not sure it could change anything.
Is there any way to use one value per row in this case ?
Thanks !
So, as Lee O. kindly said, I've first decided to alter my DataSet by adding a new column and some data inside. The problem was that I couldn't find any way to bind successfully on this second column in my designer code.
Anyway, I knew it wasn't the best way because I kind of prevented myself to modify something big to avoid big problems in my code and some other reasons, so I spent out a whole day to make it work better. I didn't think it could have taken so much time to modify it, but it finally worked.
The solution is to create (or alter in my case) a class which represents one row in your DataGrid, with as many properties as you need, including every columns you may want to have. Then, create an ObservableCollection (I really love this type of list, very useful and easy to use / update) in your ViewModel.
Finally, bind your column to your property in your class in your MainWindow like that :
// new ViewModel() + ObservableCollection<Class>
this.gridCameras.ItemsSource = viewModel.OCCameras;
this.Total.Binding = new Binding("Total");
If it can help someone too, I found something to bind correctly this class to a ComboBox in your DataGrid, which is a bit more complicated, here and here as I suppose that your ComboBox should be written like that (don't forget the x:Name property used in the foreach loop) :
<DataGridTemplateColumn Header="Fps" Width="80" x:Name="Fps">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="cbFps"
Foreground="Black"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
I strongly suggest you to add an Index property in your class, since I use that to identify the object in my ObservableCollection when I edit a cell / row and some other things. It's much better than to loop on your list until you find the element everytime you'd need it.
Hope it helps and feel free to ask any question you may have. I don't say I used the best way of the world, but it works for me and that's what I wanted.
Thanks again for your tips Lee O.

WPF MVVM and parent-child combobox

I'm sure I already found this on StackOverflow, but I don't seem to be smart enough to find it again
What I want to do (in WPF using MVVM) is this:
cmbSelectedAddressRegion: populated with the list of region
cmbSelectedAddressCities: populated with the list of cities in that region
When the user click on a region in cmbSelectedAddressRegion the items in cmbSelectedAddressCities should be the cities of that region only
I have an XAML like this
<ComboBox Name="cmbSelectedAddressRegion"
SelectedValue="{Binding Path=selectedAddressItemRegion, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Path=selectedAddressIsEnabled}"
Style="{StaticResource style_flat_ComboBox}"></ComboBox>
<ComboBox Name="cmbSelectedAddressCities"
SelectedValue="{Binding Path=selectedAddressIdCities, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="id"
SelectedValuePath="id"
ItemsSource="{Binding ElementName=cmbSelectedAddressRegion, Path=SelectedItem.Cities}" IsEnabled="{Binding Path=selectedAddressIsEnabled}"
Style="{StaticResource style_flat_ComboBox}"></ComboBox>
When I click on a region in cmbSelectedAddressRegion the cmbSelectedAddressCities is correctly populated
I also have a VM vmCustomer with a lot of DependencyProperties (amongst them selectedAddressItemRegion and selectedAddressIdCities)
When I select the customer from the master list (another combobox in the window which holds the list of customers) I see the cmbSelectedAddressRegion correctly showing the Region, but I don't see anything in the cmbSelectedAddressCities. Again if I click on the cmbSelectedAddressRegion the cmbSelectedAddressCities is populated and the currently selected cities (in the vmCustomer) is selected
The cmbSelectedAddressRegion.itemssource is bounded (in bode behind file) to an ObservableCollection(of vmAddressRegion)
Each vmAddressRegion has, amongst other DependencyProperties, a cities properties which returns an ObservableCollection(of vmAddressCities)
The ObservableCollection(of vmAddressRegion) is populated when the window is created. At the same time, for every item of ObservableCollection(of vmAddressRegion) (of type vmAddressRegion) the ObservableCollection(of vmAddressCities) is populated with the corresponding items)
I hope I've been clear enough
Any suggestion how to resolve the problem above (the cmbSelectedAddressCities not being "populated")?
Thanks for any help
The WPF ComboBox needs to be handled with care. Bindings easily get confused if ItemsSource and SelectedValue/SelectedItem change in the "wrong" order, especially if you use SelectedValue.
My advice would be to replace the "SelectedValue" bindings with "SelectedItem". The binding expressions can stay the same (although I don't think you need to specify the UpdateSourceTrigger).
Binding to SelectedItem means that your vmCustomer needs a selectedAddressCity property instead of the selectedAddressIdCity id (and you can remove cmbSelectedAddressCities' SelectedValuePath).
Sorry for the long delay.
I know I promised to post back my solution during the end of the week, but my pc decided to die the following day.
Regarding my original problem I adopted 2 "solutions".
First I removed the numeric ID changing it to the full description of the Region/City. I thought this was good, but the problem still remained for some "strange" cases (for example when clicking the first time on the Region combobox.
After digging a while in the code I discovered the real problem was in a converter I wrote for the application. During a conversion I made a mistake: instead of "if isnothing" I wrote "if not isnothing" and the resulting was a nothing converted to space, messing up the whole father-child relation.
As I was worried, the problem was in my code, and not in the piece of code I posted here.
I thanks again everyone, and apologize again for the delay

Silverlight/XNA Data Binding Irregularity

I have to preface this with a disclaimer. I'm a novice programmer, I've tried solving this on my own for days but have now completely run out of ideas/blog posts/walkthroughs and other sources. I really appreciate your time in reading and potentially replying.
I am trying to integrate scoreloop into a game I'm developing but am getting some very strange results with data binding and a listbox. My tests (below) imply that there has to be something I'm doing wrong with bindings, but the crazy thing is it actually works the first time I use it, but not for subsequent levels. Here is the important code I'm using:
XAML:
<ListBox x:Name="LeftListBox" Margin="12,48,0,128" ItemsSource="{Binding}" Background="{x:Null}" HorizontalAlignment="Left" Width="240">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<StackPanel Margin="0,0,0,0" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBlock Text="{Binding Rank}" TextWrapping="NoWrap" />
<TextBlock Text="." Width="54"/>
<TextBlock Text="{Binding Result}" TextWrapping="NoWrap" Width="76"/>
<TextBlock Text="{Binding User.Login}" TextWrapping="NoWrap"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have an event registered to fire whenever scores are loaded, this sets the binding and logs a debug message:
LeftListBox.DataContext = App._scoresController.Scores;
Debug.WriteLine("Scores Loaded");
App._scoresController.Scores contains User.Login, Rank, and Result.
After I beat a level it pulls down scores and displays them int he listbox just like I expect. As soon as it goes through the same cycle for the next level though the listbox is blank. The debug line of "Scores Loaded" always gets logged, so I know the event is firing.
What I've done so far in testing:
Turned on ALL for bindings logging and could not see anything getting logged in the output.
Set a break point at the Debug "Scores Loaded" line and can see that everytime it hits there it correctly assigned the datacontext,
with the correct fields with exactly matching names
Tried using Dispatcher.BeginInvoke(LoadScores); to be sure I was doing it off the UI thread in case this was somehow a threading
issue
Set the background color on the stackpanel to a color that I could use to ensure it wasn't being collapsed or hidden by another control
or a storyboard animation
Created a copy of the same listbox, set listbox.datacontext = this in the same LoadScores() method, then set up local variables
for it to bind to. Found that this exhibited the same behavior,
disappearing on the second time I go to set the datacontext
Created a copy of the listbox and removed all bindings, setting the three text fields manually. This would not disappear, but
showed up every time I beat a level
Beat one level (getting it to work), beat another (getting it to disappear), navigate away from the gamepage.xaml/gamepage.xaml.cs where the gameplay takes place (like to a mainpage.xaml, then back to the gamepage. This does not fix the problem, so I'm assuming the problem is higher up than something inside the gamepage.xaml/gamepage.xaml.cs
I feel like I've got to be doing something painfully stupid/obvious, but I'm a novice programmer, just picking pieces up as I have a need, and this is my first venture into the world of data binding.
I would greatly appreciate any suggestions.
Thanks in advance for your time.
I found the problem. I was wrong when I said I was never leaving the GamePage.xaml.cs and Gamepage.xaml.
I reveiwed my code and found that I was actually jumping out to a transition page that lists the details of the next level, then back to GamePage.
Whenever I left the page, strange things would happen to the App._scoresController.Scores. If I created a private _scoresController.Scores within GamePage.xaml.cs and used that instead of the one in App then everything works. It looks like something strange with Scoreloop.

SelectedItem of SelectedItem

first of all I would like to thank you for the many good posts that i read in this forum. Unluckily I could not find anything of help for my current problem (either here or anywhere else).
What I'm trying to do sounds quite simple, but I have no clue how to get it to work ... perhaps I'm still to new to wpf or I don't thing wpfy enough :)
I'm designing a front end for a part in an automated manufacturing:
I have a quantity of places where pallets can be put (but it can be empty as well).
Each pallet has up to 3 places where parts can be mounted
Everything is created dynamically of a database and is reacting to changes.
The position of the parts on the pallet comes from the database as well and should be visualized
What I would like to have is:
An overview over the pallet-places with a preview of the pallet
When I select a place I want to see a detail view of the place
When I click on a part on the pallet of the detailed pallet I want to see details to the part
The first two points are quite simple and work nicely:
I have got a DataTemplate for every component (part, pallet, pallet-place). Actually those are UserControls that are imported as Datatemplates
the overview is a ListBox with the places as DataContext
for the detail-place-view I use the UserControl and bound it to the SelectedItem of the Listbox
I tried to bind the Text of a Textblock to the ID of the selected Part ... and fail.
Probably I could use some global variables in the code behind - but that sound very ugly.
Can anybody help?
I have got a solution ... it is not nice but works.
I created an event in the pallet, that triggers, when the selected part-place changes
I handle the event in the pallet-place and create a new one
And finally I handle it in the overview and change the detailview accordingly
Most likely there are much nicer solutions, but it will suffice.
Perhaps try an ElementName binding?
<TextBlock Text="{Binding ElementName=Name_of_your_Listbox, Path=SelectedItem.ID" />
Can you post a bit more code of your TextBlock and your Binding?
Context is important, if i use a ContentControl and bind its content to the SelectedItem like this:
<ContentControl Content="{Binding SelectedItem, ElementName=mylistbox}">
I can bind to the ID of the selected item in the DataTemplate like this:
<ContentControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding ID}" />
</DataTemplate>
</ContentControl.ContentTemplate>
That is because setting the Content of the ContentControl automatically sets the DataContext as well, and this binding is relative to the DataContext since no source (ElementName, RelativeSource, Source) has been specified.
I do not know how your UserControl handles the context, if the DataContext is not affected such bindings will not work. You would need to bind directly then:
<uc:MyDetailsView Data="{Binding SelectedItem, ElementName=mylistbox}">
<!-- ... -->
<TextBlock Text="{Binding SelectedItem.ID, ElementName=mylistbox}" />
This of course defeats the purpose of having the binding on the UserControl itself in the first place. But unless you post some relevant code it's quite hard to tell what is wrong.
Also check the Output window in VisualStudio, binding errors will show up there and might provide valuable information as to what went wrong.

Resources