One really cool aspect of WPF is that controls can be encapsilated inside of other user controls. For example you could have an image inside of a ComboBox. I needed to place images inside of a ListView control. It was pretty easy to implement but very difficult for me to actually figure out.
I have a ListView that is bound to an generic list of objects, Products, in my example. I would like to simply display the collums that are in the list. In the first column I would like to display an image for the list item named "Active". The data value of Active is a "Y" or "N" character.
I use the following ListView code to create the Listview shown below:
<ListView Name="productList">
<ListView.View>
< GridView>
<GridViewColumn Header="Active" CellTemplate="{StaticResource ActiveGraphic}" Width="50"/>
<GridViewColumn Header="Active" DisplayMemberBinding="{Binding Path=Active}"/>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Path=Name}" Width="100"/>
<GridViewColumn Header="ChainOfCustody" DisplayMemberBinding="{Binding Path=ChainOfCustody}" Width="225"/>
<GridViewColumn Header="Created User Name" DisplayMemberBinding="{Binding Path=CreatedUser.UserName}" Width="125"/>
</GridView>
</ListView.View>
</ListView>
To create the Products Header, I used a Label inside of a StackPanel and used margins and the gradient brush. I simply stuck it right on top of the ListView. Here is an image of the ListView from the code above:
Now, I want to display an image instead of that Y or N for Active. Here is the solution I found:
Under the Resources section on the top of the control (mine just happens to be "UserControl") I created a DataTemplate called Active Graphic. This DataTemplate holds one object, a control wrapped in a StackPanel. The control is simply an image that is specified in a ControlTemplate (see below). The DataTemplate also uses a DataTrigger to evaluate which image should be displayed depending upon the value of Active. The DataTrigger on this data template adjusts the depenency property Template on the control, simply changing which graphic to display depending upon the value in the DataTrigger. When data is bound to the control, it does this for all of the items in the list.
<UserControl.Resources>
<DataTemplate x:Key="ActiveGraphic">
<StackPanel>
<Control x:Name="icon" Template="{StaticResource addImage}" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Active}" Value="Y" >
<Setter TargetName="icon" Property="Template" Value="{StaticResource addImage}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Active}" Value="N" >
<Setter TargetName="icon" Property="Template" Value="{StaticResource deleteImage}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</UserControl.Resources>
The Setter specifies a TargetName (icon) and Property to set based upon value "Value". The value property is linked to my control resources file in my WPF application via the {"StaticResource addImage"} binding. Below is the code from that file, a simple control template that displays and image, specifying the "addImage" name or x:Key property of the control template.
<ControlTemplate x:Key="addImage"> <Image Source="Images/add.png" Width="17" Height="16" />
</ControlTemplate>
<ControlTemplate x:Key="deleteImage"> <Image Source="Images/delete.png" Width="17" Height="16" />
</ControlTemplate>
Now back in the ListView's GridView.View section. I removed the simply Active GridViewCollumn and replaced it with this, one that uses the DataTemplate defined above with the CellTemplete property:
<GridViewColumn Header="Active" CellTemplate="{StaticResource ActiveGraphic}" Width="50"/>
Here is the updated Grid (I left the Active Flag in there so that I could verify the DataTrigger is working properly):
thanks to Corneliu's post here: http://www.acorns.com.au/2007/6/25/Learning+WPF+Using+Convertors.aspx
Per Marin's reply from below...
The implementation:
In the code above, modify the control in the DataTemplate to include code to point the event "MouseEnter" to the event handler code that you write on the backend. It will handle the deletion of the row in the ListView and then in the database...
<DataTemplate x:Key="ActiveGraphic">
<StackPanel>
<Control x:Name="icon" Template="{StaticResource addImage}" MouseEnter="DeleteIcon_Click" />
</StackPanel>
then in your code behind you would have a method named
public void DeleteIcon_Click(object sender, MouseEventArgs e)
{
// first check to make sure the mouse event was the left button
if (e.LeftButton != MouseButtonState.Pressed)
return;
// remove the item from the list
((List<ItemType>)ListView.ItemsSource).Remove( ((ItemType)(FrameWorkElement)(sender)).DataContext);
// update datastore with the change
UpdateItemType();
}