Is a custom usercontrol created each time the datacontext changes? - c#

This is more of a general question...
I have a user control that i've written (UserControl,not Custom Control). i'm using this control in the a DataGridColumn to provide lookup functionality..much like this:
<DataGridTemplate ColumnHeader="Company">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<lookupCtl:LookUpCTL SelectedCompany="{Binding Company, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
CompanyChangedCommand="{Binding DataContext.CompanyChangedCmd, RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
What i'm noticing is that whenever the dataconext changes for the parent/containing control, the constructor of LookUpCTL is called. Is this expected behavior? Anyway to prevent this? as i don't see why this necessary...the single instance of the control should be able to refresh itself from the datacontext i think.
Edit: googling is not providing any clear answers...but from what i've read, it may be the Datagrid that's the issue. since my control is used in a datagrid, each time the grid's itemsource changes, does it destroy and recreate the controls? i can see sense in it doing so..but not sure if that is the reason for what i'm seeing. Assuming it is...is there a way to have the datagrid reuse instances of the usercontrol rather than create new ones when the datagrid's itemsource changes?

It does appear that the reason usercontrols used as DataTemplate columns for a datagrid are destroyed and recreated when the itemsource for the datagrid is changed. in my case the solution was to use a normal grid given that i had always 4 items in my list that was used as the itemsource for the grid. This is not ideal and not a solution for N-item lists..but in my case it vastly improved performance since my usercontrol performed some intensive datbase lookups on initialization to cache data.

Related

Why does this DataGrid mouse input binding work in one project, but not in another?

We are building a WPF application containing a DataGrid, which should call a function on the currently selected row if that row is double-clicked. We are aiming for an MVVM approach where possible, trying to avoid events.
Because I've done something similar for a DataGrid in a previous application, I thought this would work:
<DataGrid.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding ShowDetailsCommand}"/>
</DataGrid.InputBindings>
In this working application, ShowDetailsCommand points to a method which accesses the currently selected DataGrid item via a data binding to SelectedItem.
Trying this same approach in the new project does not seem to work at all. The method which the command points to is never called (tested with a breakpoint and console output), and there is also no error message about the command not being found in the DataContext. We have also tried to move the <InputBindings> block directly into the Window object just to test if the DataContext may have changed further down, but double-clicking still produced no reaction.
As far as I can tell, the only major differences between the two DataGrids are:
The old one's ItemsSource was manually bound to a collection of objects, while the new one's Binding was created by dragging a DataSet onto it in the designer
The DataContext for the old project was a separate ViewModel class, while the new one's is the window's own Codebehind class (DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}", declared on the Window)
Could either of these be the cause for an InputBinding not working? If not, is there anything else we could be doing wrong? Apologies if this is not enough information, I am just very unsure where the problem might be. I will try to supply more information about the code if needed.
Try to bind to the ShowDetailsCommand of the DataContext of the parent window using a RelativeSource:
Command="{Binding DataContext.ShowDetailsCommand,
RelativeSource={RelativeSource AncestorType=Window}}"

IsAsync =true on ComboBox ItemsSource affects SelectedItem?

I've spent hours trying to figure out what is happening to my selecteditem property of my ComboBoxes and Im pretty sure I know what is causing the problem now, but I just dont understand why this is happening and if there is something I can do about it.
Basically I have a DataGrid with comboboxes in the headers that I use for filtering the DataGrid items. The DataGrid sits in a Tabcontrol and if I select items in the comboxes and switch tab and back the selected items are removed.
This happens only when the ItemsSource of the Comboboxes have the IsAsync property set to True. Otherwise everything works just like I want it to.
To give some more info about the logic:
The comboboxes get their values from a IValueConverter its the same converter for all comboboxes and I pass a ConverterParameter to tell it what to return. The collection sent to the converter is the items shown in the datagrid.
These values are refreshed (each item has selected parameters updated, the collection is not cleared and recreated) when the tab is activated.
This my xaml for one of the comboboxes:
<ComboBox SelectedItem="{Binding MadeBy}" ItemsSource="{Binding IssuesView,Converter={StaticResource DataGridFilterableValueConverter}",ConverterParameter=Madeby, IsAsync=true}" SelectionChanged="FilterComboBox_SelectionChanged"/>
Now, like i described previously, if I set the IsAsync to false the selecteditems stay.
Do I just have to live with this? We work alot with sql-databases and IsAsync really makes the interface alot more snappy. I can live without it in the comboboxes, but I tend to use it as much as I can.
I've seen that ppl have been having problems with the selecteditem and the general solution seems to be placing the SelectedItem before the ItemsSource, but this does not help.
Thanks
Erik
Edit 1:
Here is the xaml of the tabcontrol and tabitem.
<TabControl Grid.Row="1" x:Name="TabControl" SelectionChanged="TabControl_SelectionChanged">
<TabItem Header="Granskningssynpunkter" x:Name="IssueTab">
..... Lots of code here but nothing that concerns TabControl or TabItem

Force Refreshing WPF Bindings (one source)

I have this situation:
A IsToolbarButtonsEnabledProperty DependencyProperties
A have plenty of other DependencyProperties in a class (a huge class, needs to be this way)
A serie of Buttons on a toolbar.
The (IsEnabled) property of each of these buttons is a function of (IsToolbarButtonsEnabledProperty) throught a special converter, the buttons a differenced by ConvertParameter ("PreviousButton", "NextButton"...)
Opacity="{Binding IsEnabled, RelativeSource={RelativeSource Self}, Converter={StaticResource OpacityBoolToIntConverter}, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding Path=DPEnableLinks, Converter={StaticResource ToolButtonEnableConverter}, ConverterParameter='ZoomOut' }"
ToolButtonEnableConverter is a converter that compares ConverterParameter "PreviousButton" with other value of other dependency property (in class). I have to many DP to make one multivalueconverter, so I read them straight from my class ((MainWindow)App.Current.MainWindow;)
Questions
When I update other DPs the value isEnabled / Opacity, dont change. How to fix this?
Is there a general solution to make a Binding refresh everytime a DP changes.
(Repeating myself): I will be adding more and more DPs over time, so a MultiValueConverter seams odd.
One way to force the Binding to update is to create a (meaningless) property and add it to the Binding (using MultiBinding), and when you want to update your Binding you change that property, and all the Binding is updated.
I must add that the more "straightforward" way is to use MultiBinding to all the relevant properties. If you have way to many properties that you need to bind, maybe you should re-think if you can build this functionality some other way.

WPF Combobox binding: can't change selection

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.

Updating items inside ListBox

I have a ListBox bound to an observable collection of DiceViewModel. Whenever I click a button to add a new item, the ListBox displays the new item like I expect. Everything so far is working well.
<ListBox
ItemsSource="{Binding Path=AllDice}"
DisplayMemberPath="Value"/>
However, I have another button to roll all existing dice. The items already listed in the box don't get updated, and I'm not sure how to enforce this while keeping to the MVVM design pattern.
Also, my DiceViewModel already implements INotifyPropertyChanged.
Any suggestions?
After some more digging around, here's what I've found. The ObservableCollection doesn't automatically register itself with my DiceViewModel's INotifyPropertyChanged event. So any property changes don't get handled.
However, there is a way to do it in the xaml file:
I added this namespace definition to my Window element.
xmlns:vm="clr-namespace:Rolling.ViewModel"
Then I modified my ListBox to use a DataTemplate with a specified DataType:
<ListBox ItemsSource="{Binding Path=AllDice}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type vm:DiceViewModel}">
<TextBlock Text="{Binding Path=Value}"/>
</DataTemplate>
</ListBox.Resources>
</ListBox>
With the specified DataType, the ObservableCollection could register itself with my collection items, receive their events, and then fire it's own CollectionChanged event.
I hope this helps some other people with this poorly documented feature.
You need to implement the INotifyCollectionChanged interface on the collection that the items are bound to and then have it fire the CollectionChanged event to indicate that the collection changed.
This will cause a refresh of the entire list.
In the case of ObservableCollection INotifyPropertyChanged will only notify on changes to the structure of the collection, generally this is the addition and removal of items. The collection has no knowledge of changes to the properties of an individual item within the collection. Instead that individual item itself is responsible for sending notification of its properties changing.
The reasoning behind this goes back to class responsibility and separation of concerns. Since the DiceViewModel likely has data related rolling a die and the value of its last roll then it would follow that it would send notification when its own properties change.

Categories

Resources