Selecting TabItem from code-behind when tabs use a DataTemplate - wpf

I've got a TabControl in my WPF application. The TabControl's ItemsSource is bound to an ObservableCollction of view objects. It uses a DataTemplate to populate the visual tree for the tabs from the Items in the collection.
I need to be select the current tabs in the conde-behind in response to actions the user takes on another screen in the application. When I iterate over the items in the TabControl's Items collection, I get the instances of my view models.
How do I access the actual TabItems and iterate over them, then select the one I want?
Tony

If you're using a MVVM approach you should bind your TabControl's SelectedItem property to the same object that holds your ObservableCollection of TabItems (the ViewModel). When you need to change the current tab set the SelectedItem property to the correct TabItem in the ObservableCollection.

Related

How to get current item index of ItemsControls in viewmodel?

How to get current item index of ItemsControls in viewmodel?
We want to delete the selected textbox item using ctrl+D in the itemscontrol from the viewmodel
As #metacircle has mentioned, you should use a ListBox, which has this functionality built in, instead of your ItemsControl. Using a ListBox, you have a number of options for accessing the selected item:
SelectedIndex Property
SelectedItem Property
SelectedValue Property
You can find code examples on these pages on MSDN and further examples in the ListBox Class page.
ItemsControl does not have the concept of "current item" nor "current index".
You're looking for a Selector-derived control, such as ListBox

Any ideas how to implement ComboBox to represent hierarchical data?

Does anybody saw control like this somewhere?
I need to make such control to represent hierarchical data (it should be generic very likely, i.e. data binding, templates support).
Something like combination of ComboBox and MenuItem’s.
I think I will redefine the combobox itemtemplate with some hierarchicaldatatemplate along with popup class.
Just put ComboBoxes on a form and bind the ItemsSource to the top level collection.
Then bind the DataContext of the next ComboBox to the SelectedItem of the box on the left and bind its ItemSource to the collection of items.
You know how to bind to SelectedItem?
E.G.
Column1
Public String Name
Public List Column2s
So you bind the first combox to List with he displaymemberpath = name
Then on the second combobox you bind to Column1 selecteditem with items source path of Column2s
The trick is to build up the Lists within the Lists within the Lists
All right, I made it by custom control inherited from ComboBox, custom ComboBoxItem inherited from HeaderedItemsControl and using HierarchicalDataTemplate.

Databinding in the same Control on 2 different DataContext

In Silverlight I've a page with some controls and a listbox.
I'm using MVVM and the dataContext of the listbox is defined like this. In my model I have a property ProductCommand and this ProductCommand object contains a list of product named Products.
My listbox is in a grid with the datacontext defined as the ProductCommand property. and the databinding for the listbox is set to the Products (Binding="{Product, Mode=twoWay}").
In my model class I also have a selectedProduct property, and I want to bound it to the SelectedItem property of the listbox.
How can I do that?
I have had similar problems I found this blog article by Dan Wahlin on a Data Context Proxy very helpful.
Of course in Silverlight 5 ancestor binding will also provide you a means to solve this issue.

Where should I put list of UserControls and not break MVVM?

I have a tab control where each TabItem is a UserControl. I'd like to hold the UserControls in the TabControl's ItemsSource. Does ItemsSource list go in the Window's ViewModel? If so, I feel like it's breaking MVVM since the ViewModel would now have GUI controls within it. Or do I put this list in the codebehind of the window that holds the tab control?
Any suggestions would be great!
With tab controls, more often than not the individual tabs are created statically in XAML rather than at run time by data binding. However there is no reason why you shouldn't do this. If you have a collection of views, they should definitely be stored in a view.
Bear in mind that you could also bind the ItemsSource to a list of ViewModels objects and WPF will generate a view for you with the ItemTemplate, with the ViewModel object set as the DataContext. This collection of ViewModels should be stored in a view model, although at some point a view model will obviously have to be stored in a view.
This can most likely be done in a number of ways, all which are up for debate on how "MVVM-friendly" they are.
My setup looks like the following.
My Main window has a DataContext bound to a MainWindowViewModel which contains an property
public ObservableCollection<Workspace> WorkspaceCollection{get;set;}
MainWindow has a TabControl which ItemsSource is bound to WorkspaceCollection
Workspace are all viewmodels and are bound to different views/usercontrols via DataTemplates
You might have a look at the Write sample application of the WPF Application Framework (WAF). It has a TabControl where each TabItem is a UserControl and it does this by applying the MVVM pattern.
Here's what I've done.
I created an interface that all of my controls implement, IMyAppControl, which has some information such as Title, Description, other metadata.
My Main Window has an ObservableCollection that the tab ItemsSource binds to.

Where is IsSynchronizedWithCurrentItem property (or equivalent) for a TreeView?

Tell me it ain't so.
I have a typical windows/file explorer like setup.
Left Side I have a TreeView all data bound showing nodes in a hierachy
Right Side I have a ListView showing Node.Properties
ListView has a IsSynchronizedWithCurrentItem property, which rocks. e.g. If I had another ListView showing a list of nodes and both listViews have this property set to true. Changing selection of node in NodesListView will update the PropertiesListView automatically.
Now I need the same thing with a NodesTreeView and a PropertiesListView... and seems like TreeView has no such property.
Is there a more 'the WPF way' kind of solution to this problem ? Or do I have to handle the NodeSelectionChanged event of the Tree and refresh the listView via code.
A really simple solution is to bind your "details" UI elements to the SelectedValue property of the TreeView. For example, if your TreeView looked like this:
<TreeView Name="CategoryName" ItemsSource="{Binding Source={StaticResource A_Collection}, Path=RootItems}" />
Then you could bind details UI elements (like a textbox) using:
<TextBox Text="{Binding ElementName=CategoryTreeView, Path=SelectedValue.Name}"/>
Would cause the text box to be bound to Name property of the items currently selected in the TreeView.
If you want to bind many UI items as details for the selected TreeView item, consider setting up a DataContext on the elemtent that contains all the details controls (DockPanel / Grid / StackPanel, etc).
<ListView Name="listView1"
ItemsSource="{Binding Path=SelectedItem.Modules,
ElementName=treeView1, Mode=OneWay}"
IsSynchronizedWithCurrentItem="True">
Where ".Modules" is the collection of child items off the selected treeview item you want to display. Don't worry about wiring up the "SelectedItemChanged" event on the treeview.
Why exactly it doesn't implement the property, I do not know, but i have a suggestion down below.
Your code above will work, however, it is not what the IsSynchronizedWithCurrentItem property does. Any ItemsControl binds to the ICollectionView of the ItemsSource property. To get that ICollectionView, we can call CollectionViewSource.GetDefaultCollectionView(object o). Depending on the type of object o, you get a different concrete implementation of the ICollectionView inteface. CollectionView and ListCollectionView are two concrete classes that come to mind.
The ICollectionView interface contains a member called CurrentItem. What the IsSynchronizedWithCurrentItem does is: whenever an item is clicked on the ItemsControl, it sets the CurrentItem for the collection view. The ICollectionView also has two events: CurrentItemChanging and CurrentItemChanged. When the IsSynchronizedWithCurrentItem property is set, the ItemsControl will update the SelectedItem based on what the ICollectionView's CurrentItem is. Makes sense?
In master/detail WPF scenarios, we simply are binding to ICollectionViews and their CurrentItem (the CurrentItem syntax is something like {Binding Items/Name}, where Name is the Name property on the collection's CurrentItem.
So although your code works for your purposes, it doesn't do what that property does. To do what the property does, you need to do the following:
When an item is selected, you need to figure out which collection it belongs to. How do we do this? I believe this is why TreeView doesn't implement it. The selected item is displayed in a TreeViewItem. The DataContext is the object itself, but what is the parent collection ? I guess to get it you could either cache it in some hashmap (silly, but will work) or you can walk up the logical tree and get the TreeViewItem's parent that happens to be an ItemsControl. The ItemsSource property will be your collection.
Get the ICollectionView for that collection.
Need to cast that ICollectionView into a CollectionView (ICollectionView doesn't implement CurrentItem setter)
Call SetCurrent(.. , ..) on the CollectionView instance
Now, anything that is bound to that ICollectionView's CurrentItem will be updated.
This became longer than I expected. Let me know if any further clarification is necesary.
My solution to this turned out to be pretty tiny.. don't know if it is equivalent to IsSynchronizedWithCurrentItem. ListView refreshes as expected.
// the XAML
<TreeView DockPanel.Dock="Left" x:Name="tvwNodes" ItemsSource="{Binding}" SelectedItemChanged="OnNewNodeSelected"/>
<ListView x:Name="lvwProperties" ItemsSource="{Binding Path=Properties}"
// the code-behind
private void OnNewNodeSelected(object sender, RoutedPropertyChangedEventArgs<object> e)
{
lvwProperties.DataContext = tvwNodes.SelectedItem; // this returns the selected Node obj
}

Resources