WPF Change icon depending on binding in Control Template - wpf

I have a control template that I've defined for a DevExpress TextEdit control and I want to change the Image Source property in the template depending on a binding (e.g. IsIncrease).
<ControlTemplate x:Key="WarningTextEdit" TargetType="dxe:TextEdit">
<Grid>
<TextBox Text="{TemplateBinding Text}"/>
<Image Margin="0,0,5,0"
Source="pack://application:,,,/DevExpress.Xpf.Core.v17.2;component/Core/ConditionalFormatting/Images/IconSets/Symbols3_2.png"
Width="17"
Height="16"
RenderOptions.BitmapScalingMode="NearestNeighbor"
HorizontalAlignment="Right"/>
</Grid>
</ControlTemplate>
If the property IsIncrease was set to true then one particular icon should be shown and if the property was set to false then another particular icon should be shown. Anyone know how to do this?
Thanks

You can do it using a converter, you have two choices, either you include 2 Images inside your Grid and you hide/show them using the converter or you use one Image and use the converter to change the Source property (which is probably a better solution). Here's a converter that can handle both situation, followed by both solutions.
BoolConverter :
public class BoolConverter : MarkupExtension, IValueConverter
{
public object TrueValue { get; set; } = Binding.DoNothing;
public object FalseValue { get; set; } = Binding.DoNothing;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is bool))
return Binding.DoNothing;
return (bool)value ? TrueValue : FalseValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == TrueValue)
return true;
if (value == FalseValue)
return false;
return Binding.DoNothing;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
MainWindow.xaml (solution 1 : two Images) :
<ControlTemplate x:Key="WarningTextEdit" TargetType="dxe:TextEdit">
<Grid>
<TextBox Text="{TemplateBinding Text}"/>
<Image Margin="0,0,5,0"
Source="pack://application:,,,/DevExpress.Xpf.Core.v17.2;component/Core/ConditionalFormatting/Images/IconSets/Symbols3_2.png"
Width="17"
Height="16"
RenderOptions.BitmapScalingMode="NearestNeighbor"
HorizontalAlignment="Right"
Visibility="{Binding IsIncrease, Converter={local:BoolConverter TrueValue=Collapsed, FalseValue=Visible}}"/>
<Image Margin="0,0,5,0"
Source="pack://application:,,,/DevExpress.Xpf.Core.v17.2;component/Core/ConditionalFormatting/Images/IconSets/TrafficLights3_1.png"
Width="17"
Height="16"
RenderOptions.BitmapScalingMode="NearestNeighbor"
HorizontalAlignment="Right"
Visibility="{Binding IsIncrease, Converter={local:BoolConverter TrueValue=Visible, FalseValue=Collapsed}}"/>
</Grid>
</ControlTemplate>
MainWindow.xaml (solution 2 : one Image) :
<ControlTemplate x:Key="WarningTextEdit" TargetType="dxe:TextEdit">
<Grid>
<TextBox Text="{TemplateBinding Text}"/>
<Image Margin="0,0,5,0"
Source="{Binding IsIncrease,
Converter={local:BoolConverter TrueValue='pack://application:,,,/DevExpress.Xpf.Core.v17.2;component/Core/ConditionalFormatting/Images/IconSets/Symbols3_2.png',
FalseValue='pack://application:,,,/DevExpress.Xpf.Core.v17.2;component/Core/ConditionalFormatting/Images/IconSets/TrafficLights3_1.png'}}"
Width="17"
Height="16"
RenderOptions.BitmapScalingMode="NearestNeighbor"
HorizontalAlignment="Right"/>
</Grid>
</ControlTemplate>

To achieve that you need 2 properties to store source for image.I'm gonna use Tag property and write an attached property to store 2 image source and use trigger to change the source
public class AttachedProperty
{
public static readonly DependencyProperty AltSourceProperty =
DependencyProperty.RegisterAttached("AltSource",
typeof(string), typeof(AttachedProperty),
new PropertyMetadata());
public static string GetAltSource(DependencyObject obj)
{
return (string)obj.GetValue(AltSourceProperty);
}
public static void SetAltSource(DependencyObject obj, string value)
{
obj.SetValue(AltSourceProperty, value);
}
}
ControlTemplate
<ControlTemplate x:Key="WarningTextEdit" TargetType="dxe:TextEdit">
<Grid>
<TextBox Text="{TemplateBinding Text}" />
<Image x:Name="image"
Width="17"
Height="16"
Margin="0,0,5,0"
HorizontalAlignment="Right"
RenderOptions.BitmapScalingMode="NearestNeighbor" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsIncrease " Value="True">
<Setter TargetName="image" Property="Source" Value="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
<Trigger Property="IsIncrease " Value="False">
<Setter TargetName="image" Property="Source" Value="{Binding Path=(local:AttachedProperty.AltSource), RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<dxe:TextEdit Tag="Image1.jpg" local:AttachedProperty.AltSource="Image2.jpg"/>

Related

To add an ellipse inside date button (Clickable) in calendar control to represent special dates in WPF

Goal: To add a small dot(ellipse) under any special date and also it should be selectable. Idea is to pass a specific date then draw this ellipse under that day.
Progress: I added the ellipse under the date.
Please check the code if it is correct.
Xaml:
<Window.Resources>
<local:CustomLetterDayConverter x:Key="CustomLetterDayConverter" />
<Style x:Key="CalendarDayButtonStyle" TargetType="{x:Type CalendarDayButton}">
<Style.Triggers>
<DataTrigger Binding="{Binding Converter={StaticResource CustomLetterDayConverter}}" Value="{x:Null}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="CalendarDayButton">
<Border Background="{TemplateBinding Background}">
<StackPanel Orientation="Vertical">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" TextBlock.Foreground="{TemplateBinding Foreground}"/>
<Ellipse Height="5" Width="5" Fill="Silver" Stretch="Uniform"/>
</StackPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Calendar x:Name="HolidayCalendar" SelectionMode="MultipleRange" SelectedDate="{Binding SelectedDate}"
CalendarDayButtonStyle="{StaticResource CalendarDayButtonStyle}"
>
<Calendar.BlackoutDates>
<CalendarDateRange Start="01-09-2020" End="05-09-2020"/>
</Calendar.BlackoutDates>
</Calendar>
</Grid>
</Window>
Code-Behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
public class CustomLetterDayConverter : IValueConverter
{
static HashSet<DateTime> dict = new HashSet<DateTime>();
static CustomLetterDayConverter()
{
dict.Add(DateTime.Today);
dict.Add(DateTime.Today.AddDays(1));
dict.Add(DateTime.Today.AddDays(2));
dict.Add(DateTime.Today.AddDays(5));
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string text = null;
if (dict.Contains((DateTime)value))
text = null;
else
text = "";
return text;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Solution Tried
Following this solution I was able to add the special dates. But it is not selectable.
Thanks in advance.
When you override the Template of a control, you're taking responsibility of implementing all the visual states that the control requires.
In your case, you've only implemented a ContentPresenter and your Ellipse, there are no triggers to handle the many other visual states of the CalendarDayButton.
To see what you need to implement, you should use Blend for Visual Studio, add a Calendar control to a sample project, and then extract the control templates, so you can see a full reference template.

WPF showing control with DataTrigger skips it in TabOrder

I would like to show 'additional details' Textbox when the previous TextBox's value is not 0.00 (zero). This is easily accomplished with DataTriggers:
<TextBox Name="FirstTB" Text="{Binding Amount, StringFormat=F2}"/><!--when this TB is not 0-->
<TextBox Name="SecondTB"> <!--show this TB-->
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Amount}" Value="0">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBox Name="ThirdTB"/>
The issue is, however, when changing value of FirstTB to <> 0 and pressing Tab the focus jumps to the ThirdTB instead of SecondTB (even though SecondTB now is Visible due to the DataTrigger). How can I fix this issue?
Sadly, UpdateSourceTrigger=PropertyChanged does not appear to be an option due to its interference with StringFormats - it gives terrible UX when you are editing the value, carret jumps around like crazy due to constant StringFormat evaluation. Viewmodel used in example above:
public class MyVM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private double _amount;
public double Amount
{
get { return _amount; }
set
{
_amount = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Amount)));
}
}
}
Maybe a stupid work around. But this will do the work:
View:
<TextBox Name="SecondTB" IsVisibleChanged="SecondTB_OnIsVisibleChanged">
<!--show this TB-->
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding Amount, Mode=OneWay}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
C#:
private void SecondTB_OnIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (((bool) e.NewValue))
{
if(!(sender is TextBox txbx)) return;
ThirdTB.GotFocus += ThirdTbOnGotFocus;
}
}
private void ThirdTbOnGotFocus(object sender, RoutedEventArgs e)
{
SecondTB.Focus();
ThirdTB.GotFocus -= ThirdTbOnGotFocus;
}
If you use an IValueConverter you get a very elegant way for your problem. Everytime the value is changed, the logic is called.
public class IsZeroToHiddenConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is double d && d == 0)
return Visibility.Hidden;
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
You calso play around and add ConverterParameter to decide if you want Collapsed or Hidden behavior.
Usage
<Grid>
<Grid.Resources>
<local:IsZeroToHiddenConverter x:Key="IsZeroToHiddenConverter"/>
</Grid.Resources>
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox x:Name="Tb1" Grid.Row="0" Width="100" Margin="5" Text="{Binding Path=Amount, StringFormat=F2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox x:Name="Tb2" Grid.Row="1" Width="100" Margin="5" Visibility="{Binding Path=Amount, Converter={StaticResource IsZeroToHiddenConverter}}"/>
<TextBox x:Name="Tb3" Grid.Row="2" Width="100" Margin="5"/>
</Grid>
</Grid>
Preview

Showing same xaml format image inside multiple dataGrid cells by utilising IValueConverter

I have a working solution to convert boolean value into a xaml-image wrapped inside <Viewbox> by using <ContentControl> as follows:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding Recommended, Converter={StaticResource BoolImageConverter}}" Height="20"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
But with the above solution the converted image is only shown in the first cell.
How can I properly use <ControlTemplate> or <Control> in this
case?
I have looked into this answer but I'm unable to reproduce a working solution with the Converter.
Example of the xaml-image
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Viewbox x:Key="Symbols.StarIcon">
<Canvas Width="46" Height="44" >
...
</Canvas>
</Viewbox>
</ResourceDictionary>
The Converter idea is from this post:
public class BoolToImage : IValueConverter
{
public Viewbox TrueImage { get; set; }
public Viewbox FalseImage { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (!(value is bool))
{
return null;
}
bool b = (bool)value;
if (b)
{
return this.TrueImage;
}
else
{
return this.FalseImage;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Your ViewBox is a control with a key. And control can have only one visual parent. So the first cell monopolizes ViewBox, and others can't use it.
In your case it is better to use CellTemplateSelector property.
First, create custom DataTemplateSelector:
public class TrueFalseSelector : DataTemplateSelector
{
public DataTemplate TrueTemplate { get; set; }
public DataTemplate FalseTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item == null) return null;
var isSomething = ((CustomObjectType) item).CustomBoolProperty;
return isSomething ? this.TrueTemplate
: FalseTemplate;
}
}
Then use it in XAML. Add Selector in Resources somewhere:
<local:TrueFalseSelector x:Key="trueFalseSelector">
<local:TrueFalseSelector.TrueTemplate>
<DataTemplate>
<!-- your true template here -->
</DataTemplate>
</local:TrueFalseSelector.TrueTemplate>
<local:TrueFalseSelector.FalseTemplate>
<DataTemplate>
<!-- your false template here -->
</DataTemplate>
</local:TrueFalseSelector.FalseTemplate>
</local:TrueFalseSelector>
And voila:
<DataGrid.Columns>
<DataGridTemplateColumn CellTemplateSelector="{StaticResource trueFalseSelector}" />
</DataGrid.Columns>
EDIT: you can place DataTemplates in the same dictionary where your ViewBox is right now. Give them a key and just use like this:
<local:TrueFalseSelector x:Key="trueFalseSelector"
FalseTemplate="{StaticResource falseTemplate}"
TrueTemplate="{StaticResource trueTemplate">
Here is just another way you can do this (pure xaml):
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Style="{StaticResource RecommendedStyle}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<Style TargetType="ContentControl" x:Key="RecommendedStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ViewBox x:Key="True"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding Recommended}" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ViewBox x:Key="False"/><!--This is image for false-->
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style>

UserControl multiple datatemplate + templateselector

I need to show data inside a usercontrol in different ways depending on a flag.
To achieve this i tried the following, but using this control in the main view shows nothing.
<UserControl DataContext="**self**">
<UserControl.Resources>
<DataTemplate x:Key="mouseInputTemplate">
<TextBlock HorizontalAlignment="Center"><Run Text="{Binding Text}" /></TextBlock>
</DataTemplate>
<DataTemplate x:Key="touchInputTemplate">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Image Source="{Binding ImageUri}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Width="{Binding ImageWidth}" Height="{Binding ImageHeight}" />
<TextBlock HorizontalAlignment="Center"><Run Text="{Binding Text}" /></TextBlock>
</StackPanel>
</DataTemplate>
<local:InputModeDataTemplateSelector x:Key="inputModeTemplateSelector"
MouseInputModeTemplate="{StaticResource mouseInputTemplate}"
TouchInputModeTemplate="{StaticResource touchInputTemplate}" />
</UserControl.Resources>
<ContentControl HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch" ContentTemplateSelector="{StaticResource inputModeTemplateSelector}" />
</UserControl>
What am i doing wrong?
Is there a better way to achieve that?
Thank to EdPlunkett and more research i found out it is better to
use a ContentPresenter here and instead of binding on DataContext=this bind like this (as alsways suggested when writing a UserControl)
DataContext="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type yourType}}}"
Code:
<UserControl.Resources>
<DataTemplate x:Key="touchInputTemplate">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Image Source="{Binding ImageUri}" Width="64" Height="64" />
<TextBlock HorizontalAlignment="Center" Text="{Binding Text}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="mouseInputTemplate">
<TextBlock HorizontalAlignment="Center" Text="{Binding Text}" />
</DataTemplate>
<local:InputModeDataTemplateSelector x:Key="inputModeTemplateSelector"
MouseInputModeTemplate="{StaticResource mouseInputTemplate}"
TouchInputModeTemplate="{StaticResource touchInputTemplate}" />
</UserControl.Resources>
<Grid DataContext="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type yourType}}}">
<ContentPresenter Content="{Binding}" ContentTemplateSelector="{StaticResource inputModeTemplateSelector}">
</Grid>
Your ContentPresenter idea is the correct way to do it with a DataTemplateSelector, and I should have thought of it myself.
But here's yet another way to do it, which unlike my first answer, actually solves all the problems you're having:
XAML (in practice the Style would probably be defined in a separate ResourceDictionary):
<Window
x:Class="TestApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestApplication"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
Title="MainWindow" Height="350" Width="525"
>
<Window.Resources>
<Style TargetType="local:TestControl">
<Setter Property="Background" Value="Gainsboro" />
<Style.Triggers>
<!-- The 0 value for the InputMode enum is Mouse, so this will be the default. -->
<Trigger Property="InputMode" Value="Mouse">
<Setter Property="Background" Value="Wheat" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TestControl}">
<Grid Background="{TemplateBinding Background}">
<TextBlock HorizontalAlignment="Center"><Run Text="{TemplateBinding Text}" /></TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="InputMode" Value="Touch">
<Setter Property="Background" Value="LightSkyBlue" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TestControl}">
<Grid Background="{TemplateBinding Background}">
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Image
Source="{TemplateBinding ImageUri}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Width="{TemplateBinding ImageWidth}"
Height="{TemplateBinding ImageHeight}"
/>
<TextBlock HorizontalAlignment="Center"><Run Text="{TemplateBinding Text}" /></TextBlock>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<local:TestControl
ImageHeight="100"
ImageWidth="100"
Text="This is the test control"
ImageUri="http://www.optimizeagency.com/wp-content/uploads/2015/09/GoogleLogo.jpg"
/>
</Grid>
</Window>
C#:
using System;
using System.Windows;
using System.Windows.Controls;
namespace TestApplication
{
class TestControl : Control
{
public TestControl()
{
// If input mode may change at runtime, you'll need an event that fires when that
// happens and updates this property.
// UIUtilities.GetInputMode() is just a stub in this example.
InputMode = UIUtilities.GetInputMode();
}
#region InputMode Property
public InputMode InputMode
{
get { return (InputMode)GetValue(InputModeProperty); }
set { SetValue(InputModeProperty, value); }
}
public static readonly DependencyProperty InputModeProperty =
DependencyProperty.Register("InputMode", typeof(InputMode), typeof(TestControl),
new PropertyMetadata(InputMode.Mouse));
#endregion InputMode Property
#region Text Property
public String Text
{
get { return (String)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(String), typeof(TestControl),
new PropertyMetadata(null));
#endregion Text Property
#region ImageUri Property
// The TemplateBinding in the template can't coerce a string to an
// ImageSource, so we have to make that happen elsewhere.
public ImageSource ImageUri
{
get { return (ImageSource)GetValue(ImageUriProperty); }
set { SetValue(ImageUriProperty, value); }
}
public static readonly DependencyProperty ImageUriProperty =
DependencyProperty.Register("ImageUri", typeof(ImageSource), typeof(TestControl),
new PropertyMetadata(null));
#endregion ImageUri Property
#region ImageHeight Property
public float ImageHeight
{
get { return (float)GetValue(ImageHeightProperty); }
set { SetValue(ImageHeightProperty, value); }
}
public static readonly DependencyProperty ImageHeightProperty =
DependencyProperty.Register("ImageHeight", typeof(float), typeof(TestControl),
new PropertyMetadata(float.NaN));
#endregion ImageHeight Property
#region ImageWidth Property
public float ImageWidth
{
get { return (float)GetValue(ImageWidthProperty); }
set { SetValue(ImageWidthProperty, value); }
}
public static readonly DependencyProperty ImageWidthProperty =
DependencyProperty.Register("ImageWidth", typeof(float), typeof(TestControl),
new PropertyMetadata(float.NaN));
#endregion ImageWidth Property
}
#region This stuff belongs in a different file
public static class UIUtilities
{
public static InputMode GetInputMode()
{
// Here you'd do whatever you're already doing to detect the input mode
return InputMode.Touch;
}
}
public enum InputMode
{
Mouse,
Touch
}
#endregion This stuff belongs in a different file
}

Show tick image when item is selected in WPF Listbox

I have this simple ListBox which displays a list of images horizontal en vertical.
I have also added a tick image on every image. Now I would like to enable this tick image only when the item is selected in the Listbox.
How can I achieve this?
ListBox XAML:
<ListBox x:Name="PhotoCollection"
Grid.Row="2"
Grid.ColumnSpan="4"
ItemsSource="{Binding PhotoCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectionMode="Multiple">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<Border BorderBrush="White"
BorderThickness="2"
Margin="5"
Background="LightGray">
<Grid>
<Image Source="{Binding}"
Stretch="Uniform"
Width="50"
Height="50"
Margin="5" />
<Image Source="{StaticResource Check_24}"
Visibility="{Binding Converter={StaticResource VisibleConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}, AncestorLevel=1},Path=IsSelected}"
Stretch="Uniform"
Width="20"
Height="20"
Margin="5"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"/>
</Grid>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
EDIT: This line does the trick
Visibility="{Binding Converter={StaticResource VisibleConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}, AncestorLevel=1},Path=IsSelected}"
It should work when you bind the IsSelected property of the ListBoxItem to your property IsVisible. But it depends on how you have implemented your ViewModel and the properties.
<ListBox>
<!-- the rest of the XAML-Definition of your ListBox -->
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding IsVisible, Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
When I need to bind visibility of items to boolean values, I've been using a converter class:
public class BoolToVisibleOrHidden : IValueConverter {
public BoolToVisibleOrHidden() { }
public bool Collapse { get; set; }
public bool Reverse { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
bool bValue = (bool)value;
if (bValue != Reverse) {
return Visibility.Visible;
} else {
if (Collapse)
return Visibility.Collapsed;
else
return Visibility.Hidden;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
Visibility visibility = (Visibility)value;
if (visibility == Visibility.Visible)
return !Reverse;
else
return Reverse;
}
}
After grabbing an instance of it in XAML like so :
<local:BoolToVisibleOrHidden x:Key="BoolToVisConverter" Collapse="True" />
I can bind visibility of items like this:
Visibility="{Binding Converter={StaticResource BoolToVisConverter}, Path=DataContext.PATHTOBOOLEAN}"

Resources