c# WPF CellTemplate / DataTemplate for an Image list / array - c#

What I have:
my class:
public class MyImage
{
public String ImagePath { get; private set; }
public String Name { get; private set; }
// ...
}
XAML:
<Window.Resources>
<DataTemplate x:Key="template_image">
<Image Source="{Binding Path=ImagePath}" Stretch="None" />
</DataTemplate>
<DataTemplate x:Key="template_name">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</Window.Resources>
<ListView x:Name="gui_listview_graphics" Margin="75,0,0,0">
<ListView.View>
<GridView>
<GridViewColumn Header="Graphic" CellTemplate="{StaticResource template_image}" />
<GridViewColumn Header="Name" CellTemplate="{StaticResource template_name}" />
</GridView>
</ListView.View>
</ListView>
Works fine.
Now what i now want..
I need to display an Image-List instead of just one Image.
my new class:
public class MyNewImage
{
public ObservableCollection<String> ImagePath { get; private set; }
public String Name { get; private set; }
// ...
}
The DataTemplate should contain an Stackpanel which contains all images..
Is it possible?
If yes, how ..
Thank you!

In your DataTemplate include an ItemsControl whose Items property is bound to your collection of image paths. Your ItemTemplate for the ItemsControl will essentially be the same as your current DateTemplate, except you will use an implicit Binding for the Image source, since the string itself is your object. The default template for the ItemsPanel for an ItemsControl is a StackPanel already, but you can change this if necessary by setting the ItemsPanel property.

This template should work:
<DataTemplate x:Key="template_image_list">
<ItemsControl ItemsSource="{Binding ImagePath}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"></Image>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
Because a StackPanel does not have a property ItemsSource, you cannot bind directly to it. If you use it as ItemsPanelTemplate of an ItemsControl everything is fine.

Related

Passing a global value to DateTemplate in WPF

I have the following ListView with a DataTemplate that creates three TextBlocks and populates each entry with the data from class Item.
I want to set the width of each TextBlock to some value that is passed through with the ICollection<Item> array as a value that is the same for each entry. With the syntax below, each Item would have to have the values of GlobalWidth1, etc to be set for each instance.
Is there any way to pass the width values as a global values for the entire ICollection<Item> collection in WPF?
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Data1}" Width="{Binding GlobalWidth1}" />
<TextBlock Text="{Binding Data2}" Width="{Binding GlobalWidth2}" />
<TextBlock Text="{Binding Data3}" Width="{Binding GlobalWidth3}" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public class Item
{
public string Data1 { get; set; }
public string Data2 { get; set; }
public string Data3 { get; set; }
}
Instead of using bindings you could define resource values within the XAML file itself and use {StaticResource ...}.
xmlns:system="clr-namespace:System;assembly=System.Runtime"
...
<ListView>
<ListView.Resources>
<system:Double
x:Key="GlobalWidth1">
100
</system:Double>
<system:Double
x:Key="GlobalWidth2">
120
</system:Double>
<system:Double
x:Key="GlobalWidth3">
150
</system:Double>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding Data1}" Width="{StaticResource GlobalWidth1}" />
<TextBlock Text="{Binding Data2}" Width="{StaticResource GlobalWidth2}" />
<TextBlock Text="{Binding Data3}" Width="{StaticResource GlobalWidth3}" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
You could even have these defined in your top level App.Resources and set them from the App.xaml.cs.
Note: If you add these to the App.Resources you will need to remove them from the local ListView.Resources
public App()
{
//hardcode
this.Resources.Add("GlobalWidth1", 100);
this.Resources.Add("GlobalWidth2", 120);
this.Resources.Add("GlobalWidth3", 150);
//or perhaps define them in the global settings
this.Resources.Add("GlobalWidth1", Settings.Default.GlobalWidth1);
this.Resources.Add("GlobalWidth2", Settings.Default.GlobalWidth2);
this.Resources.Add("GlobalWidth3", Settings.Default.GlobalWidth3);
}
Of course if you want to keep it using bindings you can just add static properties on the Item class:
public class Item
{
public string Data1 { get; set; }
public string Data2 { get; set; }
public string Data3 { get; set; }
public static double GlobalWidth1 => 100;
public static double GlobalWidth2 => 120;
public static double GlobalWidth3 => 150;
}
I would personally recommend keeping it in the XAML for I find it more organized to keep pure UI code in the View layer and out of the ViewModel layer (If you are sticking to MVVM)

How do I get the SelectedItem in a Listview with Listbox in it?

I am having trouble getting the selecteditem from a listbox which is a child of a listview. Everything that I've tried returns the GpoObject which is set at the parent listview, but not the selected OuLink from the Listbox.
This is my DataTemplate for the ListBox:
<DataTemplate x:Key="OuTemplate">
<Label Content="{Binding Path=Path}"/>
</DataTemplate>
This is my Listview with the ListBox in it:
<ListView x:Name="OutListView"
BorderBrush="#FFA0A0A0"
BorderThickness="1">
<ListView.View>
<GridView>
<GridViewColumn Header="Group Policy Objects"
Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Grid.Column="0"
Text="{Binding Path=Name}"
Width="Auto"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Organizational Units">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ListBox Grid.Column="1"
ItemsSource="{Binding Path=OUs}"
ItemTemplate="{DynamicResource OuTemplate}"
Width="Auto" Height="Auto"
BorderThickness="0"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Object for binding:
public class GpoObject
{
public string Name {get; set;}
public string Id { get; set; }
public List<OuLink> OUs { get; set; }
}
public class OuLink
{
public string Path { get; set; }
}
Here are two ways to access the Path off of the ListBox selected item. I have named the listbox to make it easier in Xaml. To show the selected info I pathed to it in a textbox now which resides above the Listbox (see image):
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock x:Name="tbSelected"
Text="{Binding ElementName=PathBox, Path=SelectedItem.Path}" />
<ListBox x:Name="PathBox"
SelectionChanged="PathBox_OnSelectionChanged"
ItemsSource="{Binding Path=OUs}"
ItemTemplate="{DynamicResource OuTemplate}"/>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
Then on when the selection changes I present the user with a message box of the selected path:
private void PathBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var lbi = sender as ListBox;
if (lbi != null)
if (lbi.SelectedItem != null)
{
var link = lbi.SelectedItem as OuLink;
if (link != null)
MessageBox.Show(link.Path);
}
}
Here is a selection and its propagation to the textbox and the message box:
I suggest that within the OnSelectionChanged instead of a messagebox you place that selection into a INotifyPropertyChanged string property on your ViewModel and propagate it that way to other items within the program.
Add SelectedItem="{Binding SelectedOuLink}" to your ListBox in your xaml.
Then in your GpoObject class add:
public OuLink SelectedOuLink { get; set; }
You can now retrieve the selected OuLink object via SelectedOuLink.

How to have recursion, or a hierarchy lists and sublists in XAML?

Say I have the following class, Employee
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public ObservableCollection<Employee> Underlings { get; set; }
}
And then I have the following XAML, bound to an ObservableCollection<Employee> MyEmployees
<ListBox ItemsSource="{Binding Path=MyEmployees}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Tag="{Binding Path=Employee.Id}">
<TextBlock Text="{Binding Path=Employee.Name}"></TextBlock>
<!-- Here's where I declare my underlings -->
<ListBox ItemsSource="{Binding Path=Employee.Underlings}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Tag="{Binding Path=Employee.Id}">
<TextBlock Text="{Binding Path=Employee.Name}"></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This allows each employee in the collection MyEmployees to have some underlings. But those underlings are also of type employee, and could have their own underlings. How do I cater for those additional levels without making my XAML very complex?
Is there some way to declare my DataTemplate separately and allow it to be referenced within itself?
Do I have to do all this from code-behind instead?
(I realise the XAML above may not be 100% correct, its just an example)
therefore you have to use a TreeView and not a ListBox. And You have to specify a HierarchicalDataTemplate.
You could define the DataTemplate inside the ListBoxes Resources as the default template for Employees:
<ListBox ItemsSource="{Binding MyEmployees}">
<ListBox.Resources>
<DataTemplate DataType="{x:Type myns:Employee}">
<Grid Tag="{Binding Id}">
<TextBlock Text="{Binding Name}"></TextBlock>
<ListBox ItemsSource="{Binding Underlings}" />
</Grid>
</DataTemplate>
</ListBox.Resources>
</ListBox>

DataBinding to WP8 Toolkit ExpanderView

I'm attempting to databind to a Windows Phone 8 Toolkit Expander view with the following XAML and C# class. I know that the DataContext is set properly because the Headers have the proper text. However, the rest of the items aren't set properly (except for the ExpanderTemplate)
<phone:PanoramaItem Header="Skill Sheet">
<ListBox Name="SkillSheet" ItemsSource="{Binding}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<toolkit:ExpanderView Header="{Binding}"
ItemsSource="{Binding}"
IsNonExpandable="False">
<toolkit:ExpanderView.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding groupName}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" LineHeight="{StaticResource LongListSelectorGroupHeaderFontSize}" />
</DataTemplate>
</toolkit:ExpanderView.HeaderTemplate>
<toolkit:ExpanderView.ExpanderTemplate>
<DataTemplate>
<TextBlock Text="Test" />
</DataTemplate>
</toolkit:ExpanderView.ExpanderTemplate>
<!--This is the area that is not getting databound-->
<toolkit:ExpanderView.ItemTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding skillNames}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding skill}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</toolkit:ExpanderView.ItemTemplate>
</toolkit:ExpanderView>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</phone:PanoramaItem>
And here are the classes that the XAML is getting bound to:
public class TreeMapSkill
{
public string skill { get; set; }
}
public class TreeMapping
{
public string groupName { get; set; }
public List<TreeMapSkill> skillNames { get; set; }
public TreeMapping()
{
skillNames = new List<TreeMapSkill>();
}
}
public class TreeMappingList
{
public List<TreeMapping> mapping { get; set; }
public TreeMappingList() { }
public TreeMappingList(Dictionary<string, List<string>> map)
: base()
{
this.mapping = new List<TreeMapping>();
foreach (string key in map.Keys)
{
TreeMapping tMap = new TreeMapping();
tMap.groupName = key;
foreach (string val in map[key])
tMap.skillNames.Add(new TreeMapSkill() { skill = val });
this.mapping.Add(tMap);
}
}
The Dictionary in the constructor is simply a list of skills associated to a specific group. I can also provide a sample object if it's needed for additional reference.
Why are you adding a ListBox inside the Expander's ItemTemplate? It is already a controls collection so you don't need a ListBox in there. Just put your DataTemplate inside.
<toolkit:ExpanderView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding skill}" />
</DataTemplate>
</toolkit:ExpanderView.ItemTemplate>
The second thing is you need to specify the property path on the binding of the ItemSource property for the expander.
<toolkit:ExpanderView Header="{Binding}"
ItemsSource="{Binding skillNames}"
IsNonExpandable="False">

How to display values as images in GridViewColumn?

I have a GridViewColumn which I have bound as so:
<GridViewColumn Header="Validated" DisplayMemberBinding="{Binding Path=Validated, Converter={StaticResource imageConverter}}" />
The Binding Path = Validated returns an Enumerated value, the imageConverter takes that value and returns a System.Windows.Media.Imaging.BitmapImage. I have checked the value of the object referenced when one of these BitmapImage objects is created, and it appears to contain an image of the correct size.
My problem now is that what is being displayed in the GridView is the URI of the BitmapImage (as text), and not the image itself.
What am I doing wrong this time?
Change imageConverter to return the uri of the image instead of an actual image.
<GridViewColumn Header="Validated">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Path=Validated, Converter={StaticResource imageConverter}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Taken from this website:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/96f18c2b-cade-42d9-b544-c64a7ce3d82b
First you should have a class that contains image information, at least its address.
public class VideoGame
{
public string Name
{
get;
set;
}
public string Image
{
get;
set;
}
}
Second, add some instances into one ObservableCollection.
public partial class Window1 : Window
{
private ObservableCollection<VideoGame> _games =
new ObservableCollection<VideoGame>();
public ObservableCollection<VideoGame> Games
{
get { return _games; }
}
public Window1()
{
_games.Add(new VideoGame() {
Name = "Crysis",
Image = #"C:\Crysis_Boxart_Final.jpg" });
_games.Add(new VideoGame() {
Name = "Unreal Tournament 3",
Image = #"C:\Gearsofwar.JPG" });
_games.Add(new VideoGame() {
Name = "Gears of War",
Image = #"C:\Crysis_Boxart_Final.jpg" });
InitializeComponent();
}
}
Third, set the DataTemplate of GridViewColumn.CellTemplate.
<Window x:Class="VerticalAlignSnippet.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="512" Width="512" Name="myWindow">
<Grid>
<ListView Name="myListView"
ItemsSource="{Binding ElementName=myWindow, Path=Games}">
<ListView.View>
<GridView>
<GridViewColumn Header="Title" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Image">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<Image Source="{Binding Image}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
This approach is done in XAML. You can make use of XamlReader to load this DataTemplate in the code behind.
string str = "<DataTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"><Grid><Image Source=\"{x:Null}\" /></Grid></DataTemplate>";
DataTemplate template = new DataTemplate();
template = XamlReader.Parse(str) as DataTemplate;
.....
gv1.Columns[3].CellTemplate = template;

Categories

Resources