In a MVVM WPF application, I have a master-detail View that is used to manage Alarms. In the AlarmDetails View I have several properties that are set through ComboBox controls. One of those is the Alarm's Source, and those sources can be changed in another View (SourcesView), thus changing the combobox content in the AlarmDetailsView.
The way the data flows is relatively straightforward
A source is added to the collection in SourcesView
A message is sent from SourcesView and is received by all AlarmDetailsViewModel
The Sources collection in each AlarmDetailsViewModel is changed accordingly and since it is an ObservableCollection it notifies each binding targets of the change
The AlarmDetailsView has its DataContext set to a selected AlarmDetailsViewModel, thus the ItemsSource property of its Sources ComboBox is updated (I see it using a dummy converter, in debug I can see the updated collection being passed to ItemsSource, and it contains the newly added item)
After all this happened, when I navigate to the AlarmsView/AlarmDetailsView Tab, the items in the Sources ComboBox do not contain the new item.
When I select a new Alarm in the master list and I select the original one back, the combobox displays the new item just fine (when i select an item in the master list, the DataContext property of the AlarmDetailsView gets updated).
I am at a loss with this, I have been poundering this for a while.
Anyone have an idea? Any debugging suggestions?
Thanks,
Alex
EDIT: The ComboBox that does not update is located within the AlarmDetailsView, which means that it is used to set a property of the Alarm object (its Source, namely). Hope this helps.
EDIT 2: In addition to the answer below, another solution was to use an ObservableCollection instead of a List. For some reason, raising a PropertyChange event on the List (as described above) triggered the ComboBox to refresh its ItemsSource, but not to add the new items to its drop-down. Using the ObservableCollection, there is not need to raise the propertychange event since it manages it all by itself.
** EDIT **
Ugly solution but worked for me if someone has a better solution please share :)
Emptied the collection, notified the GUI, added the real collection notified to gui
Try using CollectionViewSource to handle your Master/Detail bindings.
Heres an example
<Window.Resources>
<CollectionViewSource x:Key="data" Source="{Binding}" />
<Window.Resources>
DataContext="{Binding CurrentItem, Source={StaticResource data}}"
Related
PersonsViewModel has a corresponding DataTemplate with a DataGrid bound to PersonList. DataGrid.SelectedItem has two-way databinding to SelectedPerson, so that when I select a row in the view, the corresponding item from PersonList is assigned to SelectedPerson.
It works fine except for one problem: when I switch screens, say, to PersonDetailScreen, and come back, the selection focus is lost! I mean, SelectedPerson still contains its former value, but the DataGrid comes out visually unselected.
I made a test, creating a two-way databound "SelectedIndex" in viewmodel. Then, I can see the actual selection is still present in viewmodel when it comes back, the problem seems to be:
"How to focus the SelectedIndex of an ItemsControl when ViewModel's datatemplate is loaded and some "SelectedIndex" databound property in such viewmodel already contains a value?"
If you have a TwoWay Binding then you can set the DataGrid.SelectedItem value from your view model. I'm a little bit confused as to your set up though... when you say 'when I switch screens ... and come back, the selection focus is lost', it sounds a bit like you're keeping the view model alive, but not the view? I'm more used to displaying a fresh view each time, but the fix would be the same either way.
Now if this were one of my views, I'd load the data into any collections from the constructor and (internally in a base class) set the CurrentItem property that is data bound to the ListBox.SelectedItem property to the first item in the collection. You should do the same, except that instead of setting it to the first item, you'd set it to whichever item was last selected.
So the answer is just to set the SelectedItem property each time the view is displayed. However, if you're saying that the SelectedItem property is already set to the item that should be selected, you may need to set it to any other value first and then set it back to the correct item.
What are we talking about here, item selection or item focus?
If you want a visual item to get focus when a template is rendered, your best bet is to set the focus manually in your xaml's code behind, by, say, hooking a handler to your page's 'Loaded' event and setting the focus manually to your datagrid by calling on its 'Focus()' method.
I know this breaks some MVVM rules, but focus management is highly dependent on your page's visual tree, and thus cannot be properly modeled through a viewmodel, which should be as view-independent as possible.
Thanks to Sheridan's insights, I have solved the problem easier than I imagined, and somewhat unintentionally.
I had the DataGrid defined directly in a DataTemplate. When the template loaded, I BELIEVE, although I am not sure at all, that some initialization step required to pass the "SelectedItem" or "SelectedItem" value to the View was "lost".
I planned to do this re-selection manually in code behind, so what I did was to move the datagrid to some UserControl I created from scratch. But then, the very fact that the DataTemplate now instantiates a proper View (derived from UserControl) inside itself, which in turn contains the datagrid, seems to "make more notifications work", so to say, and the View displays selected row like it always should.
So, now I have this in my DataTemplate:
<DataTemplate x:Name="PersonScreenTemplate" DataType="{x:Type vm:PersonScreenViewModel}">
<vw:PersonScreenViewView/>
</DataTemplate>
That seems to be the perfect pure-WPF design pattern for ViewModel-first: a datatemplate whose content is a single usercontrol, which in turn declares and binds and handles everything.
I am new to WPF. I want to delete row from datagrid runtime.When I try to delete row like this
Datagrid.Items.Remove(eRow);
It gives me an error " The error is: Operation is not valid while ItemsSource is in use. Access and modify elements with ItemsControl.ItemsSource instead."
I read online that you could use ObservationCollection and InotifyPropertyChangedEvent but I dont know how to implement it.
I have remove button like this
This is datagrid
<ctrls:RhinoDataGrid x:Name="dataGrid" Grid.Row="1" Margin="5" ItemsSource="{Binding Model.CurrentDataTable}"
Style="{StaticResource RhinoDataGridBaseStyle}" IsReadOnly="{Binding Model.IsLinkFile}"
SelectedValue="{Binding Model.CurrentDataRow}" SelectedValuePath="Row"
>
</ctrls:RhinoDataGrid>
Pls help me. Thanks.
Your DataGrid's ItemsSource has a binding on Model.CurrentDataTable. If you want to delete a row, you will have to delete the item in that collection. But the DataGrid won't notice that modification, if the collection does not implement INotifyCollectionChanged.
.NET has a builtin ObservableCollection, which implements INotifyCollectionChanged. If you use this collection type for CurrentDataTable, the DataGrid will update itself, when the collection is modified.
WPF Data binding means that you very rarely manipulate the user interface directly. What you're trying to do is directly remove the row from the grid control on the UI, which is how you might have approached things in Winforms.
With WPF data binding the user interface reacts to the underlying data. So in your case the grid is bound onto (or "watching") the contents of the ItemsSource specified by the binding : Binding Model.CurrentDataTable
To delete a row, you need to remove it from the underlying data and the UI will automatically reflect the change.
This is what ObservableCollection and INotifyPropertyChanged are all about - you really need to read up on them if you're doing WPF development!
I have a WPF Data Grid bound to an observable collection, which is working as intended.
What I am trying to do now is add text below it to say: "Number of selected rows: {count goes here}"
What's the proper way to do this? I could add a new property in the View Model called SelectedCount or something similar and bind to that, but it doesn't feel right. It seems redundant. Also, I could set the label text dynamically in the code behind, but I'm not sure if that's the "right" place to do this either.
Here's an example below.
EDIT:
Please pretend there's a checkbox column header whose intention is to provide check/uncheck all functionality. The state of this header checkbox should not count towards the final count.
You could use element binding to declaratively bind to the SelectedItems.Count property in XAML:
<TextBlock Text="{Binding ElementName=MyDataGrid,
Path=SelectedItems.Count, StringFormat=Number of selected rows: {0}}" />
Update
Presumably you're using MVVM, so adding a SelectedXCount property to your view model is a perfectly reasonable application of the view model. The advantage of having it in the view model is that you could unit test based on the number of selected items. E.g. if you want to check that the user can only progress (a CanNext property returns true) if the user has selected some items.
The SelectedItems property is not a DependencyProperty so can't be bound to, but there are many articles online that get around the issue when using the DataGrid in MVVM. Most of the solutions involve using a mechanism for calling a view model command on the invocation of the DataGrid's SelectionChanged event.
I'm binging DataGrid to ObservableCollection I update collection on timer and grid updates nicely which is what I want.
However, even though I can sort collection on initial population - when I append new items they go to bottom. User can sort grid and then it looks good.
I would like to "force" grid to sort by specific column when it is initialized. Can it be done in XAML or somehow in MVVM manner?
EDIT
Just to give more details. This is mail client main screen. I've got service that polls for data and if new mail arrived - it get's appended to collection. Sorting at this point is not desirable for couple reasons. First, this is list of object. Second, user may decided to sort by other column using DataGrid
It feels right to set grid to sort by column I initially want (Time Received) and let users sort by any column while preserving this order when new items arrive.
You should wrap your ObservableCollection in a collection view. Generally you will wrap the collection in either a CollectionViewSource, or a PagedCollectionView. The CollectionViewSource can be used declaratively in XAML, but can't be controlled from the ViewModel. You can however wrap your collection in a PagedCollectionView, and expose that from your ViewModel. This MSDN article should help you: http://msdn.microsoft.com/en-us/library/dd833072(v=vs.95).aspx. Collection views are very powerful tools, and will help you elegantly bridge the gap between a View and a ViewModel. Hope this helps.
[Edit] Edited the answer to correspond to the solution
You can try adding SortDescriptions by including the Collection in a collectionView. In example.
<CollectionViewSource Source="{Binding ObservableCollection}" x:Key="Data" >
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="City" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
Then in your ItemsSource set the following binding.
ItemsSource={Binding Source={StaticResource Data}}
Just sort inside your ViewModel, where ever you're updating the collection from the timer sort it. In fact when you sort the collection the view should rebind if you're binding correctly in the View/Xaml to the datacontext(the viewmodel).
Anytime you update an Observable collection in a viewmodel anything that's bound to it will be updated.
I am constructing my app infra structure, and finding it hard to achieve a very basic behavior - I want to raise events from different user controls in the system and being able to catch those events on some other user controls that listens to them. For example i have a user control that implements a TreeView. I have another user control that implmements a ListView. Now, i want my ListView to listen to the TreeView, and when the selection is changed on the TreeView, i want to repopulate my ListView accordingly.
I also want this to happen even if the ListView is not located within the TreeView on the WPF logical tree.
PLEASE HELP!!
Thanks,
Oran
Use data binding.
If the content of the list view is stored inside the object shown in the tree view you can just bind into the tree SelectedItem property.
Otherwise bind the tree SelectedItem to a property in your view models (or your window!) and in the setter of this property change the list that is bound to the list view ItemSource property.
You can see the technique in this series on my blog the post I linked to is the last post with the code download link, you'll need to read from the beginning of the series if you want the full explanation.
EDIT: Here's how I did it in one project: (the GridView definition removed since it's not relevant here)
<TreeView
Name="FolderTree"
Width="300"
ItemsSource="{Binding Root.SubFolders}"
ItemTemplate="{StaticResource FolderTemplate}"/>
<ListView
Name="FileView"
ItemsSource="{Binding ElementName=FolderTree, Path=SelectedItem.Files}">
</ListView>
The list bound into the tree view's ItemsSource is of objects that have 3 properties: Name (that is bound to a TextBlock in the FolderTemplate), SubFolders (that is likewise bound to the HierarchicalDataTemplate.ItemsSource property) and Files that is bound to the ListView using {Binding ElementName=FolderTree, Path=SelectedItem.Files}
Note that non of the lists are observable collections (because in this project they never change) but are loaded lazily (on-demand) by the properties getters (because in this project they are expensive to load).
This is the point where the added complexity of MVVM (Model-View-ViewModel pattern) can start to pay off. What you need is a publish/subscribe infrastructure, and MVVM Light has that, along with good MVVM structure that doesn't get overly complex. Prism is another good WPF/Silverlight infrastructure foundation with publish and subscribe support.