Binding TabControl ItemsSource to Collection of ViewModels - wpf

For some reason I am having a heck of a time getting my TabControl to display properly when binding the ItemsSource to a ObservableCollection of view models. I'm basing my design off of the tutorial found here: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx. I did find a few questions like mine here but none addressed my particular situation.
This is my TabControl in xaml.
<TabControl ItemsSource="{Binding Workspaces}"
SelectedIndex="{Binding ActiveWorkspaceIndex}"
ItemTemplate="{StaticResource ClosableTabItemTemplate}"/>
Where ClosableTabItemTemplate is the following.
<DataTemplate x:Key="ClosableTabItemTemplate">
<DockPanel Width="120">
<Button
Command="{Binding Path=CloseCommand}"
Content="X"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
FontFamily="Courier"
FontSize="9"
FontWeight="Bold"
Margin="0,1,0,0"
Padding="0"
VerticalContentAlignment="Bottom"
Width="16" Height="16"
/>
<ContentPresenter
Content="{Binding Path=DisplayName}"
VerticalAlignment="Center"
/>
</DockPanel>
</DataTemplate>
Workspaces is the ObservableCollection of view models. ActiveWorkspaceIndex is just the active workspace index that I keep track of in the view model. I associate my view model with an instance of a view through the following data template in my App.xaml file.
<DataTemplate DataType="{x:Type vm:ViewModelStartPage}">
<v:ViewStartPage/>
</DataTemplate>
I only add one view model to my collection of workspaces. I see 2 views display in the tab control and they aren't tabbed. It's almost like the TabControl doesnt know to treat the different views as TabItems, its behaving more like a stack panel, stacking the views. If I create the tab items in code it works fine like this:
System.Windows.Controls.TabItem i = new System.Windows.Controls.TabItem();
i.Content = new Views.ViewStartPage();
i.Header = "A Tab Item";
this.xTabControl.Items.Add(i);
I must be missing some content template or something. I will be styling my tabs later but for now I would be happy just getting the basic tabs working. Also the views in the tab contents may be different for each tab so I can't use the simple textblock TabControl template examples I see all over the place...
I.e. not this...
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Content}" />
</DataTemplate>
Any ideas?

I ended up using a ContentControl with a TabControl data template (like the original tutorial project). Here is the xaml code I ended up with. I did not change the code-behind from the original sample project to make this work. The ContentControl is in my MainWindow.xaml and the other two pieces of code were in a ResourceDictionary.
<!-- Workspaces Tab Control -->
<ContentControl Grid.Row="1"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource WorkspacesTemplate}"/>
<!-- Workspaces Template -->
<DataTemplate x:Key="WorkspacesTemplate">
<TabControl Style="{StaticResource ClosableTabControl}"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource WorkspaceTabItemTemplate}"
Margin="1"/>
</DataTemplate>
<!-- Workspace Tab Item Template -->
<DataTemplate x:Key="WorkspaceTabItemTemplate">
<Grid Width="Auto" Height="Auto">
<ContentPresenter ContentSource="Header" Margin="3"
Content="{Binding Path=DisplayName}"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource Foreground}"/>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Grid>
</DataTemplate>

Related

Customizable content inside DataTemplate

I have the following data template for a TemplateGridViewModel. It renders a paginated grid of items where the pages can be navigable.
I want to be able to use the template with different items. Currently it just displays a label.
<DataTemplate DataType="{x:Type vm:TemplateGridViewModel}">
<Border BorderBrush="Black" BorderThickness="0.5" Margin="10 20">
<ItemsControl ItemsSource="{Binding CurrentPage}" VerticalAlignment="Stretch">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4" Rows="2"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="0.5">
<Label Content="{Binding ManagedTemplateId}"/>
<!-- What I want is:
<ContentControl Content="{Binding Content}"/>
-->
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</DataTemplate>
I imagine that I could display my interchangeable content by replacing the label with <ContentControl Content="{Binding Content}"/> (or even just <ContentPresenter/>?).
And then it would be used like this:
<ContentControl Content="{Binding IndexViewModel}">
<!--Interchangeable content here!-->
</ContentControl>
But ah, you see the problem! Content is already defined, it's the view model which is what binds us to this template in the first place!
How can I present a viewmodel via datatemplate and use interchangeable content?
When you bind an ItemsControl it sets the view model of each visual list item to the corresponding collection item, so replace the label with this:
<ContentControl Content="{Binding}" />
Then add DataTemplates for the types that are in your CurrentPage collection.

Border that bulges near mouse pointer

I have a ListView populated with ListViewItems, like navigation bar style menu options.
I would like to create the effect of the ListViewItem border "bulging" like in the Mail application on Win10 as shown in this picture
The border "bulges" (fisheye?) as the mouse moves left and right along the ListViewItem. It's a nice effect that I'd like to replicate but am struggling to even get started on how to tackle this.
My list items are bound to a content control which is templated with a ControlTemplate. The ItemsControl ItemTemplate is a DataTemplate containing an image and a Textbox wrapped inside a button control, like this .... (although I suspect how it's created is probably not relevant to my question)
<ContentControl Content="{Binding Path=MenuModel.AllMenuItems}"
x:Name="menuCtrl1"
Template="{StaticResource MenuListItemControlTemplate}"
VerticalAlignment="Top" />
...
<ControlTemplate x:Key="MenuListItemControlTemplate" TargetType="ContentControl">
<Border Background="Transparent" >
<ScrollViewer Margin="{StaticResource DefaultNoMargin}" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{TemplateBinding Content}"
ItemTemplate="{StaticResource MenuListItemDataTemplate}" />
</ScrollViewer>
</Border>
</ControlTemplate>
...
<DataTemplate x:Key="MenuListItemDataTemplate" DataType="local:SingleMenuItem">
<Button Command="{Binding MenuCommand}" Style="{StaticResource MenuButtonStyle}" >
<StackPanel Orientation="Horizontal" >
<Image Source="{Binding MenuIconSource}" Style="{StaticResource MenuItemImage}" />
<TextBox Text="{Binding DisplayText}" Style="{StaticResource MenuItemStyle}" />
</StackPanel>
</Button>
</DataTemplate>
Any tips on how to achieve it or even where to start would be gratefully received.

Caliburn unable to load usercontrol as ActiveView inside Telerik's RadPane

I have recently moved ContentControl on the View(xaml) inside a telerik RadPane like so:
<telerik:RadDocking.DocumentHost>
<telerik:RadSplitContainer Visibility="{Binding UserControlVisible}">
<telerik:RadPaneGroup>
<telerik:RadPane CanUserClose="False" Header="{Binding Operation}">
<ContentControl x:Name="ActiveItem" Margin="10" VerticalAlignment="Top" />
</telerik:RadPane>
</telerik:RadPaneGroup>
</telerik:RadSplitContainer>
</telerik:RadDocking.DocumentHost>
Since, I have done this my UserControls are not injected as the Content inside ContentControl. I have tried to explicitly bind Content Property on ContentControl to ActiveItem but that says, unable to find the associated view.
Any help will be much appreciated.
try to add a datatemplate to the current content:
<DataTemplate>
<ContentControl cal:View.Model="{Binding}" />
</DataTemplate>
Where cal is:
xmlns:cal="http://www.caliburnproject.org"
and bind the active item explicitly.
Now it looks like
<telerik:RadDocking.DocumentHost>
<telerik:RadSplitContainer Visibility="{Binding UserControlVisible}">
<telerik:RadPaneGroup>
<telerik:RadPane CanUserClose="False" Header="{Binding Operation}">
<ContentControl x:Name="ActiveItem" Margin="10" VerticalAlignment="Top" Content="{Binding ActiveItem}">
<ContentControl.ContentTemplate>
<DataTemplate>
<ContentControl cal:View.Model="{Binding}" />
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</telerik:RadPane>
</telerik:RadPaneGroup>
</telerik:RadSplitContainer>
</telerik:RadDocking.DocumentHost>

WPF Nested Databinding; How Do I Bind To An Item Inside Another Item

I have a ListView bound to an ObservableCollection of CustomerContacts.
It works great so far, but being new to this, I'm not sure how to do the next part.
Each customer contact has several contact types, which I want to display under their name.
So inside of CustomerContacts I have another ObservableCollection of ContactTypes.
Here is my current datatemplate:
<DataTemplate x:Key="iconTemplate">
<DockPanel Height="133" Width="150">
<Image Source="/Tracks%203.5;component/Images/NoPic.png" Height="25" Width="25" Margin="1,0" />
<TextBlock DockPanel.Dock="Top" Text="{Binding FullName}" Margin="5,3,5,0" FontWeight="Bold" HorizontalAlignment="Left" />
<<TextBlock Text="{Binding Title}" Margin="5,0,5,3" HorizontalAlignment="Left" />>
</DockPanel>
</DataTemplate>
And here's my first attempt at putting the listview inside:
<DataTemplate x:Key="iconTemplate">
<DockPanel Height="133" Width="150">
<Image Source="/Tracks%203.5;component/Images/NoPic.png" Height="25" Width="25" Margin="1,0" />
<TextBlock DockPanel.Dock="Top" Text="{Binding FullName}" Margin="5,3,5,0" FontWeight="Bold" HorizontalAlignment="Left" />
<ListView ItemsSource="{Binding ContactTypes}">
<ListView.Template>
<ControlTemplate TargetType="ItemsControl">
<ItemsPresenter/>
</ControlTemplate>
</ListView.Template>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Margin="3,0,0,0" HorizontalAlignment="Center" Text="{Binding Path=ContactType}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!--<TextBlock Text="{Binding Title}" Margin="5,0,5,3" HorizontalAlignment="Left" />-->
</DockPanel>
</DataTemplate>
I want to replace the TextBlock bound to Title with a ListView/ListBox/ItemsControl bound to the items in ContactTypes.
Somewhat similar to this question: < WPF: bind to a List inside a class > but without all the codebehind. It would be nice to have it in the XAML.
There's a couple things I would do here:
Simplify what you're trying to do in order to isolate the problem. Instead of that whole ListView business, just put in there an ItemsControl: <ItemsControl ItemsSource="{Binding ContactTypes}" /> That'll give you the default view of things, which may just show you the data type of your objects (whatever ContactTypes is), but it'll let you know if the binding is working. If it is, you'll get something listed there. If it isn't, you won't.
If you're not getting anything listed, go use Snoop to drill into it and look for errors. Snoop shows the databinding errors, allows you to inspect the DataContext of each of the items, etc.
If you're still having problems, it might benefit us if you posted your class definitions and your code where you wire things up. There might be some other underlying issues (for instance, is the ContactTypes property null when the binding initially occurs and you're not using INPC to let the binding system know when it changes?).

How can I create a silverlight combobox that drops down a treeview?

I'm trying to create a user control that is a combobox that, when opened, presents a treeview of heirarchal data.
I created the user control and replaced a portion of the template in Popup with:
<ScrollViewer x:Name="ScrollViewer" BorderThickness="0" Padding="1">
<sdk:TreeView x:Name="Tree">
</sdk:TreeView>
</ScrollViewer>
However, I'm not sure how to enable binding on this. The treeview needs to be bound to a different datacontext than the combobox. I tried implementing a DependencyProperty on the user control that would allow me to set the datacontext, but I'm definitely not going about it the right way. At this point, all I get is an empty treeview.
Any help on this would be greatly appreciated.
P.S. One additional caveat is that I need to template the treeview like so:
<sdk:TreeView x:Name="Tree">
<sdk:TreeView.ItemTemplate>
<sdk:HierarchicalDataTemplate ItemsSource="{Binding ChildUnits}">
<StackPanel Orientation="Vertical" Width="200">
<TextBlock x:Name="name" TextWrapping="Wrap" Text="{Binding Name}" FontWeight="Bold" />
<TextBlock x:Name="type" Text="{Binding Id}" FontStyle="Italic" FontSize="10" Foreground="Gray" />
</StackPanel>
</sdk:HierarchicalDataTemplate>
</sdk:TreeView.ItemTemplate>
</sdk:TreeView>

Resources