I'm confused on this for a long time,these both seem to affect the tabitems' presentation in the tabcontrol.
Is it designed for best control of the presentation of the tabcontrol?
Or if there is something I dont't understand.
There's some very long answers here for what is actually a very simple question. To avoid confusion:
ItemTemplate is the template used to format each item in the ItemsSource to create the headers (the controls that appear in the tab bar) and ContentTemplate is the template used to format each item in the ItemsSource to create the content of the tabs (the controls that appear when you click on the header).
The ItemsControl.ItemTemplate Property is used to define what each item in the data bound collection should look like... from the ItemsControl.ItemTemplate Property page on MSDN:
Gets or sets the DataTemplate used to display each item.
As you can see, it is of type DataTemplate, which is customary for a template that displays data... its DataContext will automatically be set to an item from the collection and so controls declared in that DataTemplate will automatically have access to the items properties. Please see the Data Templating Overview page on MSDN for further help with this.
Similarly, from MSDN, the ContentControl.ContentTemplate Property:
Gets or sets the data template used to display the content of the ContentControl.
Again, its DataContext will automatically be set to the object that is set as the Content property. Please note that the ContentControl only has a ContentTemplate Property and no ItemTemplate Property, which is used for collection items... from the Data Templating Overview page on MSDN:
Because myTaskTemplate is a resource, you can now use it on other controls that have a property that takes a DataTemplate type. As shown above, for ItemsControl objects, such as the ListBox, it is the ItemTemplate property. For ContentControl objects, it is the ContentTemplate property.
UPDATE >>>
To clarify this situation further, think of this simple rule:
Use the ContentTemplate property to define how an object that is set as the Content property of a ContentControl should look.
Use the ItemTemplate property to define how the items of a collection control should look.
That the difference at its simplest. However, I'd like to point out that as these properties are both of type DataTemplate, their values are interchangeable.
For example, let's say that you have a Person class and you display a collection of Person objects in a ListBox. You can declare a DataTemplate to set as the ListBox.ItemTemplate property to define how each Person in the collection should look. However, if you just wanted to display a single Person, then you could use a ContentControl with the Content set to an instance of the Person class, and still use the same DataTemplate, but set as the ContentTemplate instead:
Multiple objects:
<ListBox ItemsSource="{Binding People}" ItemTemplate="{StaticResource Template}" ... />
...
Single object:
<ContentControl Content="{Binding Person}"
ContentTemplate="{StaticResource Template}" ... />
Setting the TabControl.ItemTemplate you specify a template to use for all TabItems in the Items collection of the TabControl, unless you override the TabItem.ContentTemplate for a specific TabItem.
So, while they do the same, TabControl.ItemTemplate is a more generic template for all the TabItems in the TabControl and TabItem.ContentTemplate is specific for the TabItem it is used in.
The above is not quite true, as TabControl has an ItemTemplate property and a ContentTemplate property, to make it more confusing.
ItemTemplate is used as template for the header (the tab thingy) of all TabItems added through databinding on the ItemsSource or through Xaml without making the the added item a TabItem:
<TabControl ItemsSource="{Binding ListOfItems}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="Red"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="Blue"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
This will create a TabControl with red text in the header/tab and blue text for content.
Now, if we do the following:
<TabControl>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="Red"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" Foreground="Blue"/>
</DataTemplate>
</TabControl.ContentTemplate>
<TabItem Header="One" Content="One"/>
<TabItem Header="Two" Content="Two"/>
<TabItem Header="Three" Content="Three"/>
</TabControl>
We'll have a TabControl with three tabs, and the header text is black, content is still blue. And a DataError informing us that the ItemTemplate and ItemTemplateSelector properties are ignored for items already of the ItemsControl's container type, in this case TabItem. In this case, we need to specify TabItem.HeaderTemplate to change the appearance of the header.
So TabControl.ItemTemplate and TabItem.ContentTemplate don't do the same, but my previous explanation still holds for TabControl.ContentTemplate and TabItem.ContentTemplate.
Related
I want to dynamically load user control to a content control,so i used the "Good Old Method" of changing usercontrol based on DataTemplate.
But on setting the ViewModel property i.e binded to ContentControl content property, ContentControl is displaying "text" conatining the respective viewmodel`s name , instead of loading the respective Usercontrol.
Window`s Resources
<dxr:DXRibbonWindow.Resources>
<DataTemplate x:Key="TYRVM" DataType="{x:Type VM:ProductTYRViewModel}">
<views:TYRUserControl/>
</DataTemplate>
<DataTemplate x:Key="THORVM" DataType="{x:Type VM:ProductTHORViewModel}">
<views:THORUserControl/>
</DataTemplate>
<dxr:DXRibbonWindow.Resources>
Window`s code
<ContentControl Name="content" Content="{Binding Path=VMs,Mode=TwoWay}"/>
Now i don`t know what exactly the problem is!!
You have specified a Key on the DataTemplate so the DataTemplate is not automatically applied based on the Type.
This from MSDN:
This property that is very similar to the TargetType property of the Style class. When you set this property to the data type without specifying an x:Key, the DataTemplate gets applied automatically to data objects of that type. Note that when you do that the x:Key is set implicitly. Therefore, if you assign this DataTemplate an x:Key value, you are overriding the implicit x:Key and the DataTemplate would not be applied automatically.
Link to MSDN : DataTemplate.DataType
In our application we have a screen design feature which is comprised of a custom ScreenDesignPanel and a Property Grid with a ComboBox at the top which points to the selected item on the ScreenDesignPanel. This allows the user to select the UIElement via the ComboBox or via the mouse to set its properties. We achieve this by binding the ItemsSource of the ComboBox to the ScreenDesignPanel's Children collection, then binding their SelectedItems together. This works great.
However, for whatever reason, if the SelectedItem is a ContentControl or a subclass like Button the ItemTemplate specified for the ComboBox is ignored for the 'selected item area' but it is applied when displaying the item in the dropdown list. If the SelectedItem is not a ContentControl, the template is used in both cases.
This also is seemingly specific to the ComboBox. If we use any other selector control: ListBox, ListView, ItemsControl... even third-party ComboBox controls... they all work as expected, properly applying the DataTemplate. ComboBox is doing something internally which no other control is doing.
Note: Below is an over-simplified example for illustrative purposes of the issue only. It is not how we're actually using it as described above.
Also of note: In the DataTemplate for the ComboBox.ItemTemplate, we are only using properties (i.e. Foreground in the example), and are not displaying the DataContext (i.e. the actual ContentControl) itself. This is important because again, the actual control already exists on the ScreenDesignPanel and therefore can't be used for display in the ComboBox's ItemTemplate as it would have two parents which isn't allowed. In other words, it is being used purely as data here.
One last thing... we have a working solution in our app, which was to wrap the Children before binding it to the ComboBox.ItemsSource. However, I'm still curious as to why the ComboBox behaves the way it does which is SPECIFICALLY what I'm asking. (In other words, I'm not looking for other solutions to this design. We already have a working one. I'm looking for clarity on the odd behavior of the ComboBox itself.)
On to the code!
In the first example below, note how the data template is applied to everything in the dropdown, but the selected item area only uses a template if the selected item is not a ContentControl.
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="I am the template" Foreground="{Binding Foreground}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<!-- Four 'Data' items for example only -->
<TextBlock Text="I am a Red TextBox" Foreground="Red"/>
<ListBox Foreground="Purple">
<ListBoxItem>I am a Purple ListBox</ListBoxItem>
</ListBox>
<ContentControl Content="I am a Blue ContentControl" Foreground="Blue" />
<Button Content="I am a Button with Green text" Foreground="Green" />
</ComboBox>
This second example shows that it is completely acceptable and fully supported to use a UIElement as the content of a ContentPresenter and still use a DataTemplate (via ContentTemplate) so you can use it in a purely-data role, allowing the template itself to define the visual appearance without displaying the UIElement itself, which is used purely as data here.
<ContentPresenter>
<ContentPresenter.ContentTemplate>
<DataTemplate>
<TextBlock Text="I am the ContentTemplate" Foreground="{Binding Foreground}" />
</DataTemplate>
</ContentPresenter.ContentTemplate>
<ContentPresenter.Content>
<Button Content="I am the button" Foreground="Green" />
</ContentPresenter.Content>
</ContentPresenter>
Again, the issue is specific to a ComboBox. I want to find out why the data template isn't applied in that single case, and how to force it to be applied, if possible.
Of note, ComboBox does define SelectionBoxItemTemplate which is separate from the regular ItemTemplate but the rub is that is read-only so you can't set it. We really don't want to re-template the ComboBox as that can mess up proper theming.
Have you tried explicitly setting the DataTemplate to the ContentControl.ContentTemplate property?:
<UserControl.Resources>
<DataTemplate x:Key="DataTemplate">
<TextBlock Text="{Binding Content,
StringFormat='Displayed via template: {0}'}" />
</DataTemplate>
</UserControl.Resources>
...
<ContentControl Content="ContentControl"
ContentTemplate="{StaticResource DataTemplate}" />
I have been trying to do this for ages and having no joy whatsoever.
I have a ribbon window of the following hierarchy:
MainWindow
MainContent (Tab Control)
TabContainerViewModel
ViewModelBase
View model base has an ObservableCollection of tabs type ViewModelBase. The Tab Control itself is binding fine to these, displaying appropriate view models. I have 2 problems however, I want a "NotepadView" (Of type viewmodelbase) to be replicated numerous times (one view many view models).
At the minute, I have 4 views (NotePadViewModelx where x is 1-4) with corresponding viewmodels, this is because each view had the same text per tab. Now I have individual views per tab.
When I type into tab 1 and switch to tab 2, I lose what I typed in tab one when I click back.
Could somebody help to provide me with a solution to these problems?
One NotepadView for multiple instances of the ViewModel
Keeping information when tabs change
Many thanks in advance.
I moved the Header and Text properties into ViewModelBase and then when I add a ViewModelBase as a Tab I used a DataTemplate for the ViewModelBase as I was for the NotePadViewModels with just a textbox.
<DataTemplate DataType="{x:Type ui:ViewModelBase}">
<TextBox Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</DataTemplate>
As apose to using this:
<DataTemplate DataType="{x:Type ui:NotepadViewModel}">
<ui:NotePadView DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type ui:NotepadViewModel2}">
<ui:NotePadView2 DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type ui:NotepadViewModel3}">
<ui:NotePadView3 DataContext="{Binding}"/>
</DataTemplate>
Thanks to Rachel for a small hint to the problem. Basically on the previous way nothing was actually binding within the datatemplate so it had no reason to keep it as a resource. By adding a default textbox within a datatemplate I can add as many notepads as I want whilst being able to add other views which don't need a textbox.
By default, WPF will re-use a TabItem when possible and simply replace the DataContext behind it. If your properties are not bound to anything in the DataContext, they will get reset when you switch tabs.
So to get your changes to persist, you need to store them in the DataContext
For example,
<TabControl ItemsSource="{Binding MyNotepadVMCollection}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding SomePropertyOnViewModel}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
I'm new to WPF and using MVVM. I have a view in which I want to display different content according to what a user selects on a menu. One of those things is another user control Temp which has a view model (TempVM) so I am doing this:
<ContentControl Content="{Binding Path=TempVM}"/>
and TempVM (of type TempViewModel)is null until the user clicks a button. Its data template is this
<DataTemplate DataType="{x:Type vm:TempViewModel}">
<view:Temp />
</DataTemplate>
That's fine, but the other thing I want to do is show a listbox when a user clicks a different menu item. So I am trying to do
<ContentControl Content="{Binding Path=Missions}"/>
(Missions is an observable collection of MissionData) and trying to template it like this:
<DataTemplate DataType="{x:Type ObservableCollection(MissionData)}">
<StackPanel>
<ListBox ItemsSource="{Binding}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
<Button Content="Go"/>
</StackPanel>
</DataTemplate>
But the compiler doesn't like the type reference. If I try doing it by giving the template a key and specifying that key in the ContentControl it works but obviously I see the ListBox and button when there's no Missions. Obviously I could make a user control and viewmodel and follow the same pattern as I did for the TempVM but it seems over the top. Am I going the right way about this and what do I need to do?
From what i see is that you try to use a Collection as a dataobject which is in my opinion bad practice. Having a DataTemplate for a collection is also problematic, like you already have witnessed. I would advice you to use a ViewModel for your missions collection.
class MissionsSelectionViewModel
{
public ObservableCollection<Mission> Misssions;
public MissionData SelectedMission;
public ICommand MissionSelected;
}
and modify your datatemplate to
<DataTemplate DataType="{x:Type MissionsSelectionViewModel}">
<StackPanel>
<ListBox ItemsSource="{Binding Missions}" SelectedItem="{Binding Path=MissionData, Mode=TwoWay}" DisplayMemberPath="MissionName" SelectedValuePath="MissionId" />
<Button Content="Go" Command="{Binding MissionSelected}/>
</StackPanel>
</DataTemplate>
If I were to follow your pattern of implicit templates, I would derive a custom non-generic collection MissionDataCollection from ObservableCollection<MissionData> and use it to keep MissionData items. Then I would simply reference that collection in DataType. This solution gives other advantages like events aggregation over the collection that are useful.
However, it seems to me that the best solution is the following.
Add a IsMissionsListVisible property to your VM.
Bind the Visibility property of the ContentControl showing the list to the IsMissionsListVisible property.
Use a keyed DataTemplate resource.
Implement the logic that determines if IsMissionsListVisible. Supposedly it should be true when there is at least one mission in the selected item. But the logic may be more complex.
I would do it this way. In fact, I do it this way usually, and it gives several benefits. The most important is that I can explicitly control the logic of content visibility in various situations (e.g. async content refresh).
I'm using a few data templates to display different values, those data templates are chosen by DataTemplateSelector. Every control has some DataBinding to my custom objects. Objects are part of an ObservableCollection and then DTS is choosing the template for them. The problem is: When I try to run my app with some pre defined objects (in code) the selected controls has no values. Ex:
<!--Date Template-->
<DataTemplate x:Key="DateTemplate">
<WrapPanel x:Name="DateTemplate_Panel">
<WrapPanel.DataContext>
<params:FTParams />
</WrapPanel.DataContext>
<Label x:Name="DateTemplate_Label" Content="{Binding Path=Name}" />
<DatePicker x:Name="DateTemplate_DatePicker" SelectedDate="{Binding Path=SelectedValue}" SelectedDateFormat="Long" />
</WrapPanel>
</DataTemplate>
Controls are responding only when I change their value (INotifyPropertyChanged is implemented)
If I set
<Label Content="{Binding Path=SelectedValue}"/>
and I select a date in DataPicker then the content is loaded correctly. But I really need to have this values loaded on startup.
Can you give me some advice?
The data template should not have embedded data. And you definitely don't want to instantiate instances of FTParams, from within the DT. The DataContext property of the DataTemplate is set implicitly, when you have the data somewhere else in the tree.
I assume you have some sort of ItemsControl, but for the simplicity, let the sample below have a content control:
<ContentControl ContentTemplate="{StaticResource DateTemplate}">
<params:FTParams />
</contentControl>
If you had all your items in ItemsControl (with ItemsSource bind to the ObservableCollection), then instead of ContentTemplate you should set the ItemsTemplate, or if you want to work with template selector, set the ItemTemplateSelector.
<ItemsControl ItemsSource="{Binding PathToTheObsCollectionProperty}"
ItemTemplateSelector="{StaticResource MySelector}" />
In all cases, the DT should not have explicitly set the DataContext property.
Then have your data template without the DataContext element.