I'd like to bind the SelectedItem
of a TabControl
to a corresponding field in my view model, however while still declaring the available items within the TabControl
itself (as opposed to using ItemsSource
) and retrieving their Content
as for the actual SelectedItem
. Meaning: If a tab is being selected, it's Content
should end up as the SelectedItem
(not the TabItem
) and vice-versa.
Sample view model, inheriting a Caliburn.Micro conductor without (!) collection:
public class MyViewModelConductor : Conductor<ConductedViewModelBase> {
public ViewModelA { get; set; }
public ViewModelB { get; set; }
}
And the corresponding TabControl
in XAML:
<TabControl SelectedItem="{Binding ActiveItem}">
<TabItem cal:View.Model="{Binding ViewModelA}">
<TabItem.Header> <!-- vm specific fancy UI stuff --> </TabItem.Header>
</TabItem>
<TabItem cal:View.Model="{Binding ViewModelB}">
<TabItem.Header> <!-- vm specific fancy UI stuff --> </TabItem.Header>
</TabItem>
</TabControl>
I'm aware I could just use e.g. Conductor<'1>.Collection.OneActive
and bind to ItemsSource
, but there are a few reasons I'd like to refrain from doing so:
- The available view models from the conductor's side is a fixed set, declared by the exposed properties, not an infinite collection
- For the UI's side, for each
TabItem
I need to declare a specific header with lots of just UI related stuff (icon, color) which is platform-specific, hence I would not like to leak it into my view models.
I've tried utilizing SelectedValue
and SelectedValuePath
and binding to the TabControl itself (e.g. SelectedValue="{Binding RelativeSource={RelativeSource Self}, Path=SelectedItem}" SelectedValuePath="Content"
), but there WPF tries to look for a Content
property on my bound view models as soon as one becomes selected.
CodePudding user response:
You would have to bind the SelectedValue
to your view model class. Then use the SelectedValuePath
to declare which property of the SelectedItem
is used as the SelectedValue
.
<TabControl SelectedValuePath="Content"
SelectedValue="{Binding ViewModelSelectedContentProperty}">
<TabItem cal:View.Model="{Binding ViewModelA}">
<TabItem.Header> <!-- vm specific fancy UI stuff --> </TabItem.Header>
</TabItem>
<TabItem cal:View.Model="{Binding ViewModelB}">
<TabItem.Header> <!-- vm specific fancy UI stuff --> </TabItem.Header>
</TabItem>
</TabControl>
But I don't recommend this. You should bind the ItemSource
to a set of data models and define DataTemplates to give you a clean design and dynamic/extensible behavior.
Usually you would use DataTemplate
for the TabControl.ContentTemplate
to template the content and TabControl.ItemTemplate
to template the TabItem
(TabControl
header).
Don't hardcode the ItemsControl
items. They're is absolutely no reason to avoid data templating.