I want to set some text where the value is not null.
XAML:
<TextBlock >
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="solved"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Report}" Value="{x:Null}">
<Setter Property="Text" Value=""/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Note: Here Report is STRING which has some random value like (e.g 11,112,11a)
it shows solved in every row which seems that data trigger is not working.
but its working with this code with this random value (e.g 11,112,11a)
<TextBlock Text="{Binding Report}"/>
I want Solved as text instead of that random values (e.g 11,112,11a) otherwise blank where there is no value.
You could use a converter like so:
xaml:
xmlns:cnv="clr-namespace:Converters"
...
<TextBlock Text="{Binding Report, Converter={cnv:CustomNullToStringConverter NotNullValue=Solved,NullValue=''}}"/>
Converter:
class CustomNullToStringConverter : MarkupExtension, IValueConverter
{
public string NullValue { get; set; }
public string NotNullValue { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string && String.IsNullOrEmpty(value as string)) return NullValue;
if (value == null) return NullValue;
return NotNullValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Related
How can I display tool tip as a list of values binded to one of the column of a grid having comma separated string value in WPF.
I can bind it to a column which is showing comma separated values.
<ctl:FieldHelp.Columns>
<data:DataGridTextColumn Binding="{Binding ID, Mode=TwoWay}" IsReadOnly="True">
<data:DataGridTextColumn.CellStyle>
<Style TargetType="data:DataGridCell" >
<Setter Property="ToolTipService.ToolTip" Value="{Binding Details}"></Setter>
</Style>
</data:DataGridTextColumn.CellStyle>
</data:DataGridTextColumn>
<data:DataGridTextColumn Binding="{Binding Name, Mode=TwoWay}" IsReadOnly="True"/>
<data:DataGridTextColumn Binding="{Binding Description, Mode=TwoWay}" IsReadOnly="True"/>
Use a value converter. You hav to create a class which implements the IValueConverter interface.
public class TooltipStringListConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// Process your comma seperated string here
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// You can leave this empty
}
}
In your XAML change
<Setter Property="ToolTipService.ToolTip" Value="{Binding Details}"></Setter>
To this:
<Setter Property="ToolTipService.ToolTip" Value="{Binding Details, Converter={StaticResource TooltipStringListConverter}}"></Setter>
Be sure that you use the correct namespace of the converter.
I have not tested it, but i'm sure this is the way to go.
Edit:
I forgot the include the converter in the xaml like Celso Livero did by adding
<Window ...
xmlns:converters="clr-namespace:NamespaceOfYourConverter"
>
<Window.Resources>
<converters:TooltipStringListConverter x:Key="TooltipStringListConverter"/>
</Window.Resources>
Here, how to use a converter:
change:
Value="{Binding Details}" => Value="{Binding Details, Converter={StaticResource CommaStringConverter}}"
Create your converter
public class CommaStringConverter : BaseConverter, IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return string.Join("\n", value.Split(','));
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Add it to your app.xaml file
<Application
.....
xmlns:converters="clr-namespace:YourAppNameSpace.YourConvertersFolder"
...... >
<Application.Resources>
<converters:CommaStringConverter x:Key="CommaStringConverter " />
...........
</Application.Resources>
</Application>
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.
How can I bind a value of string Y or N to a isEnabled Value?
<TextBox IsEnabled="{Binding Path=StringValueFromSomeEntity, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
StringValueFromSomeEntity can be a Y or N value.
Use a converter to convert the string to a bool-value:
public class StringToBoolConverter:IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value.ToString().ToLower() == "y")
return true;
return false;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
return "Y";
return "N";
}
}
Refer to it in the resources:
<Window.Resources>
<conv:StringToBoolConverter x:Key="StringToBool"/>
</Window.Resources>
Apply it to your binding (if you just want to change the IsEnabled property according to your string, use Mode=OneWay, but if you really want to bind TwoWay you need the ConvertBack-method):
<TextBox IsEnabled="{Binding Path=StringValueFromSomeEntity, Mode=OneWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource ResourceKey=StringToBool}"/>
You can create an IValueConverter subclass like this:
public class YNBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value as string) == 'Y';
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value ? 'Y' : 'N';
}
}
You need ConvertBack if you are expecting to bind TwoWay.
Then add it to the resources of your page.
And add it to the binding
{Binding Path=StringValueFromSomeEntity, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged, Converter={StaticResource YNBoolConverter}}
By default string cann't be converted into Boolen type so you have to tell WPF how to convert and take the value in place where you want to have.
here are two ways to implement this.
Using ValueConverter (prefered way)
Add a Converter into your project like below.
public class StringToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string val = System.Convert.ToString(value).ToUpper();
if (string.IsNullOrWhiteSpace(val))
return false;
return val == "Y" ? true : false;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Add the namespace into you window and add resource
<Window.Resources>
<convrter:StringToBoolConverter x:Key="stringtoboolConverter"/>
</Window.Resources>
Now refrance this convert into IsEnabled Propery of Checkbox.
<GroupBox Header="With Converter" >
<StackPanel>
<TextBox x:Name="txt1" Text="Y" />
<CheckBox IsEnabled="{Binding ElementName=txt1,Path=Text,Converter={StaticResource stringtoboolConverter}}" />
</StackPanel>
</GroupBox>
Using Style / Triggers (alternative way)
<TextBox x:Name="txt" Text="Y" />
<CheckBox Content="IsEnabled" Tag="{Binding ElementName=txt,Path=Text}" >
<CheckBox.Style>
<Style TargetType="{x:Type CheckBox}">
<Style.Triggers>
<Trigger Property="Tag" Value="Y" >
<Setter Property="IsEnabled" Value="true"/>
</Trigger>
<Trigger Property="Tag" Value="N" >
<Setter Property="IsEnabled" Value="false"/>
</Trigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
Use a ValueConverter.
Create a class which implements IValueConverter
for example
public class StringToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string value = (string)value;
return value.Trim().ToLower() == "y";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
After that you can it applay to your binding
I have a class having a Boolean member and I want to populate a Wpf ListBox with a collection of my class.
I want the background of the listboxitem to a different color if my boolean property is false. Is it possible with XAML ? What is the best way to do that ?
class Mi
{
public bool mybool{get;set;}
}
...
List<Mi> mycollection;// the datacontext
You could use a DataTrigger:
<DataTemplate DataType="{x:Type my:Mi}">
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter PropertyName="Background" Value="White" />
<Style.Triggers>
<DataTrigger Binding="{Binding mybool}" Value="True">
<Setter PropertyName="Background" Value="Yellow" />
</DataTrigger>
</Style.Triggers>
</Style>
<Grid.Style>
... your ListBoxItem contents here ...
</Grid>
</DataTemplate>
Here's a quick general converter for booleans that allows you to specify a value for true and something different for false for properties of any type.
[ValueConversion(typeof(bool), typeof(object))]
public class BooleanValueConverter : IValueConverter
{
public object FalseValue { get; set; }
public object TrueValue { get; set; }
#region IValueConverter Members
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return (bool)value ? this.TrueValue : this.FalseValue;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return object.Equals(this.TrueValue, value) ? true : false;
}
#endregion
}
Use it like so.
<SolidColorBrush x:Key="TrueBrush" Color="Green" />
<SolidColorBrush x:Key="FalseBrush" Color="Red" />
<local:BooleanValueConverter x:Key="BooleanBackground"
TrueValue="{StaticResource TrueBrush}"
FalseValue="{StaticResource FalseBrush}" />
...
Background="{Binding Path=Some.PropertyPath.Ending.With.A.Boolean,
Converter={StaticResource BooleanBackground}}" />
You could achieve this with a DataTemplateSelector, having two templates with differing backgrounds.
A better way would probably be to bind the the background property to your boolean and use an IValueConverter which would return an appropriate colour.
Background="{Binding Path=mybool, Converter={StaticResource boolToBackgroundConverter}}"
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" />