split DataTemplate in different grids using ItemTemplateSelector - wpf

I am working on WPF-XAML. My requirement is :
I need to add collection of Trunks(which consists of Border & TexBlocks) in a Tab.
there will be 2 types of such Trunks (say RSPTrunkTemplate and ASPTrunkTemplate). now I need to add collection of Trunks of type RSPTrunkTemplate in one grid. then there will be GridSplitter and then I need to add another collection of Trunks of type ASPTrunkTemplate in another grid.
I am using ItemTemplateSelector as follows :
<Grid>
<ItemsControl Name="TrunkList"
ItemsSource="{Binding RSPTrunks}"
ItemTemplateSelector="{StaticResource TrunkItemTemplateSelector}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
this TrunkItemTemplateSelector is as follows :
<Helpers:TrunkItemTemplateSelector x:Key="TrunkItemTemplateSelector"
RSPTrunkTemplate="{StaticResource RSPTrunkTemplate}"
SPTrunkTemplate="{StaticResource ASPTrunkTemplate}" />
Now, RSPTrunkTemplate should be in one grid and ASPTrunkTemplate shoulb be in another grid.
How to do this. Do I have to change my approach.?
I seek your help guys.

ItemTemplateSelector, as the name suggests, is used to specify a different template for the objects inside a ItemsControl, not to do filtering. If i understand correctly you want to apply a grouping maybe this link can help you http://msdn.microsoft.com/en-us/library/ms742542.aspx

Related

Create dummy data type so I can have a DataProvider to my ItemsControl in xaml

I am developing an UserControl in XAML consisting of an image of the human body, where some electrode position points are to be specified.
Each position would be modeled by some datatype containing properties for Position, Orientation (angle) and State (enabled, disabled, activated):
I want to use sample data (DataProvider or something similar) defined directly in XAML so I can design position and orientation visually and try some different possibilities by editing the single XAML file and having immediate visual feedback.
So I will create an ItemsControl with a Canvas, define a DataTemplate for my objects, and then I would like to write something similar to this XAML:
<ItemsControl x:Name="DotsOverBody"
ItemsSource="{StaticResource SomeLocalXamlDefinedDataSource}"
ItemTemplate="{StaticResource SomeSuitableTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- Could I define a generic (xml-based?) "Body Position" type
directly in XAML? -->
<BodyPosition X="123" Y="456" Angle="45"/>
<BodyPosition X="133" Y="476" Angle="33"/>
<BodyPosition X="125" Y="356" Angle="77"/>
<BodyPosition X="323" Y="457" Angle="89"/>
<BodyPosition X="528" Y="426" Angle="10"/>
<BodyPosition X="163" Y="156" Angle="3"/>
</ItemsControl>
A screenshot of a legacy application we have is below, with my intended visual output (I already have a DataTemplate for that, which I would change the target type of course).
So how could I achieve this?

ItemsControl with only custom subelements

I would like to build a custom component that layouts its childs in either a StackPanel or a Grid (with variable row count, which makes me consider the StackPanel instead). The items are custom elements/objects that just hold some configuration, based on which a few controls are created to display them (some labels and text boxes).
Ideally, the component should be used somehow like this (where SpecializedCustomPanelItem is a subtype of CustomPanelItem):
<CustomPanel>
<CustomPanelItem Param1="value A" Param2="value B">Text</CustomPanelItem>
<CustomPanelItem Param1="value C">Other text</CustomPanelItem>
<SpecializedCustomPanelItem>More text</SpecializedCustomPanelItem>
<!-- The number of items is variable -->
</CustomPanel>
I’ve read on the ItemsControl for a while now, and it fits my needs rather well. I would create simply types for the items, and make data templates for them available from inside the ItemsControl. Then they should already render fine.
However I would like to require the items inside that ItemsControl to be of a specific type (i.e. CustomPanelItem or a subtype). I actually thought that the ItemsControl would allow this, just like you within a ComboBox or a MenuItem, but it turns out that it actually allows any subtype, and if necessary wraps them in a item container.
So I have been thinking if an ItemsControl is actually what I am looking for, as I do not want any “fancy” things like selection or scrolling which most of those controls implement. I actually only want to build a simple interface to a common pattern in the application that auto generates those components and layouts them in a Grid/StackPanel.
Should I still be using the ItemsControl or rather build some more custom component?
In this case you don't really need a custom component. Changing the ItemsPanel type to whatever type you need + multiple templates for the Items should do the trick.
However to answer the question in the heading: If you want to force an items control to only accept a certain type of items, you will have to create
a. A CustomItemsControl
b. A CustomItemsControlItem
Then for the CustomItemsControl you should declare the attribute
[StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(CustomItemsControlItem))]
Then you also will need to
protected override DependencyObject GetContainerForItemOverride()
{
return new CustomItemsControlItem();
// You can throw an exception here
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is CustomItemsControlItem;
}
If memory serves this should force the ItemsControl to not allow other types to be added as children and should throw exceptions. You could then do some magic inside CustomItemsControlItem by defining some DependencyProperties which you can then set when adding the items in XAML.
But yet if you have multiple types in your ViewModel that you want to display correctly, the correct way is still to provide multiple templates for the CustomItemsControlItem targetting your ViewModel types.
Hope this helps.
This sounds perfect for an ItemsControl
You can set it's ItemsPanelTemplate to define the kind of panel which will hold your items, and set the ItemContainerTemplate to define how to draw each item.
If items should be drawn differently based on what type they are, I'd suggest using implicit DataTemplates instead of setting the ItemContainerTemplate
<Window.Resources>
<DataTemplate DataType="{x:Type my:BasePanelItem}">
<my:CustomPanelItem Param1="{Binding Param1}" Param2="{Binding Param2}" Content="{Binding SomeValue}" />
</DataTemplate>
<DataTemplate DataType="{x:Type my:SpecializedPanelItem}">
<my:SpecializedCustomPanelItem Content="{Binding SomeValue}" />
</DataTemplate>
</Window.Resources>
<ItemsControl ItemsSource="{Binding MyItems}">
<!-- ItemsPanelTemplate -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<my:CustomPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
You mentioned that you wanted to perhaps use a dynamically created Grid instead of a StackPanel as well. If you do, you might be interested in some GridHelpers I have posted on my blog. This would allow you to bind the number of Columns/Rows on the Grid in the ItemsPanelTemplate
<ItemsControl ItemsSource="{Binding MyCollection}">
<!-- ItemsPanelTemplate -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid local:GridHelpers.RowCount="{Binding RowCount}"
local:GridHelpers.ColumnCount="{Binding ColumnCount}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- ItemContainerStyle -->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}" />
<Setter Property="Grid.Row" Value="{Binding RowIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>

Binding from resources inside a DataTemplate

Is there some way to get the DataContext of a DataTemplate to use in bindings within its resources?
<DataTemplate x:Key="History">
<ItemsControl ItemsSource="{Binding History}">
<ItemsControl.Resources>
<app:BitmapProvider x:Key="Converter" ShowDetails="True"
Type="{Binding Model.Type}" />
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Data, Converter={StaticResource Converter}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
The above template is used as the CellTemplate of a ListBox. The object at that level has two properties, History (containing a list of "historic info" objects) and Model (containing a bunch of other stuff, including Type). I'm using an ItemsControl to display the historic items next to each other; I want to display an image for each one, and the image is obtained from the BitmapProvider, which is an IValueConverter.
The converter needs two bits of info to obtain a result: one is the Data of the individual historic items, and the other is the Type of the whole collection. An added complication is that constructing this particular converter (or changing the Type given to it) is expensive, so I don't want to put it at the level of the individual history item, or to use a MultiBinding, and I can't put it outside of the template because then it won't have access to the Type.
Unfortunately, the above gives me the following error:
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=Model.Type; DataItem=null; target element is 'BitmapProvider' (HashCode=57142809); target property is 'Type' (type 'TypeDetails')
Which I understand to mean that the resource can't figure out how to get the DataContext of the element it's contained within.
(I have searched, and most of the answers I could find suggested moving it outside the template or using a MultiBinding instead -- neither of which would really work in this case, as far as I can tell, as I've explained above. But I'd be delighted to be proven wrong, or given another alternative.)
I think you can accomplish that with DataContextSpy.
try something like:
<ItemsControl.Resources>
<spy:DataContextSpy x:Key="Spy"/>
<app:BitmapProvider x:Key="Converter" ShowDetails="True"
Type="{Binding DataContext.Model.Type,Source={StaticResource Spy}}" />
</ItemsControl.Resources>

WPF GridView: "Newspaper" Column?

I'm not sure how else to describe this, outside of calling it a "newspaper" column.
Essentially I have a potentially long list of codes that I want to display in a grid, and I have limited vertical real estate. I would like to show these codes (which are all from the same database column) in multiple columns, maybe 3-5 columns across.
I can absolutely break the data up into separate sources and bind to them separately if that is the best solution, but I thought there might be an easy, built-in way to accomplish this with WPF.
This is actually trivial using a WrapPanel.
For a hard-coded list:
<WrapPanel Orientation="Vertical">
<ItemOne />
<ItemTwo />
...
</WrapPanel>
For a data-bound list:
<ItemsControl ItemsSource="...">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="...">
...
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If desired you can replace the ItemsControl with a ListBox or make it a ComboBox or whatever. You can use a default template for your data or use a custom template as shown above. You can even use a ListView along with a GridView if you want a multi-column list.
I suspect what you're looking at here is a custom layout panel which has a well defined height and then wraps into the next column. You could then use the ItemsPanelTemplate of the list control to use your new custom panel.
With respect to the development of the panel itself, I would suspect that either wrapping or inheriting from the Grid would be an excellent first choice. The panel could then manage the column definitions itself based on the number of items it contains.
To determine the layout of the individual items, I suspect using the ActualHeight to determine when another item would cause a column to overflow and using that to move to the next column would be the optimal solution. I would imagine using a single vertical stack panel with no border or padding inside each column may make it easier for your to offload the layout to those controls, but I believe you would still wind up having to determine which panel to lay the items out in based on the item heights.

WPF creating dynamic rows using MVVM pattern

I want to load my WPF UserControl with dynamic rows. My scenario is as follows.
1. When the UserControl gets loaded, I will populate my List<string> object with some values which I will get from a database.
2. I need to create equal number of rows in my UserControl which matches the number of items in the List<string> object. The display will look something like below
Column 1 is a Label Control and Column 2 will be a TextBlock control
Label 1: Item 1 (This is the value from the List<string> object)
Label 2: Item 2
Label 3: Item 3
I know how to create rows dynamically but my problem is how do I do this when I'm using the MVVM pattern.
Note: I'm using the MVVM toolkit from CodePlex.
Thanks,
Jithu
Set the MVVM object you have as the dataContext of your UserControl, I hope the object has a Collection property in it. Then create an ItemsControl more like below
It is not clear from your description that where is really the Label and Item comes from your ViewModel. The below code will create Rows dynamically as many as your Collection.Count.
<ItemsControl ItemsSource="{Binding YourStringCollection}" HorizontalAlignment="Left" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemsTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
</DataTemplate >
</ItemsControl. ItemsTemplate >
</ItemsControl>

Resources