Convertor on ImageSource - convert image to grayscale - wpf

I have multibinding on Image control. I bind two properties one is type of bool(IsLogged) and one is typeof Uri (ProfilePhoto).
XAML:
<Image.Source >
<MultiBinding Converter="{StaticResource avatarConverter}">
<Binding Path="ProfilePhoto"></Binding>
<Binding Path="StatusInfo.IsLogged"></Binding>
</MultiBinding>
</Image.Source>
</Image>
I create converter, which convert BitmapImage to gray scale if property IsLogged is false.
It look like this:
public class AvatarConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var image = values[0] as BitmapImage;
string s = values[1].ToString();
bool isLogged = System.Convert.ToBoolean(s);
if (!isLogged)
{
try
{
if (image != null)
{
var grayBitmapSource = new FormatConvertedBitmap();
grayBitmapSource.BeginInit();
grayBitmapSource.Source = image;
grayBitmapSource.DestinationFormat = PixelFormats.Gray32Float;
grayBitmapSource.EndInit();
return grayBitmapSource;
}
return null;
}
catch (Exception ex)
{
throw ex;
}
}
return image;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
It works good if only I bind on image source property type fo BitmapImage, but I need bind property type of Uri.
I have a fear of the creation variable BitmapImage in converter and as source use Uri.
An return this variable as Source of image. I think this is not ideal way. Maybe I am wrong.
What is your opinion
Some elegant solution?

Although you can do it with a converter, there is a much better option: using a shader effect. You'll find an implementation of a GreyscaleEffect on this page.
<Style x:Key="grayedIfNotLogged" TargetType="Image">
<Style.Triggers>
<DataTrigger Binding="{Binding StatusInfo.IsLogged}" Value="False">
<Setter Property="Effect">
<Setter.Value>
<fx:GrayscaleEffect />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
...
<Image Source="..." Style="{StaticResource grayedIfNotLogged}" />

Related

Workaround for binding a property of type 'Binding' in WPF [duplicate]

Is there a way I could do this in a Style:
<Style TargetType="FrameworkElement">
<Setter Property="Visibility">
<Setter.Value>
<Binding Path="Tag"
RelativeSource="{RelativeSource AncestorType=UserControl}"
Converter="{StaticResource AccessLevelToVisibilityConverter}"
ConverterParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag}" />
</Setter.Value>
</Setter>
</Style>
I simply need to send the Tag of top-level parent and the Tag of control itself to my converter class.
The ConverterParameter property can not be bound because it is not a dependency property.
Since Binding is not derived from DependencyObject none of its properties can be dependency properties. As a consequence, a Binding can never be the target object of another Binding.
There is however an alternative solution. You could use a MultiBinding with a multi-value converter instead of a normal Binding:
<Style TargetType="FrameworkElement">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource AccessLevelToVisibilityConverter}">
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=FindAncestor,
AncestorType=UserControl}"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
The multi-value converter gets an array of source values as input:
public class AccessLevelToVisibilityConverter : IMultiValueConverter
{
public object Convert(
object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.All(v => (v is bool && (bool)v))
? Visibility.Visible
: Visibility.Hidden;
}
public object[] ConvertBack(
object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
No, unfortunately this will not be possible because ConverterParameter is not a DependencyProperty so you won't be able to use bindings
But perhaps you could cheat and use a MultiBinding with IMultiValueConverter to pass in the 2 Tag properties.
There is also an alternative way to use MarkupExtension in order to use Binding for a ConverterParameter. With this solution you can still use the default IValueConverter instead of the IMultiValueConverter because the ConverterParameter is passed into the IValueConverter just like you expected in your first sample.
Here is my reusable MarkupExtension:
/// <summary>
/// <example>
/// <TextBox>
/// <TextBox.Text>
/// <wpfAdditions:ConverterBindableParameter Binding="{Binding FirstName}"
/// Converter="{StaticResource TestValueConverter}"
/// ConverterParameter="{Binding ConcatSign}" />
/// </TextBox.Text>
/// </TextBox>
/// </example>
/// </summary>
[ContentProperty(nameof(Binding))]
public class ConverterBindableParameter : MarkupExtension
{
#region Public Properties
public Binding Binding { get; set; }
public BindingMode Mode { get; set; }
public IValueConverter Converter { get; set; }
public Binding ConverterParameter { get; set; }
#endregion
public ConverterBindableParameter()
{ }
public ConverterBindableParameter(string path)
{
Binding = new Binding(path);
}
public ConverterBindableParameter(Binding binding)
{
Binding = binding;
}
#region Overridden Methods
public override object ProvideValue(IServiceProvider serviceProvider)
{
var multiBinding = new MultiBinding();
Binding.Mode = Mode;
multiBinding.Bindings.Add(Binding);
if (ConverterParameter != null)
{
ConverterParameter.Mode = BindingMode.OneWay;
multiBinding.Bindings.Add(ConverterParameter);
}
var adapter = new MultiValueConverterAdapter
{
Converter = Converter
};
multiBinding.Converter = adapter;
return multiBinding.ProvideValue(serviceProvider);
}
#endregion
[ContentProperty(nameof(Converter))]
private class MultiValueConverterAdapter : IMultiValueConverter
{
public IValueConverter Converter { get; set; }
private object lastParameter;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (Converter == null) return values[0]; // Required for VS design-time
if (values.Length > 1) lastParameter = values[1];
return Converter.Convert(values[0], targetType, lastParameter, culture);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (Converter == null) return new object[] { value }; // Required for VS design-time
return new object[] { Converter.ConvertBack(value, targetTypes[0], lastParameter, culture) };
}
}
}
With this MarkupExtension in your code base you can simply bind the ConverterParameter the following way:
<Style TargetType="FrameworkElement">
<Setter Property="Visibility">
<Setter.Value>
<wpfAdditions:ConverterBindableParameter Binding="{Binding Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}"
Converter="{StaticResource AccessLevelToVisibilityConverter}"
ConverterParameterBinding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag}" />
</Setter.Value>
</Setter>
Which looks almost like your initial proposal.

WPF dynamically change image source based on two values [duplicate]

Is there a way I could do this in a Style:
<Style TargetType="FrameworkElement">
<Setter Property="Visibility">
<Setter.Value>
<Binding Path="Tag"
RelativeSource="{RelativeSource AncestorType=UserControl}"
Converter="{StaticResource AccessLevelToVisibilityConverter}"
ConverterParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag}" />
</Setter.Value>
</Setter>
</Style>
I simply need to send the Tag of top-level parent and the Tag of control itself to my converter class.
The ConverterParameter property can not be bound because it is not a dependency property.
Since Binding is not derived from DependencyObject none of its properties can be dependency properties. As a consequence, a Binding can never be the target object of another Binding.
There is however an alternative solution. You could use a MultiBinding with a multi-value converter instead of a normal Binding:
<Style TargetType="FrameworkElement">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource AccessLevelToVisibilityConverter}">
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=FindAncestor,
AncestorType=UserControl}"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
The multi-value converter gets an array of source values as input:
public class AccessLevelToVisibilityConverter : IMultiValueConverter
{
public object Convert(
object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.All(v => (v is bool && (bool)v))
? Visibility.Visible
: Visibility.Hidden;
}
public object[] ConvertBack(
object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
No, unfortunately this will not be possible because ConverterParameter is not a DependencyProperty so you won't be able to use bindings
But perhaps you could cheat and use a MultiBinding with IMultiValueConverter to pass in the 2 Tag properties.
There is also an alternative way to use MarkupExtension in order to use Binding for a ConverterParameter. With this solution you can still use the default IValueConverter instead of the IMultiValueConverter because the ConverterParameter is passed into the IValueConverter just like you expected in your first sample.
Here is my reusable MarkupExtension:
/// <summary>
/// <example>
/// <TextBox>
/// <TextBox.Text>
/// <wpfAdditions:ConverterBindableParameter Binding="{Binding FirstName}"
/// Converter="{StaticResource TestValueConverter}"
/// ConverterParameter="{Binding ConcatSign}" />
/// </TextBox.Text>
/// </TextBox>
/// </example>
/// </summary>
[ContentProperty(nameof(Binding))]
public class ConverterBindableParameter : MarkupExtension
{
#region Public Properties
public Binding Binding { get; set; }
public BindingMode Mode { get; set; }
public IValueConverter Converter { get; set; }
public Binding ConverterParameter { get; set; }
#endregion
public ConverterBindableParameter()
{ }
public ConverterBindableParameter(string path)
{
Binding = new Binding(path);
}
public ConverterBindableParameter(Binding binding)
{
Binding = binding;
}
#region Overridden Methods
public override object ProvideValue(IServiceProvider serviceProvider)
{
var multiBinding = new MultiBinding();
Binding.Mode = Mode;
multiBinding.Bindings.Add(Binding);
if (ConverterParameter != null)
{
ConverterParameter.Mode = BindingMode.OneWay;
multiBinding.Bindings.Add(ConverterParameter);
}
var adapter = new MultiValueConverterAdapter
{
Converter = Converter
};
multiBinding.Converter = adapter;
return multiBinding.ProvideValue(serviceProvider);
}
#endregion
[ContentProperty(nameof(Converter))]
private class MultiValueConverterAdapter : IMultiValueConverter
{
public IValueConverter Converter { get; set; }
private object lastParameter;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (Converter == null) return values[0]; // Required for VS design-time
if (values.Length > 1) lastParameter = values[1];
return Converter.Convert(values[0], targetType, lastParameter, culture);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (Converter == null) return new object[] { value }; // Required for VS design-time
return new object[] { Converter.ConvertBack(value, targetTypes[0], lastParameter, culture) };
}
}
}
With this MarkupExtension in your code base you can simply bind the ConverterParameter the following way:
<Style TargetType="FrameworkElement">
<Setter Property="Visibility">
<Setter.Value>
<wpfAdditions:ConverterBindableParameter Binding="{Binding Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}"
Converter="{StaticResource AccessLevelToVisibilityConverter}"
ConverterParameterBinding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag}" />
</Setter.Value>
</Setter>
Which looks almost like your initial proposal.

Pass user settings into converterparameter in wpf [duplicate]

Is there a way I could do this in a Style:
<Style TargetType="FrameworkElement">
<Setter Property="Visibility">
<Setter.Value>
<Binding Path="Tag"
RelativeSource="{RelativeSource AncestorType=UserControl}"
Converter="{StaticResource AccessLevelToVisibilityConverter}"
ConverterParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag}" />
</Setter.Value>
</Setter>
</Style>
I simply need to send the Tag of top-level parent and the Tag of control itself to my converter class.
The ConverterParameter property can not be bound because it is not a dependency property.
Since Binding is not derived from DependencyObject none of its properties can be dependency properties. As a consequence, a Binding can never be the target object of another Binding.
There is however an alternative solution. You could use a MultiBinding with a multi-value converter instead of a normal Binding:
<Style TargetType="FrameworkElement">
<Setter Property="Visibility">
<Setter.Value>
<MultiBinding Converter="{StaticResource AccessLevelToVisibilityConverter}">
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=FindAncestor,
AncestorType=UserControl}"/>
<Binding Path="Tag" RelativeSource="{RelativeSource Mode=Self}"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
The multi-value converter gets an array of source values as input:
public class AccessLevelToVisibilityConverter : IMultiValueConverter
{
public object Convert(
object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.All(v => (v is bool && (bool)v))
? Visibility.Visible
: Visibility.Hidden;
}
public object[] ConvertBack(
object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
No, unfortunately this will not be possible because ConverterParameter is not a DependencyProperty so you won't be able to use bindings
But perhaps you could cheat and use a MultiBinding with IMultiValueConverter to pass in the 2 Tag properties.
There is also an alternative way to use MarkupExtension in order to use Binding for a ConverterParameter. With this solution you can still use the default IValueConverter instead of the IMultiValueConverter because the ConverterParameter is passed into the IValueConverter just like you expected in your first sample.
Here is my reusable MarkupExtension:
/// <summary>
/// <example>
/// <TextBox>
/// <TextBox.Text>
/// <wpfAdditions:ConverterBindableParameter Binding="{Binding FirstName}"
/// Converter="{StaticResource TestValueConverter}"
/// ConverterParameter="{Binding ConcatSign}" />
/// </TextBox.Text>
/// </TextBox>
/// </example>
/// </summary>
[ContentProperty(nameof(Binding))]
public class ConverterBindableParameter : MarkupExtension
{
#region Public Properties
public Binding Binding { get; set; }
public BindingMode Mode { get; set; }
public IValueConverter Converter { get; set; }
public Binding ConverterParameter { get; set; }
#endregion
public ConverterBindableParameter()
{ }
public ConverterBindableParameter(string path)
{
Binding = new Binding(path);
}
public ConverterBindableParameter(Binding binding)
{
Binding = binding;
}
#region Overridden Methods
public override object ProvideValue(IServiceProvider serviceProvider)
{
var multiBinding = new MultiBinding();
Binding.Mode = Mode;
multiBinding.Bindings.Add(Binding);
if (ConverterParameter != null)
{
ConverterParameter.Mode = BindingMode.OneWay;
multiBinding.Bindings.Add(ConverterParameter);
}
var adapter = new MultiValueConverterAdapter
{
Converter = Converter
};
multiBinding.Converter = adapter;
return multiBinding.ProvideValue(serviceProvider);
}
#endregion
[ContentProperty(nameof(Converter))]
private class MultiValueConverterAdapter : IMultiValueConverter
{
public IValueConverter Converter { get; set; }
private object lastParameter;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (Converter == null) return values[0]; // Required for VS design-time
if (values.Length > 1) lastParameter = values[1];
return Converter.Convert(values[0], targetType, lastParameter, culture);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (Converter == null) return new object[] { value }; // Required for VS design-time
return new object[] { Converter.ConvertBack(value, targetTypes[0], lastParameter, culture) };
}
}
}
With this MarkupExtension in your code base you can simply bind the ConverterParameter the following way:
<Style TargetType="FrameworkElement">
<Setter Property="Visibility">
<Setter.Value>
<wpfAdditions:ConverterBindableParameter Binding="{Binding Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}"
Converter="{StaticResource AccessLevelToVisibilityConverter}"
ConverterParameterBinding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Tag}" />
</Setter.Value>
</Setter>
Which looks almost like your initial proposal.

DataTrigger for ItemsControl's ItemTemplate triggering on the last item in the list

I Have an ItemsControl with a defined ItemTemplate.
The ItemTemplate contains an image.
The Image has a style trigger to set the image.
What binding can I use to trigger a different image to be displayed if the item is the last in the list?
While the simplest approach for this is still probably to add an indicator field at the VM / collection level and refer to that property in the Trigger, there are some alternatives too.
One such could be to use the AlternationCount of the ItemsControl. This approach will make sure you're last item's special style is retained when changes are made to the source collection like when adding/removing/ sorting and sorts.
In the ItemsControl set AlternationCount="{Binding Items.Count}" (Items is the collection being bound to)
As for the trigger in ItemTemplate to detect the last item, it's a bit more complicated.
xaml:
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="(ItemsControl.AlternationIndex)"
RelativeSource="{RelativeSource FindAncestor,
AncestorType=ContentPresenter}" />
<Binding Path="ItemsSource.Count"
RelativeSource="{RelativeSource FindAncestor,
AncestorType=ItemsControl}" />
</MultiBinding>
</DataTrigger.Binding>
<!-- Change the following setter to what you need for new image -->
<Setter Property="Foreground"
Value="Tomato" />
</DataTrigger>
and the converter used:
internal class MyConverter : IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
bool result;
try {
result = System.Convert.ToInt32(values[0]) == System.Convert.ToInt32(values[1]) - 1;
} catch (Exception) {
result = false;
}
return result;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}

WPF TextBlock Negative Number In Red

I am trying to figure out the best way to create a style/trigger to set foreground to Red, when value is < 0. what is the best way to do this? I'm assuming DataTrigger, but how can I check for negative value, do i have to create my own IValueConverter?
If you are not using an MVVM model (where you may have a ForegroundColor property), then the easiest thing to do is to create a new IValueConverter, binding your background to your value.
In MyWindow.xaml:
<Window ...
xmlns:local="clr-namespace:MyLocalNamespace">
<Window.Resources>
<local:ValueToForegroundColorConverter x:Key="valueToForeground" />
<Window.Resources>
<TextBlock Text="{Binding MyValue}"
Foreground="{Binding MyValue, Converter={StaticResource valueToForeground}}" />
</Window>
ValueToForegroundColorConverter.cs
using System;
using System.Windows.Media;
using System.Windows.Data;
namespace MyLocalNamespace
{
class ValueToForegroundColorConverter: IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
SolidColorBrush brush = new SolidColorBrush(Colors.Black);
Double doubleValue = 0.0;
Double.TryParse(value.ToString(), out doubleValue);
if (doubleValue < 0)
brush = new SolidColorBrush(Colors.Red);
return brush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
}
You should have your view specific information in your ViewModel. But you can get rid of the Style specific information in the ViewModel.
Hence create a property in the ViewModel which would return a bool value
public bool IsMyValueNegative { get { return (MyValue < 0); } }
And use it in a DataTrigger so that you can eliminate the ValueConverter and its boxing/unboxing.
<TextBlock Text="{Binding MyValue}">
<TextBlock.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsMyValueNegative}" Value="True">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
For Amsakanna's solution I had to add a class name to the Property Setter:
<Setter Property="TextBlock.Foreground" Value="Red" />

Resources