First of all I apologise for my bad english.
I have xml binded to a listview, where each row stands for one person. I want row's background color blue or pink depending on sex element in xml. I've created style with triggers, but they seems to check only first xml node and all rows are colored same as 1st row.
Xml element sex is 0 for male, 1 for female.
One DataTrigger (second is similar):
<DataTrigger Binding="{Binding Source={StaticResource Data}, XPath=People/Person/Sex}" Value="0">
<Setter Property="Background" Value="{StaticResource MaleBrush}" />
</DataTrigger>
This is xml and style binding to listview (data is XmlDataProvider):
<ListView ... ItemsSource="{Binding Source={StaticResource Data}, XPath=People/Person}" ItemContainerStyle="{StaticResource SexStyle}">
And this is style header:
<Style x:Key="SexStyle" TargetType="{x:Type ListViewItem}">
Thanks for help!
You should use a ValueConverter for this.
You bind the background of the datatemplate to the gender.
Then you create a value converter class like this:
public sealed class GenderToBackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string) {
if (Convert.ToString(value) == "m") {
return Colors.Blue;
} else {
return Colors.Pink;
}
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This value converter you then add to your resources like this:
<local:GenderToBackgroundConverter x:Key="GenderToBackgroundConverter" />
And in your datatemplate:
<Stackpanel Background={Binding Sex, Converter={StaticResource GenderToBackgroundConverter}}">
</Stackpanel>
Related
I have a table, and when the user would like to mark a row. he can click on button and mark it.
so I wrote a converter for the property, that if its true it return color(yellow)
and if false it return white,
however it Deletes the default style when user select a row in the table.
I was thinking of using Multibuilding once for mark, and one for selected.
however I am not understand what the syntax should be in WPF.
attaching the code I wrote,
will appreciate a code examples.
WPF:
<Style TargetType="syncfusion:GridCell" x:Key="ComCell">
<Setter Property="Foreground" Value="{Binding COMPort , Converter={StaticResource CVconverters } }" />
<Setter Property="Background" Value="{Binding isBookMarked, Converter={StaticResource BookMarkConverter}}"></Setter>
</Style>
C#:
public class BookMarkConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string valStr = value.ToString();
if (valStr == "True")
{
return Brushes.Yellow;
}
else
{
}
return Brushes.White;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Instead of returning Brushes.White from the converter, you could return Binding.DoNothing from the converter if you simply don't want to change the background.
You could also base your Style on the default one:
<Style TargetType="syncfusion:GridCell" x:Key="ComCell"
BasedOn="{StaticResource {x:Type syncfusion:GridCell}}">
I have a DataGrid displaying a list of maps with some details. I'm trying to change the foreground colour of the map which is currently active but I'm struggling. I've written the following converter to check if the DataGrid item's value matches the current map's name, but I'm not sure how to pass the name of the current map from XAML.
Converter:
public class StringToBrushConverter : IValueConverter
{
public string CurrentMapName { get; set; }
public Color CurrentMapColor { get; set; }
public Color OtherMapColor { get; set; }
string _value = "";
public StringToBrushConverter(string currentMapName, Color currentMapColor, Color otherMapColor)
{
CurrentMapName = currentMapName;
CurrentMapColor = currentMapColor;
OtherMapColor = otherMapColor;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
_value = value as string;
return (string)value == CurrentMapName ? new SolidColorBrush(CurrentMapColor) : new SolidColorBrush(OtherMapColor);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return _value; //TODO: Will this work?
}
}
public sealed class RaceMapColorConverter : StringToBrushConverter
{
public RaceMapColorConverter() : base("", new Color { R = 255, G = 0, B = 0 }, new Color { R = 255, G = 255, B = 255 }) { }
}
XAML:
<DataGrid x:Name="MapHistory"
AutoGenerateColumns="False"
IsReadOnly="True"
HeadersVisibility="Column"
GridLinesVisibility="None"
CanUserSortColumns="False"
Style="{StaticResource MapTimerDataGrid}"
CellStyle="{StaticResource MapTimerControlCell}"
ItemsSource="{Binding Plan.Plan}">
<DataGrid.Resources>
<!-- The Binding in CurrentMapName is not valid. It says it can only be set on a dependency property but I'm not sure how to do that -->
<conv:RaceMapColorConverter x:Key="MapColorConverter" CurrentMapName="{Binding CurrentMap.MapName}" CurrentMapColor="Red" OtherMapColor="White" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Map" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Map.MapName}" Foreground="{Binding Map.MapName, Converter={StaticResource MapColorConverter}}" Padding="0" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!-- Other columns removed to shorten -->
</DataGrid.Columns>
</DataGrid>
I'm also not sure how to implement 'ConvertBack()`. Will storing the original value in the converter work? or could a new instance be created for the same control? Am I even going about this the right way? Thought it's also worth mentioning that I'm using Caliburn.Micro just in case it has any tricks that may help. Any help would be appreciated.
As a general rule-of-thumb you should use converters when the data conversion needs to be two-way (i.e. view-model to view and back again) and DataTriggers when it's one-way only. That answers your question about ConvertBack (which in the example you've provided never gets called anyway, in which case you can just return Binding.DoNothing).
In this particular case, however, a converter is warranted because you need to compare two bindings. Instead of writing an application-aware converter for this one very specific task, I would instead opt for a more generic MultiConverter that simply compares two values that you provide it:
public class EqualityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return (values.Length==2) && Object.Equals(values[0], values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return new object[] { Binding.DoNothing, Binding.DoNothing };
}
}
Then back in your application you pass both of your bindings into a MultiBinding of this type and use a DataTrigger to change the map color when the result of the comparison is true:
<Label Content="{Binding Map.MapName}">
<Label.Style>
<Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="Foreground" Value="Red" /> <!-- normal map color -->
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource EqualityConverter}" Mode="OneWay">
<Binding Path="Map.MapName" />
<Binding Path="CurrentMapName" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Foreground" Value="White" /> <!-- other map color -->
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
I have converter for applying background color for DataGrid rows based on values in one column. It is working fine and is applying background color "on load". However I decided to dive deeper into WPF and MVVM and try to attach this event to CheckBox, but failed at this point. I am not getting any errors but my CheckBox does not work either. Any suggestions what should be edited? I guess I need to attach Convert event somehow to BacgroundColorBool?
IDToBackgroundConverter.cs:
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace Inspector_FilterTest
{
class IDToBackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string)
{
if (value.ToString().Trim().StartsWith("7")) return Brushes.DarkSlateBlue;
if (value.ToString().Trim().StartsWith("6")) return Brushes.Peru;
}
// value is not an integer. Do not throw an exception
// in the converter, but return something that is obviously wrong
return Brushes.Firebrick;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
I use it in XAML:
<Window.Resources>
<Inspector_FilterTest:IDToBackgroundConverter x:Key="IDToBackgroundConverter"/>
</Window.Resources>
...
<DataGrid x:Name="DataGrid1" ItemsSource="{Binding MainDataTable}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" >
<Setter Property="Background" Value="{Binding YRNRO, Converter={StaticResource IDToBackgroundConverter}}" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
This is working fine and background colors are set (based on starting number in YRNRO) when I am loading my DataGrid. However I would like to be able to control this with CheckBox.
I have created a CheckBox:
ViewModel_Main.cs:
// Binding checkbox background color
private bool _BacgroundColorBool;
public bool BacgroundColorBool
{
get => this._BacgroundColorBool;
set
{
this._BacgroundColorBool = value;
OnPropertyChanged();
// Refresh the DataTable filter expression
EnableRowFiltering();
}
}
XAML:
<CheckBox Style="{StaticResource MyCheckBox}" IsChecked="{Binding BacgroundColorBool}" x:Name="ActiveCustomer_Copy" Content="" HorizontalAlignment="Left" Margin="221,55,0,0" VerticalAlignment="Top"/>
but I don't understand how to connect this all together in order to be able to control "background fill applied"/"background fill not applied" with CheckBox.
EDIT:
Some corrections done to code originally provided by BionicCode (no errors in debugger anymore, I hope this is correct?). In order to get color back to transparent in Datagrid after CheckBox is unchecked pay attention to return Brushes.Transparent;. That is where you can apply "revertBack" logic.
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace Liinos_inspector_FilterTest
{
class IDToBackgroundConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// The order of the parameters in the 'values' array is equal to the order
// of the input binding as they are defined in the MultiBinding
var isBackgroundEnabled = (bool)values[0];
if (!isBackgroundEnabled)
{
return Brushes.Transparent;
}
if (values[1] is string stringValue)
{
return stringValue.Trim().StartsWith("7")
? Brushes.DarkSlateBlue
: stringValue.Trim().StartsWith("6")
? Brushes.Peru
: Brushes.Firebrick;
}
return Binding.DoNothing;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
One solution, that doesn't require modification of your view model, is to use a MultiBinding with a IMultiValueConverter. A IMultiValueConverter accepts multiple inputs returned from a MultiBinding, to convert them to a single output (and reverse):
First, turn the IValueConverter into a IMultiValueConverter.
It's best practice to throw the exception that describes the cause of the error the best.
The NotImplementedException is primarily intended to notify the developer that he forgot to implement a relevant and referenced method. It's like a 'TODO'. Since you deliberately decided not to implement the ConvertBack member, because you decided not to support the backwards conversion, you should throw a NotSupportedException instead. This notifies the consumer of your type that the method is not supported instead of implying that the author simply forgot to implement it.
class IDToBackgroundConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
// The order of the parameters in the 'values' array is equal to the order
// of the input binding as they are defined in the MultiBinding
var isBackgroundEnabled = (bool) values[0];
if (!isBackgroundEnabled)
{
return Brushes.Transparent;
}
if (values[1] is string stringValue)
{
return stringValue.Trim().StartsWith("7")
? Brushes.DarkSlateBlue
: stringValue.Trim().StartsWith("6")
? Brushes.Peru
: Brushes.Firebrick;
}
return Binding.DoNothing;
}
// Throw a NotSupportedException instead of a NotImplementedException
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
=> throw new NotSupportedException();
}
Setup the MultiBinding:
<CheckBox IsChecked="{Binding BackgroundColorBool}" />
<DataGrid>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background">
<Setter.Value>
<MultiBinding>
<MultiBinding.Converter>
<IDToBackgroundConverter />
</MultiBinding.Converter>
<Binding Path="BackgroundColorBool" />
<Binding Path="YRNRO" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
</DataGrid>
Because the purpose of the logic is to handle the coloring or visuals of the view, you should not implement this logic or store related data in the view model.
View and view model should never mix!
Whether to use the previous example or to eliminate the BackgroundColorBool property in the view model, depends on the the purpose of the CheckBox. Its name suggests a data related purpose ("ActiveCustomer...") and of course could bind to the view model.
But the name of the source property ("BackgroundColor..."), the CheckBox binds to, suggests a merely view related purpose. In this case the property should not be in the view model. Instead move it as a DependencyProperty to the hosting view or bind the CheckBox directly:
<CheckBox x:Name="ActiveCustomer_Copy" />
<DataGrid>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background">
<Setter.Value>
<MultiBinding>
<MultiBinding.Converter>
<IDToBackgroundConverter />
</MultiBinding.Converter>
<Binding ElementName="ActiveCustomer_Copy"
Path="IsChecked" />
<Binding Path="YRNRO" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</DataGrid.RowStyle>
</DataGrid>
I have a requirement to change a button's style based on a value in the data. It looks like a StyleSelector would work perfectly but there doesn't seem to be a way to set one for a button.
Is there a way to set a button style dynamically from data? Maybe even a pure XAML approach?
A more general way to accomplish the same thing:
SomeView.xaml
<UserControl>
<UserControl.Resources>
<converters:BooleanToStyleConverter x:Key="MyButtonStyleConverter"
TrueStyle="{StaticResource AmazingButtonStyle}"
FalseStyle="{StaticResource BoringButtonStyle}"/>
</UserControl.Resources>
<Grid>
<Button Style={Binding IsAmazingButton, Converter={StaticResource MyButtonStyleConverter}}/>
</Grid>
</UserControl>
BooleanToStyleConverter.cs
public class BooleanToStyleConverter : IValueConverter
{
public Style TrueStyle { get; set; }
public Style FalseStyle { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool && (bool) value)
{
return TrueStyle;
}
return FalseStyle;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This converter works in any view with any kind of control using whatever style you choose as long as you are binding to a Boolean property in your ViewModel to control the style switching. Easy to adapt it to other binding requirements though. This works in a DataTemplate as well.
You could place your Button Styles in a Resource Dictionary and bind the Style for the Button and use a Converter
ButtonStyles.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="ButtonStyle1" TargetType="Button">
<Setter Property="Background" Value="Green"/>
<Setter Property="FontSize" Value="12"/>
</Style>
<Style x:Key="ButtonStyle2" TargetType="Button">
<Setter Property="Background" Value="Red"/>
<Setter Property="FontSize" Value="14"/>
</Style>
</ResourceDictionary>
Then for the Button that has this requirement you bind Style to the property of interest
<Button ...
Style="{Binding Path=MyDataProperty,
Converter={StaticResource ButtonStyleConverter}}"/>
And in the Converter you load the ButtonStyles Resource Dictionary and return the desired Style based on the value
public class ButtonStyleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Uri resourceLocater = new Uri("/YourNameSpace;component/ButtonStyles.xaml", System.UriKind.Relative);
ResourceDictionary resourceDictionary = (ResourceDictionary)Application.LoadComponent(resourceLocater);
if (value.ToString() == "Some Value")
{
return resourceDictionary["ButtonStyle1"] as Style;
}
return resourceDictionary["ButtonStyle2"] as Style;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I'm trying to stylish this ListBox with alternate row colors:
<ListBox x:Name="listBox" Background="{x:Null}" SelectionChanged="listBox_SelectionChanged" >
<ListBox.Resources>
<local:AltBackgroundConverter x:Name="AltBackgroundConverter" />
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0" Height="50" Background="{Binding Converter={StaticResource AltBackgroundConverter}}" >
<TextBlock Text="{Binding Titulo}" Style="{StaticResource ListViewItemTextBlockStyle}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is the AltBackgroundConverter.cs:
public class AltBackgroundConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (!(value is int)) return null;
int index = (int)value;
if (index % 2 == 0)
return Colors.White;
else
return Colors.Gray;
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Obviously, it's not working because I'm not achieving how to bind the row number to the converter. As you can see, I'm binding a entire ListBox Item.
I think the solution should be really simple, but I can't find it.
There are a couple of problems with your solution right now.
Your binding to your list items, while the converter needs an integer to figure out if an item is even or odd. Based on your screenshot you should change it so it looks like this:
Background="{Binding Numero, Converter={StaticResource AltBackgroundConverter}}"
Your converter returns a color, while the Background is a brush, so you could fix it like this:
public class AltBackgroundConverter : IValueConverter
{
private Brush whiteBrush = new SolidColorBrush(Colors.White);
private Brush grayBrush = new SolidColorBrush(Colors.Gray);
public object Convert(object value, Type targetType, object parameter, string language)
{
if (!(value is int)) return null;
int index = (int)value;
if (index % 2 == 0)
return whiteBrush;
else
return grayBrush;
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Things should start working at this point, but your highlighting will only highlight the text instead of entire rows. This is how you can fix that up:
<ListBox.ItemContainerStyle>
<Style
TargetType="ListBoxItem">
<Setter
Property="HorizontalContentAlignment"
Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>