Alternate ListBox row colors - c#

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>

Related

WPF - Bind ComboBox Item Foreground to Its Value

I created a ComboBox listing the colors that System.Windows.Media.Colors predefines, using the approach told in this question: How can I list colors in WPF with XAML?
My XAML code now is:
<Window ...>
<Window.Resources>
<ObjectDataProvider
ObjectInstance="{x:Type Colors}" MethodName="GetProperties" x:Key="ColorList" />
<local:StringToBrushConverter x:Key="FontColorConversions" />
</Window.Resources>
<Grid Background="Black">
...
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource ColorList}}"
SelectedValue="{Binding FontColor, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedValuePath="Name">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Foreground" Value="{Binding Converter={StaticResource FontColorConversions}}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
...
</Grid>
</Window>
And besides, please note that I bind SelectedValue to a VM class's FontColor property, which is of string type.
class FontSetting : INotifyPropertyChanged
{
private string _fontColor = "Lavender"; // initial color
public event PropertyChangedEventHandler PropertyChanged;
public string FontColor
{
get
{
return _fontColor;
}
set
{
_fontColor = value;
OnPropertyChanged("FontColor");
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And I set the DataContext of the Window containing this ComboBox to an instance of FontSetting.
So each item in the ComboBox actually display a string representing a certain color now, what I want to do is set an item's Foreground color to that color its content indicates, like this:
Can anyone help? Thanks.
UPDATED:
Since most of the solutions have a converter which converts string to Brush and actually I already have it, now I want to put mine here, as I binded a TextBox's Foreground to FontSetting's FontColor property, so that when you change the ComboBox, the color of that TextBox changes accordingly.
Here is my converter class, and it works fine by now:
class StringToBrushConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
BrushConverter conv = new BrushConverter();
SolidColorBrush brush = conv.ConvertFromString("Lavender") as SolidColorBrush;
if (null != value)
{
brush = conv.ConvertFromString(value.ToString()) as SolidColorBrush;
}
return brush;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
When I click the ComboBox to open the dropdown list, I got an exception:
CONCLUSION
Amine's solution works, it's my mistake. I explain briefly now, if you bind a ComboBox to System.Windows.Media.Colors like what I am doing, when the item is rendered, the Convert() method of the converter class (which you assign to the binding) is executed, and actually the value passed to Convert() as its first parameter is a Syetem.Windows.Media.Color instance. I made mistake coz I thought it was of string type.
Therefore, in my case I need two converter classes, one converting string to Brush, and the other one converting Color to Brush. So I will keep my own StringToBrush converter and add Amine's ColorToBrush converter.
However, I simplified Amine's implementation a bit:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
BrushConverter conv = new BrushConverter();
SolidColorBrush brush = SolidColorBrush)conv.ConvertFromString(FontSetting.DEFAULT_FONT_COLOR);
if (null != value)
{
PropertyInfo pi = value as PropertyInfo;
if (null != pi)
{
brush = conv.ConvertFromString(pi.Name) as SolidColorBrush;
}
}
return brush;
}
Moreover, Joe's input is also valuable, put all them together, I can keep the items' color consistent, which is perfect.
You can set Style of ComboBoxItem as bellow :
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240" x:Name="CB"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
DisplayMemberPath="Name"
SelectedValuePath="Name">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Foreground" Value="{Binding Converter={StaticResource converter}}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
By using this converter:
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
object obj = ((System.Reflection.PropertyInfo)value).GetValue(this,null);
return (SolidColorBrush)new BrushConverter().ConvertFromString(obj.ToString());
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
return value;
}
}
Two ways, use a value converter or an intermediate property. The easiest is probably an intermediate property as you are using well structured bindings already for your SelectedItem (but value converters are fun too!).
SelectedValue is bound to FontColor, so in your setter set another value:
public string FontColor
{
get
{
return _fontColor;
}
set
{
_fontColor = value;
ForegroundColorToDisplay = GetBrushFromColorString(value);
OnPropertyChanged("FontColor");
}
}
public Brush _foregroundColorToDisplay
public Brush ForegroundColorToDisplay
{
get
{
return _foregroundColorToDisplay;
}
set
{
_foregroundColorToDisplay= value;
OnPropertyChanged("ForegroundColorToDisplay");
}
}
or, if you don't want to store it:
public string FontColor
{
get
{
return _fontColor;
}
set
{
_fontColor = value;
//note it fires two changed events!
OnPropertyChanged("ForegroundColorToDisplay");
OnPropertyChanged("FontColor");
}
}
public Brush ForegroundColorToDisplay
{
get
{
return GetBrushFromColorString(value);;
}
}
You can bind to this new property in your xaml:
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
SelectedValue="{Binding FontColor, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
Foreground="{Binding ForegroundColorToDisplay, Mode=OneWay}"/>
If you are interested, a value converter would work like this:
Create a class in your code behind (or elsewhere if it's going to be used a lot) that implements IValueConverter, takes the string and returns a Brush:
public class StringToBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
return GetBrushFromString(value as string)
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
Throw new SomeException();
}
}
and use it in your binding in xaml to convert the selectedItem binding to Foreground brush:
<Window.Resources>
<local:StringToBrushConverter x:Key="converter" />
</Window.Resources>
...
<ComboBox Grid.Column="1" Grid.Row="1" Height="22" Width="240"
VerticalAlignment="Center" HorizontalAlignment="Left"
ItemsSource="{Binding Source={StaticResource colorPropertiesOdp}}"
SelectedValue="{Binding FontColor, Mode=TwoWay}"
DisplayMemberPath="Name"
SelectedValuePath="Name"
Foreground="{Binding FontColor, Mode=OneWay, Converter={StaticResource converter}}"/>

Converter Parameter In ItemTemplate

I have a collection of Checkboxes in a Listbox binding to an Enum which uses a converter. Code shown below:
<ListBox HorizontalAlignment="Left" Height="183" Margin="159,30,0,0" VerticalAlignment="Top" Width="144">
<ListBox.Resources>
<local:FlagsEnumValueConverter x:Key="FlagsConverter" />
</ListBox.Resources>
<CheckBox Content="Checkbox1" IsChecked="{
Binding Path=TestModel.TestEnum,
Converter={StaticResource FlagsConverter},
ConverterParameter={x:Static tc:TestEnum.Test1}}"/>
<CheckBox Content="Checkbox2" IsChecked="{
Binding Path=TestModel.TestEnum,
Converter={StaticResource FlagsConverter},
ConverterParameter={x:Static tc:TestEnum.Test2}}"/>
<CheckBox Content="Checkbox3" IsChecked="{
Binding Path=TestModel.TestEnum,
Converter={StaticResource FlagsConverter},
ConverterParameter={x:Static tc:TestEnum.Test3}}"/>
</ListBox>
Converter Code:
class FlagsEnumValueConverter : IValueConverter
{
private int mTargetValue;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var mask = (int)parameter;
mTargetValue = (int)value;
return ((mask & mTargetValue) != 0);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
mTargetValue ^= (int)parameter;
return Enum.Parse(targetType, mTargetValue.ToString());
}
}
I'd rather use an ItemTemplate to keep things clean and simple however I'm not sure what to do with the 'ConverterParameter' since it requires the specific Enum value. I've been Googling for a solution but haven't found one. I honestly don't even know where to start. Is this even possible?

wpf binding and stringformat in hexa

I have the following list binded to a list of bytes:
<ListView ItemsSource="{Binding Payload}" VerticalAlignment="Center" BorderBrush="Transparent">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding StringFormat=0x{0:x2}}" Margin="2,1,2,1" MinWidth="25" MinHeight="20" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"></WrapPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
That Payload is an ObservableCollection. I get the values and display them in hexa but i want this to be a two way mode. At first i get values like 0x00 but i would like to modify that value let's say in 0xff and put that back in the Payload collection like 255.
I think i need a converter but i would need some help in that direction.
As the commenters mentioned, you can use an IValueConverter for that. Try this:
public class ByteToHexadecimalConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || targetType != typeof(string)) return DependencyProperty.UnsetValue;
byte byteValue = 0;
if (!byte.TryParse(value.ToString(), out byteValue)) return DependencyProperty.UnsetValue;
return byteValue.ToString("x2");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || targetType != typeof(byte)) return DependencyProperty.UnsetValue;
string stringValue = value.ToString();
byte returnValue = 0;
try { returnValue = System.Convert.ToByte(stringValue, 16); }
catch { return DependencyProperty.UnsetValue; }
return returnValue;
}
}
You might need to tweak it a bit for your exact requirements, but it's all easily understandable. You can find out more about IValueConverter from the IValueConverter Interface page on MSDN.

WPF - Highlighting rows in listview with binded XML

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>

Where to set the Converter for items of a collection in XAML

I just made my first converter to convert from int to string. I have a combobox fill with integers(years) but if the value is 0 I want the combobox to show 'All'.
This is my converter:
public class IntToString : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
{
int intY = (int)value;
if (intY == 0)
{
String strY = "All";
return strY;
}
else
{
return intY.ToString();
}
}
return String.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
}
}
In XAML where should I set the converter ? I tried in the ItemsSource of the combobox:
ItemsSource="{Binding YearsCollection, Converter={StaticResource intToStringYearConverter}}"
But I always get InvalidcastException on this line:
int intY = (int)value;
The problem is that you are trying to convert the entire collection rather than just one item from the collection.
You would want to do something like this:
<ListBox ItemsSource="{Binding YearsCollection}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border DataContext="{Binding Converter={StaticResource intToStringYearConverter}">
...
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You can't use the converter like this, converter in ItemsSource is supposed to convert whole collection, not individual items. The collection object can't be cast to integer, so you get the exception.
You have to use DataTemplate and apply the converter on individual items.
Or - if all you need is cast to int - you could use ItemStringFormat.
Also, for setting the default message when the source is null, you can use TargetNullValue property of a Binding.

Categories

Resources