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.
Related
I am doing a tree view UI. I used DataTemplateSelector to decide whether to display a series of textbox or combobox dynamically based on a collection of data argument.
Please note in my code. ArugumentDetailsCollection is an observable collection containing ArgumentDetails class. DefaultValue is a string property in ArgumentDetails class. Please note the property is not dependency property
The problem is that DefaultValue is not bind to TextBox. When the TextBox is displayed, it contains empty string.
Please note the Textbox is working well if data template selector is not used.
Please can someone advice ? thank you
<ItemsControl x:Name="argumentTexts" ItemsSource="{Binding ArgumentDetailsCollection}">
<ItemsControl.Resources>
<DataTemplate x:Key="TextBoxDataTemplate">
<TextBox Text="{Binding Path=DefaultValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
VerticalAlignment="Center"
Width="Auto"
Margin="5,0,0,0"
Padding="0"
Style="{StaticResource GridEditStyle}"
IsEnabled="True"/>
</DataTemplate>
<DataTemplate x:Key="ComboBoxDataTemplate">
<ComboBox HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Width="Auto"
Margin="5,0,0,0"
Padding="0"
Style="{StaticResource GridEditStyle}"
IsEnabled="True"/>
</DataTemplate>
<columnConfiguratorControls:ArgumentTypeTemplateSelector x:Key="ArgTemplateSelector" ComboBoxDataTemplate="{StaticResource ComboBoxDataTemplate}" TextBoxDataTemplate="{StaticResource TextBoxDataTemplate}"/>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Stretch" IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type structures:ArgumentDetails}">
<ContentControl Content="{Binding VisibleName}"
ContentTemplateSelector="{StaticResource ArgTemplateSelector}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Writing Content="{Binding VisibleName}" in your ContentControl will make the datacontext of the selected DataTemplate the VisibleName property. That's why you cannot access the DefaultValue property, since it's member of ArgumentDetails.
Change the binding to :
Content="{Binding}"
You will also need to review your ContentTemplateSelector class
I'm wanting to override ItemsControl, such that it doesn't create a container for each item, but presents them as is.
Is this possible? either through a new derived type or xaml?
Edit : To further explain, I'm wanting to bind the Canvas attached properties in the data template, as opposed to doing it in the ItemContainerStyle, but for that to work, there would have to be no container, .. such that the Canvas looks directly at the Ellipse/Rectangle.
The reason being, that the values i want to bind to may not always have the same name, as in this case, the Left property is 'Left' for types of A, but 'X' for types of B.
Im aware that i can do this with Priority bindings in the ItemContainerStyle, but would prefer to set the bindings in the data template.
Is it possible?
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:A}">
<Ellipse Width="25" Height="25" Fill="Red"
Canvas.Left="{Binding Left}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:B}">
<Rectangle Width="25" Height="25" Fill="Green"
Canvas.Left="{Binding X}"/>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
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>
I have an ItemsControl using a StackPanel to display a list of items.
I would like a label to appear for each row, but for the content to the left of the label to be defined by a DataTemplateSelector. I do not want to redefine the label for each DataTemplate generated by the TemplateSelector.
Is this possible?
<ItemsControl ItemsSource="{Binding Path=Values}" >
<ItemsControl.Resources>
<v:MyTemplateSelector x:Key="myTemplateSelector"></v:MyTemplateSelector>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<WrapPanel>
<Label>Test: </Label>
<!--What goes here should be defined by myTemplateSelector-->
</WrapPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I figured it out. The solution was to use a ContentPresenter element with a ContentTemplateSelector attribute:
<DataTemplate>
<WrapPanel>
<Label>Test: </Label>
<ContentPresenter
ContentTemplateSelector="{StaticResource ResourceKey=myTemplateSelector}">
</ContentPresenter>
</WrapPanel>
</DataTemplate>
In WPF Listbox, I'm confused with these 2 notions:
ItemTemplate and ItemsPanelTemplate
Can someone explain me more?
Thanks
John
Let me try to explain this by example:
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Background="SteelBlue" Padding="5" BorderBrush="Black"
BorderThickness="1" Margin="0,2">
<TextBlock Text="{Binding}"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="DarkKhaki"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
And the result:
The ItemTemplate determines the layout of each item in the list. On the other hand the ItemsPanel is the panel that will contain the individual items. Given the above definition the visual tree will be something similar to this:
<StackPanel>
<Border>
<TextBlock Text="Alpha"/>
</Border>
<Border>
<TextBlock Text="Beta"/>
</Border>
....
</StackPanel>
ItemTemplate is used to specify a DataTemplate used to render the item in your ListBox.
ItemPanelTemplate is used to specify the panel used to arrange the children of your ListBox.
For example, if your ListBox is bound to an ObservableCollection you must specify a DataTemplate to tell it how to render each Person object.
<ListBox ItemsSource={Binding Persons}>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text={Binding FirstName}/>
<TextBlock Text={Binding LastName}/>
<TextBlock Text={Binding Age}/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
That will arrange each item vertically because ListBox used a StackPanel by default. If you want to change this behaviour, used the ItemPanelTemplate property:
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
You can even change the StackPanel to any other panel (WrapPanel for example).