I am working on a Treeview control. The items control should display a set of textbox and combobox dynamically depending on the value of the data structure.
The ArgumentTypeTemplateSelector 's convert code is executed. however, no Textbox and combo is display. Please would someone kindly help. thank you.
The tree View (xaml)
<ItemsControl x:Name="argumentTexts" ItemsSource="{Binding ArgumentDetailsCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Stretch" IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type structures:ArgumentDetails}">
<ItemsControl x:Name="items" ItemsSource="{Binding DefaultValue}"
ItemTemplateSelector="{Binding DefaultValue, Converter={StaticResource ArgTypeTemplateSelector}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
public class ArgumentTypeTemplateSelector : IValueConverter
{
private DataTemplate comboboxDataTemplate;
private DataTemplate textboxDataTemplate;
public DataTemplate ComboBoxDataTemplate
{
get
{
return this.comboboxDataTemplate;
}
set
{
this.comboboxDataTemplate = value;
}
}
public DataTemplate TextBoxDataTemplate
{
get
{
return this.textboxDataTemplate;
}
set
{
this.textboxDataTemplate = value;
}
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string str = (string)value;
if (str.Contains("1"))
{
return this.ComboBoxDataTemplate;
}
return this.TextBoxDataTemplate;
}
In the resourcedictionary(xaml)
<DataTemplate x:Key="TextBoxDataTemplate">
<TextBox Text="{Binding 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" IsEnabled="True"/>
</DataTemplate>
<columnConfiguratorControls:ArgumentTypeTemplateSelector x:Key="ArgTypeTemplateSelector" ComboBoxDataTemplate="{StaticResource ComboBoxDataTemplate}" TextBoxDataTemplate="{StaticResource TextBoxDataTemplate}"/>
</ResourceDictionary>
DataTemplateSelectors work in a different way than converters. Your DataTemplateSelector will retrieve the item of your list as argument and depending on the criteria you define, you can choose a DataTemplate that should be returned.
Try the following:
In your xaml file define your DataTemplateSelector as static resource and set it in your ItemsControl:
<ItemsControl x:Name="argumentTexts" ItemsSource="{Binding ArgumentDetailsCollection}">
<ItemsControl.Resources>
<!-- define your template selector as static resource to reference it later -->
<ns:ArgumentTypeTemplateSelector x:Key="ArgumentTypeTemplateSelector"/>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Stretch" IsItemsHost="True" Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type structures:ArgumentDetails}">
<!-- use your template selector here -->
<ItemsControl x:Name="items" ItemsSource="{Binding DefaultValue}"
ItemTemplateSelector="{StaticResource ArgumentTypeTemplateSelector }"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
In your code behind file, you need to implement your data template selector for example in the following way:
// derive from base class DataTemplateSelector
public class ArgumentTypeTemplateSelector : DataTemplateSelector
{
// override SelectTemplate method
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
// get access to the resources you need, for example
// by accessing the UI object where your selector is placed in
var frameworkElement = (FrameworkElement) container;
// return a data template depending on your custom logic,
// you can cast "item" here to the specific type and check your conditions
if(item has condition)
return (DataTemplate) frameworkElement.FindResource("YourDataTemplateKey");
else
// ...
return base.SelectTemplate(item, container);
}
}
Related
I have Items Control, but I want improve this code for working with different types of input data.
<Grid>
<ItemsControl x:Name="control"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ItemsSource="{x:Bind ItemsSource, Mode=OneWay}"
ItemTemplate="{x:Bind CellTemplate, Mode=OneWay, Converter={StaticResource SimpleSelector}}">
<!--I want make like this-->
<ContentControl VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
ContentTemplate="{Binding SelectedCollageTemplate, Converter={StaticResource CollageTemplateSelector}}" />
<!-- -->
<!--now I have this-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controls:SimplePanel SelectedCollage="{Binding SelectedCollage, Mode=TwoWay}"
SelectedCollagePattern="{Binding SelectedCollagePattern}">
<controls:SimplePanel.Background>
<ImageBrush Stretch="Fill"
ImageSource="ms-appx:///Images/Background/5.jpg" />
</controls:SimplePanel.Background>
</controls:SimplePanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- -->
</ItemsControl>
</Grid>
As you can see I want change hardcode to more flexible way and use Template selector
I create selector:
<templateSelector:CollageTemplateSelector x:Key="CollageTemplateSelector"
SimpleTemplate="{StaticResource SimpleTemplate}"
ShapeTemplate="{StaticResource ShapeTemplate}"/>
And added DataTemplate:
<DataTemplate x:Key="SimpleTemplate">
<controls:SimplePanel
SelectedCollage="{Binding SelectedCollage, Mode=TwoWay}"
SelectedCollagePattern="{Binding SelectedCollagePattern}">
<controls:SimplePanel.Background>
<ImageBrush Stretch="Fill"
ImageSource="ms-appx:///Images/Background/5.jpg" />
</controls:SimplePanel.Background>
</controls:SimplePanel>
My converter returns Simple Panel. But when I lauch it my SimplePanel doesnt start(I have break point on constructor) and part of code doesnt work. What is my problem?
You're setting the ContentTemplate of your ContentControl to your selector; you should set the ContentTemplateSelector property instead.
In your ItemsControl you're setting ItemsTemplate to something that looks like a template selector; you should set the ItemsTemplateSelector property instead.
You shouldn't bind to template selectors, but access them as StaticResources.
I don't fully understand the details of what you're trying to do, so here's an example of a DataTemplateSelector that works.
To start with, I'm using the following ItemsSource, with the intent of making the string "Three" show in red:
public string[] ItemsSource => new[]
{
"One", "Two", "Three",
};
The template selector has two DataTemplate properties that will be set from XAML -- one for "Three" strings; another for all other strings:
public sealed class ItemTemplateSelector : DataTemplateSelector
{
/// <summary>
/// This property is set in XAML.
/// </summary>
public DataTemplate NormalTemplate { get; set; }
/// <summary>
/// This property is set in XAML.
/// </summary>
public DataTemplate ThreeTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
if ("Three".Equals(item))
{
return ThreeTemplate;
}
return NormalTemplate;
}
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return SelectTemplateCore(item);
}
}
The data template selector has two versions of SelectTemplateCore. This page discusses which one to use under what circumstances:
If your ItemsControl.ItemsPanel is an ItemsStackPanel or ItemsWrapGrid, provide an override for the SelectTemplateCore(Object) method. If the ItemsPanel is a different panel, such as VirtualizingStackPanel or WrapGrid, provide an override for the SelectTemplateCore(Object, DependencyObject) method.
The XAML (which assigns data templates to the selector's two properties) looks like this:
<Grid>
<Grid.Resources>
<local:ItemTemplateSelector x:Key="ItemTemplateSelector">
<local:ItemTemplateSelector.NormalTemplate>
<DataTemplate>
<TextBlock Foreground="Blue" Text="{Binding}" />
</DataTemplate>
</local:ItemTemplateSelector.NormalTemplate>
<local:ItemTemplateSelector.ThreeTemplate>
<DataTemplate>
<TextBlock Foreground="Red" Text="{Binding}" />
</DataTemplate>
</local:ItemTemplateSelector.ThreeTemplate>
</local:ItemTemplateSelector>
</Grid.Resources>
<ItemsControl
VerticalAlignment="Center"
HorizontalAlignment="Center"
ItemsSource="{x:Bind ItemsSource, Mode=OneWay}"
ItemTemplateSelector="{StaticResource ItemTemplateSelector}">
</ItemsControl>
</Grid>
The result looks like this, with the string "Three" shown in red:
I hope this is sufficient to put you on the right track.
I am creating 'Lessons' tab in my application. The problem is in displaying data.
Timetable(List<DayInfo>) is binded to ItemsControl. Each DayInfo is an item in this ItemsControl. I tried to bind Exams collection to nested ItemsControl placed in ItemTemplate, but It's not working.
I'd like to know what I'm doing wrong. I guess my Exams binding is the problem.
Timetable:
private List<DayInfo> timetable;
public List<DayInfo> Timetable
{
get { return timetable; }
set
{
timetable = value;
NotifyOfPropertyChange(() => Timetable);
}
}
There is DayInfo.cs:
public class DayInfo : IValue
{
public string DayName { get; }
public List<ExamEntry> Exams { get; }
...
}
ExamEntry.cs:
public class ExamEntry : DayEntry, IValue
{
public string Description { get; }
...
}
XAML code:
<ItemsControl
ItemsSource="{Binding Timetable}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
...
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<StackPanel
Orientation="Horizontal"
Margin="0">
</StackPanel>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label
Content="{Binding DayName}" /> <!-- It still works -->
<ItemsControl
ItemsSource="{Binding Exams}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label
Content="{Binding Description}" /> <!-- It's not displayed -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Everything is fine with this piece of code. I've forgotten about another part of project, where Timetable was bugged. Sorry for confusion.
I am trying to implement the following scanarios
APPROACH SO FAR
Tried to implement it with an ItemsControl (with WrapPanel) and a TextBox wrapped inside a WrapPanel, but it does not have a desired output as there are two WrapPanels wrapping separately
<toolkit:WrapPanel Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding someThing}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Border>
<TextBlock Text="somesomething" />
</Border>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<TextBox/>
</toolkit:WrapPanel>
I am thinking if I can add the TextBox at the END of the ItemsControl, but failed to do so. Please specify if there is any other workaround/ solution to any of my approaches
You need to use DataTemplateSelector for the ItemsControl and specify different templates for different list items.
public class BlockItem
{
// TODO
}
public class BoxItem
{
// TODO
}
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate BlockTemplate { get; set; }
public DataTemplate BoxTemplate { get; set; }
protected override DataTemplate SelectTemplateCore(object item)
{
if (item is BlockItem) return BlockTemplate;
else if (item is BoxItem) return BoxTemplate;
return base.SelectTemplateCore(item);
}
}
XAML:
<ItemsControl ItemsSource="{Binding someObject}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplateSelector>
<local:MyTemplateSelector>
<local:MyTemplateSelector.BlockTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="something"/>
</Grid>
</DataTemplate>
</local:MyTemplateSelector.BlockTemplate>
<local:MyTemplateSelector.BoxTemplate>
<DataTemplate>
<Grid>
<TextBox Text="something"/>
</Grid>
</DataTemplate>
</local:MyTemplateSelector.BoxTemplate>
</local:MyTemplateSelector>
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
And you then add different types of objects to your items source:
someObject.Add(new BlockItem());
someObject.Add(new BlockItem());
someObject.Add(new BlockItem());
someObject.Add(new BlockItem());
someObject.Add(new BoxItem());
If you want the TextBox to be the last element, then you need it to be the last item in your ItemsSource list.
Every example and I article I found, is about grouping items by one property and displaying it. But what I have, is a strongly type group key, which I want to display. Here is models and grouping logic:
The item interface
public interface IItem {
string Title { get; }
string ToolTip { get; }
object Icon { get; }
Type GroupType { get; }
}
IItem has many implementations like this:
public class Item : IItem {
public string Title { get; private set; }
public string ToolTip { get; private set; }
public object Icon { get; private set; }
// I have many implementation of IGroup which I will use them in GroupType properties.
public Type GroupType { get { return SomeGroupTypeHere; } }
}
And here is the group interface:
public interface IGroup {
string Name { get; }
object Icon { get; }
}
and it has many implementations too.
I collect them in my view model (by getting help from Autofac):
public class MyViewModel {
private readonly IEnumerable<IGrouping<IGroup, IItem>> _items;
public MyViewModel(IEnumerable<IGroup> groups, IEnumerable<IItem> items){
_items = items.GroupBy(t => {
var g = groups.First(u => u.GetType() == t.GroupType);
return g;
});
}
public IEnumerable<IGrouping<IGroup, IItem>> Items {
get { return _items; }
}
}
Now, the problem is, how to display this grouped items, in a ItemsControl?
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate >
<!-- here is my template that uses IItem properties, example: -->
<Button Content="{Binding Title}"
ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</ItemsControl.ItemTemplate>
</ItemsControl>
The code, just only displays the first item in each group, and I have no idea what to do to show group headers (that use IGroup properties) and also show all items in each group. Any suggestion please? Any article or blog-post will be very useful. Thanks in advance.
You want to use HierarchicalDataTemplate when you want to display Grouped data, Change your ItemsControl.ItemTemplate to a `HierarchialDataTemplate'.
Sample (and Untested) HierarchialDataTemplate:
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding}">
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate >
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Refer to this blogpost for step-by-step approach to HierarchialDataTemplates.
UPDATE
I have tried the above HierarchialDataTemplate with your code and it doesnt seem to work. However, if I replace ItemsControl with TreeView, it does work as expected.
So, I guess, you might want to have nested ItemControl for your case, Tested sample code as below:
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
<ItemsControl ItemsSource="{Binding}" Margin="15,0,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the screen shot with this nested ItemsControls.
UPDATE 2
I think you need to update the template to display Group, the current template doesnt do that.
Updated Template is below:
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding Key.Name}" />
<ItemsControl ItemsSource="{Binding}" Margin="15,0,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And the resulting Screenshot:
I have an ItemsControl that contains a collection of items that are shown at a page.
The ItemsControl has an ItemTemplate property which is set to certain DataTemplate resource.
<DataTemplate x:Key="SimpleTemplate">
<!-- .... -->
</DataTemplate>
<DataTemplate x:Key="ComplexTemplate">
<!-- .... -->
</DataTemplate>
...............................
<ItemsControl
x:Name="MainCanvas"
DataContext="{StaticResource mainItems}"
ItemsSource="{Binding Path=Buttons}"
ItemTemplate="{StaticResource SimpleTemplate}"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="4000" Height="4000" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Is it possible to change DataTemplate for a one specific item in my ItemsControl programmatically?
Sound like you are looking for ItemTemplateSelector
You can create a TemplateSelector and decide which template to apply based on a given item:
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate SimpleTemplate { get; set; }
public DataTemplate ComplexTemplate { get; set; }
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
// Logic to decide which template to apply goes here
return // Either SimpleTemplate or ComplexTemplate
}
}
In xaml, add your template selector as a resource
<local:MyTemplateSelector x:Key="itemTemplateSelector">
<local:MyTemplateSelector.SimpleTemplate>
<DataTemplate>
<!-- Implementation goes here -->
</DataTemplate>
</local:MyTemplateSelector.SimpleTemplate>
<local:MyTemplateSelector.ComplexTemplate>
<DataTemplate>
<!-- Implementation goes here -->
</DataTemplate>
</local:MyTemplateSelector.ComplexTemplate>
</local:MyTemplateSelector>
And use it in your ItemsControl
<ItemsControl
x:Name="MainCanvas"
DataContext="{StaticResource mainItems}"
ItemsSource="{Binding Path=Buttons}"
ItemTemplateSelector="{StaticResource itemTemplateSelector}">
Hope this helps