WPF ListView Object DataTrigger - c#

I'm trying to style a rectangle inside of a listview item, based on a data-field of the listview item object.
To return a boolean I'm converting the integer from daydata.workload to a boolean uasing a IValueConverter.
I'm getting no exception, the rectangle is just not affected by the DataTrigger. The other style rules are working fine.
<Window.Resources>
<cv:numConverter x:Key="capacityConverter" />
<Window.Resources>
-
<ListView Name="weekView" ItemsSource="{Binding dayList}" ItemTemplate="{StaticResource DefaultTemplate}" >
<ListView.Resources>
<Style TargetType="Rectangle" x:Key="capacityBG">
<Setter Property="Stroke" Value="#FFE2E2E2" />
<Setter Property="Width" Value="180" />
<Setter Property="Height" Value="10" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=dayList.workload, Converter={StaticResource capacityConverter}, ConverterParameter=12}">
<DataTrigger.Value>true</DataTrigger.Value>
<Setter Property="Fill" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.Resources>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
-
<Rectangle Style="{DynamicResource capacityBG}" VerticalAlignment="Top" Grid.Row="0" />
-
public class numConverter : IValueConverter
{
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((int)value) > val;
}
object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public int val { get; set; }
}
-
public class dayData
{
public DateTime date { get; set; }
public int workload { get; set; }
public List<job> jobs { get; set; }
}

The problem here is that values entered for the converter parameter and the data trigger value are treated as string. You need to specify the type for each one of these values like shown below:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
<Style.Triggers>
<DataTrigger>
<DataTrigger.Binding>
<Binding Path="WorkLoad" Converter="{StaticResource capacityConverter}">
<Binding.ConverterParameter>
<sys:Int32>12</sys:Int32>
</Binding.ConverterParameter>
</Binding>
</DataTrigger.Binding>
<DataTrigger.Value>
<sys:Boolean>true</sys:Boolean>
</DataTrigger.Value>
<Setter Property="Fill" Value="Red"/>
</DataTrigger>
</Style.Triggers>
Then you can cast converter parameter to an int to make the comparison.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value > (int)parameter;
}

Related

Conditional tooltip for DataGridTextColumn

I am currently facing a small issue with my DataGridTextColumn.
I want to display a tooltip at a DataGridTextColumn but only if the text is not empty.
How can I achieve this? The code that I am currently using:
<DataGridTextColumn IsReadOnly="True" Header="Person" Binding="{Binding SomeBinding, TargetNullValue='-'}" Width="Auto"
CellStyle="{StaticResource SomeStyle}"/>
With the style
<Style x:Key="SomeStyle"
TargetType="DataGridCell" BasedOn="{StaticResource InactiveStyle}">
<Style.Setters>
<Setter Property="ToolTip" Value="{Binding Path=SomeBinding}"/>
</Style.Setters>
</Style>
This code does provide me the tooltip, however, it is also showing the tooltip when there is no text. If there are any questions, please let me know and I can help you.
Try to add data triggers for string.Empty and null:
<Style x:Key="SomeStyle" TargetType="DataGridCell"
BasedOn="{StaticResource InactiveStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=SomeBinding}" Value="">
<Setter Property="ToolTip" Value="{x:Null}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=SomeBinding}" Value="{x:Null}">
<Setter Property="ToolTip" Value="{x:Null}"/>
</DataTrigger>
</Style.Triggers>
<Style.Setters>
<Setter Property="ToolTip" Value="{Binding Path=SomeBinding}"/>
</Style.Setters>
</Style>
Here is an approach :
//Create a class which inherits from IValueConverter
public class CellToolTipConverter : IValueConverter
{
#region IValueConverter Membres
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string stringValue = (string)value;
if (!string.IsNullOrEmpty(stringValue))
return "Your tooltip";//As you are in a c# class, you have many possibilities.
else
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
//In your xaml :
//Declare your namespace
xmlns:CustomClasses="clr-namespace:YourAssamblyName.YourNameSpaceOfConverterClass"
<UserControl.Resources>
<CustomClasses:CellToolTipConverter x:Key="CustomToolTipConverter"/>
</UserControl.Resources>
//In your grid view
<GridView.RowStyle>
<Style TargetType="{x:Type telerik:GridViewRow}">
<Setter Property="MyCustomToolTipProperty" Value="{Binding YourProperty, Converter=
{StaticResource CustomToolTipConverter}}"/>
</Style>
</GridView.RowStyle>

I want to change the color in a WPF ListView when column contains certain words

I know how to change colors if certain values match, as shown below.
How can I do for change foreground color not only same, but also values are included.
(Like String.Contain(Value))
<GridViewColumn Header="Permission" Width="170" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding permission}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding permission}" Value="Pass">
<Setter Property="Foreground" Value="#4c72cc"/>
</DataTrigger>
<DataTrigger Binding="{Binding permission}" Value="Fail">
<Setter Property="Foreground" Value="#ef6eab"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
To achieve that there are two ways:
Method 1
Either define two converters that check whether your string contains the "Pass"/"Fail" values:
public class PassConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value as string)?.Contains("Pass");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class FailConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (value as string)?.Contains("Fail");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Add the converters to your static resources:
<Window.Resources>
<YourNameSpace:PassConverter x:Key="PassConverter"></local:PassConverter>
<YourNameSpace:FailConverter x:Key="FailConverter"></local:FailConverter>
</Window.Resources>
And update your Triggers to use them:
<GridViewColumn Header="Permission" Width="170" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Permission}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=permission, Converter={StaticResource PassConverter}}">
<DataTrigger.Value>True</DataTrigger.Value>
<Setter Property="Foreground" Value="#4c72cc"/>
</DataTrigger>
<DataTrigger Binding="{Binding permission,Converter={StaticResource FailConverter}}" >
<DataTrigger.Value>True</DataTrigger.Value>
<Setter Property="Foreground" Value="#ef6eab"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Method 2
You could also use a single MultiValueConverter and pass either "Pass" or "Fail" strings with the permission value and check like so:
<GridViewColumn Header="Permission" Width="170" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Permission}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger>
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource PassFailConverter}">
<Binding Path="permission"/>
<Binding>
<Binding.Source>
<system:String>
Pass
</system:String>
</Binding.Source>
</Binding>
</MultiBinding>
</DataTrigger.Binding>
<DataTrigger.Value>True</DataTrigger.Value>
<Setter Property="Foreground" Value="#4c72cc"/>
</DataTrigger>
<DataTrigger>
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource PassFailConverter}">
<Binding Path="permission"/>
<Binding>
<Binding.Source>
<system:String>
Fail
</system:String>
</Binding.Source>
</Binding>
</MultiBinding>
</DataTrigger.Binding>
<DataTrigger.Value>True</DataTrigger.Value>
<Setter Property="Foreground" Value="#ef6eab"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
And the multivalueconverter should look something like that:
public class PassFailConverter:IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return (values[0] as string).Contains(values[1] as string);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Don't forget to include the converter in your static resources:
<YourNameSpace:PassFailConverter x:Key="PassFailConverter"></local:PassFailConverter>
You can use value converter for that (IValueConverter) and binding color directly to "permission" and convert to color

How can I reset the ThemeResource of DataTemplate?

I have defined a DataTemplate that includes a textbox. In "glove mode" I need a large font/minHeight so the touch screen works nicely, but in "office mode" I want different set of values. I believe this should be possible, but can't figure it out.
How can I modify the theme in the code behind? Or if this is totally wrong, how should I do it?
Styles:
<Style x:Key="GloveTextBoxStyle" TargetType="TextBox">
<Setter Property="FontSize" Value="30"/>
<Setter Property="MinHeight" Value="60"/>
</Style>
<Style x:Key="OfficeTextBoxStyle" TargetType="TextBox">
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinHeight" Value="30"/>
</Style>
DataTemplate:
<DataTemplate x:Key="InspectionItemStringTemplate" x:DataType="data:InspectionItem"><TextBox Text="{x:Bind NewValue,Mode=TwoWay}"
x:Name="MyTextBox"
x:Phase="1" Style="{ThemeResource GloveTextBoxStyle}"/></DataTemplate>
What about IValueConverter?
You can create something like that:
public class TextBoxStyleConverter : IValueConverter
{
public Style GloveTextBoxStyle { get; set; }
public Style OfficeTextBoxStyle { get; set; }
public object Convert(object value, Type targetType, object parameter, string language)
{    
// analyze binded value and return needed style
return condition ? GloveTextBoxStyle : OfficeTextBoxStyle;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
and in your XAML code
<local:TextBoxStyleConverter x:Key="TextBoxStyleConverter">
<local:TextBoxStyleConverter.GloveTextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="30"/>
<Setter Property="MinHeight" Value="60"/>
</Style>
</local:TextBoxStyleConverter.GloveTextBoxStyle>
<local:TextBoxStyleConverter.OfficeTextBoxStyle>
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="14"/>
<Setter Property="MinHeight" Value="30"/>
</Style>
</local:TextBoxStyleConverter.OfficeTextBoxStyle>
</local:TextBoxStyleConverter>
<DataTemplate x:Key="InspectionItemStringTemplate"
x:DataType="data:InspectionItem">
<TextBox Text="{x:Bind NewValue,Mode=TwoWay}"
x:Name="MyTextBox"
x:Phase="1"
Style="{Binding Converter={StaticResource TextBoxStyleConverter}}"/>
</DataTemplate>

Is it possisble to avoid using converter in multibinding

I came across a situation where I want to avoid using converter in multibinding, below is the xaml source snippet from my current code. Below code works perfectly fine, but IS IT POSSIBLE to avoid converter first place ??
ViewModel:
public MainViewModel()
{
Cars = new List<string>() { "Audi", "BMW", "Ferrari", "Ford" };
Models = new List<string>() { "Model 1", "Model 2" };
IsOptionEnable = false;
}
public bool IsOptionEnable { get; private set; }
public List<string> Models { get; private set; }
public List<string> Cars { get; private set; }
Main window xaml:
<Grid>
<ComboBox HorizontalAlignment="Left" VerticalAlignment="Top" Width="120" Margin="87.2,44.8,0,0"
ItemsSource="{Binding Cars}"
SelectedItem="{Binding SelectedItm}"
Style="{StaticResource ModelsComboBox}">
</ComboBox>
</Grid>
Resource dictionary:
<Style x:Key="ModelsComboBox" TargetType="ComboBox">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ComboBoxItem">
<Setter Property="IsEnabled">
<Setter.Value>
<MultiBinding Converter="{StaticResource ModelToBoolConverter}">
<Binding/>
<Binding Path="DataContext.IsOptionEnable" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
Multivalue converter:
internal sealed class ModelToBoolConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool enable = true;
if ((values[0] != null && values[0] != DependencyProperty.UnsetValue) &&
(values[1] != null && values[1] != DependencyProperty.UnsetValue))
{
var comboboxItemText = values[0] as string;
if ((comboboxItemText == "Ferrari") && (bool)values[1] == false)
{
enable = false;
}
}
return enable;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
}
You can use MultiDataTrigger in this case.
<Style TargetType="ComboBox">
<Setter Property="IsEnabled" Value="True" />
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding}" Value="Ferrai"/>
<Condition Binding="{Binding Path=DataContext.IsOptionEnable, RelativeSource={RelativeSource AncestorType=ComboBox}}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="IsEnabled" Value="False" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
You can use MultiDataTrigger to achieve the same.
Resource dictionary:
<Style x:Key="ModelsComboBox" TargetType="ComboBox">
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ComboBoxItem">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding}" Value="Ferrari"/>
<Condition Binding="{Binding Path=DataContext.IsOptionEnable,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}}"
Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Setter.Value>
</Setter>
</Style>

wpf how to bind to DataContext existence?

I set the datacontext dynamically in code. I would like a button on screen to be enabled/disabled depending if DataContext == null or not. I can do it in code when I assign the DataContext but it would be better if I can bind like that :)
You should be able to use a DataTrigger on the button style to disable your button when the DataContext is null. The other option is to bind the IsEnabled property to the DataContext and use a value converter to return false if DataContext is null and true otherwise.
With trigger:
<Button>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}" Value="{x:Null}">
<Setter Property="IsEnabled" Value="false"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
With converter:
Converter:
public class DataContextSetConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value != null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And use it
<UserControl.Resources>
<local:DataContextSetConverter x:Key="dataContextSetConverter"/>
</UserControl.Resources>
...
<Button IsEnabled="{Binding Path=DataContext, RelativeSource={RelativeSource Self}, Converter={StaticResource dataContextSetConverter}}"/>
This should do it:
<Button Content="ButtonName">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=DataContext}" Value="{x:Null}">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Categories