Control Encapsilation in WPF - Image in a ListView

by sondlerd
AddThis Social Bookmark Button

30. August 2007 06:49

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();
  

 

Currently rated 3.7 by 3 people

  • Currently 3.666667/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

WPF

Related posts

Comments

September 11. 2007 16:28

Gravatar

Useful post. Thanks!

I was doing something similar, changing the colour of the text in one of the cells depending on a Trigger. What had me stumped for a while was that the DisplayMemberBinding was also set (this is how it was getting its value before I got to it) and DisplayMemberBinding takes preference over CellTemplate.

Got rid of DisplayMemberBinding and it all worked!

cheers,
Stephen

Stephen Price au

September 12. 2007 17:51

Gravatar

Ya, it appears that DisplayMemberBinding, when used in conjunction with CellTemplate, does take precedence. Thanks for the heads up. This could be hard to find, specially if you have contructed the GridViewColumn via cut and paste.

Thanks!

sondlerd us

September 12. 2007 20:37

Gravatar

Thanks for the post. Very Helpful. I was able to create a ListView in GridView that rotates the control in a cell between a custom editable box control and a regular textblock. This way if a property on my binding object is marked editable it can be clicked on and edited in the ListView and if not editable then when clicked on it doesnt do anything.

Thanks!

Kyle us

September 13. 2007 15:43

Gravatar

Great example, it sounds very useful. Glad you found the post helpful. The result is very cool, it really allows for maximum possibilities but the implementation is tricky at first.

sondlerd us

September 20. 2007 08:12

Gravatar

Awesome, thanks! I am trying to take this one step further and make the "active" icon change when you click it (which will update to bound data from true to false and vice versa. Any ideas?

Thanks,
Martin

Martin Cook gb

September 21. 2007 05:06

Gravatar

Martin,

Good idea. I have done this and will use the example of a delete icon in the row. This icon will then delete the row from the ListView and also make a call to the database to delete the row from the database.

I added the code to the bottom of the original post because the reply hides the code due to the markup.

Rob

sondlerd us

September 21. 2007 05:15

Gravatar

You could jazz it up even more and change the cursor over top of the icon to change to the hand and then back to the normal arrow when you mouse off of it.

thanks, Tony Selke for that idea.

sondlerd us

December 30. 2007 14:06

Gravatar

HI... Robert

Good Example....

Excume man,, I need your help... I added the code for my listview but i have one error because i have all elements necesesary for run my example.. this is error... Cannot fin resouce named '{addImage}'. Resource names are case sensitive. Error at object 'System.Windows.Setter'......



<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation";
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml";
x:Class="Scrit.WsMedico.Client.Window2"
x:Name="Window"
Title="Window2"
Loaded="Window2_Loaded"
Width="640" Height="480">

<Window.Resources>

<DataTemplate x:Key="ActiveGraphic">
<StackPanel>
<Control x:Name="icon" Template="{StaticResource addImage}" />
</StackPanel>

<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=FG_NOTA_GRUPAL}" Value="1">
<Setter TargetName="icon" Property="Template" Value="{StaticResource addImage}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>

<ControlTemplate x:Key="addImage">
<Image Source="images/btnCruz.jpg" Width="17" Height="16" />
</ControlTemplate>

</Window.Resources>

<Grid x:Name="LayoutRoot">

<ListView AllowDrop="False" Grid.Column="0" Grid.Row="1" BorderBrush="#FF3D5BBA" ItemsSource="{Binding}" x:Name="grdGrupoDespedir" Margin="0,0,11,0" Height="251.174">
<ListView.View>
<GridView AllowsColumnReorder="False" >
<GridViewColumn Width="0" DisplayMemberBinding="{Binding Path=FL_GRUPO_FAMILIAR}"/>
<GridViewColumn Width="300" Header="Paciente" DisplayMemberBinding="{Binding Path=NB_PACIENTE}"/>
<GridViewColumn Width="80" Header="Clínica" DisplayMemberBinding="{Binding Path=NB_CLINICA_ASOCIADA}"/>
<GridViewColumn Width="80" Header="Sesión" DisplayMemberBinding="{Binding Path=DS_SESION}"/>
<GridViewColumn Width="50" Header="Grupal" CellTemplate="{StaticResource ActiveGraphic}" />
<GridViewColumn Width="80" Header="Individual" />
<GridViewColumn Width="220" Header="Cita" />
</GridView>
</ListView.View>
</ListView>

</Grid>
</Window>


Carlos Rojo mx

January 31. 2008 23:49

Gravatar

Carlos, thanks for posting and sorry for the delay.

If you got your code fixed please post the answer. My assumption is that the image path is not correct. Check to make sure that image is located in subdirectory names images.

sondlerd us

October 13. 2008 18:07

Gravatar

Good post, very helpful,
Carlos,
Move Data Templete part underneat the listview, but leave Control templete under Window resources, like;

<ListView.resources>
<DataTemplate x:Key="ActiveGraphic">
<StackPanel>
<Control x:Name="icon" Template="{StaticResource addImage}" />
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=FG_NOTA_GRUPAL}" Value="1">
<Setter TargetName="icon" Property="Template" Value="{StaticResource addImage}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListView.resources>

it should work if not, then check the image file path

Serkan us

Add comment


(Will show your Gravatar icon)  

  Country flag




Live preview

November 22. 2008 00:44

Gravatar

Powered by BlogEngine.NET 1.1.0.7
Theme by Mads Kristensen

Robert D. Sondles

Name of author Robert Sondles
is an independent software engineer operating under his company named Blueberry Island Media ltd. He started the company in 2003 and has been developing softare with .Net since 2002. He is located in Philadelphia, PA..

E-mail me Send mail

Calendar

<<  November 2008  >>
MoTuWeThFrSaSu
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

View posts in large calendar

Recent comments

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008

Sign in