Color the row in combobox? - c#

I'm having trouble with finding the solution for the problem, namely I had an idea to color each row/column of combobox with different colors, depending on the area, but i cannot find any clues or hints or instructions to do so. the app is pretty simple
<ComboBox x:Name="comboBox1" HorizontalAlignment="Left" Margin="84,70,0,0" VerticalAlignment="Top" Width="230"/>
this is my XAML combobox, which i fill from the code:
SortedList<int, string> AreaList = new SortedList<int, string>();
AreaList.Add(1, "Agriculture");
AreaList.Add(2, "Forestry");
AreaList.Add(3, "Fruits");
AreaList.Add(4, "Food");
AreaList.Add(5, "Metals");
AreaList.Add(6, "Mining");
AreaList.Add(7, "Electricity");
AreaList.Add(8, "Building Contracts");
AreaList.Add(9, "Transport");
AreaList.Add(10, "Alcohol");
AreaList.Add(11, "Information Technologies");
AreaList.Add(12, "Health And Social Services");
AreaList.Add(13, "Art and Entertainement");
AreaList.Add(14, "Hospitality Business");
AreaList.Add(15, "Education");
AreaList.Add(16, "Real Estate");
AreaList.Add(17, "Sales");
AreaList.Add(18, "Architecture");
AreaList.Add(19, "Engineering");
AreaList.Add(20, "Wholesale");
AreaList.Add(21, "Other");
comboBox1.ItemsSource = AreaList.ToList();
comboBox1.SelectedValuePath = "Key";
comboBox1.DisplayMemberPath = "Value";
each of these items have their color in another window, but i would like to show those colors in the combobox, the background of "Agriculture" row/column should be green etc.
Is there a solution to this, or do i have to redo it all over?

You could use an ItemContainerStyle with a DataTrigger for each value that maps to a colour:
<ComboBox x:Name="comboBox1">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Value}" Value="Agriculture">
<Setter Property="Background" Value="Green" />
</DataTrigger>
<DataTrigger Binding="{Binding Value}" Value="Forestry">
<Setter Property="Background" Value="Red" />
</DataTrigger>
<!-- and so on-->
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
You may also want to read this:
Changing the background colour of a ComboBox in WPF: https://blog.magnusmontin.net/2014/04/30/changing-the-background-colour-of-a-combobox-in-wpf-on-windows-8/

You can make use of ItemContainerStyle and Converter
public class StringToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (((KeyValuePair<int, string>)value).Value.ToString() == "Agriculture")
return Brushes.Green;
//and so on or other ways to get the color
return Brushes.Transparent;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
and XAML goes as follows,
<Window.Resources>
<local:StringToColorConverter x:Key="StringToColorConverter"/>
</Window.Resources>
<Grid >
<ComboBox x:Name="comboBox1" HorizontalAlignment="Left" Margin="84,70,0,0" VerticalAlignment="Top" Width="230">
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Background" Value="{Binding Converter={StaticResource StringToColorConverter}}">
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</Grid>

Related

How do I change the background color of a cell by a condition?

How do I change the background color of a cell by a condition on a numeric value?
For example, when the price is greater than 10, the background is colored red.
I did this but the value remains constant, I want this to apply to all values that are greater than 100, for example. Sorry about my bad english
<DataGridTextColumn Binding="{Binding Price}"
Header="Price">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="Text" Value="100">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
You can use an IValueConverter to convert prices into colors and define a DataGridTextColumn.CellStyle to use this converter.
Define this somewhere in your code:
public class PriceToBackgroundColorConverter : IValueConverter {
// Converts a value into a color.
// Returns Red if value is greater than 100, else White.
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
int price = (int)value;
if (price > 100) {
return Brushes.Red;
} else {
return Brushes.White;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
Now you can use this converter by adding it to the Resources of your DataGrid (or of any parent control) and using a Setter to style all DataGridCell objects.
<DataGrid ItemsSource="{Binding Items}">
<DataGrid.Resources>
<wpfapp1:PriceToBackgroundColorConverter x:Key="PriceToBackgroundColorConverter"></wpfapp1:PriceToBackgroundColorConverter>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Price}" Header="Price">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding Path=Price, Converter={StaticResource PriceToBackgroundColorConverter}}"></Setter>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
This will instruct your DataGridTextColumn to apply a Style to all its DataGridCell children, and this Style binds the Background property of each DataGridCell to its Price property through the PriceToBackgroundColorConverter.
You can change the converter code to perform any kind of logic you want, have several colors for different ranges etc.
You can also define more converters and more Setter to change additional properties.

C# DataGrid cell colour seems random wpf

I've written some code to try and change the color of a DataGrid cell based on the length of the value inside the cell. However it appears to work correctly when it loads in the first few rows however when i scroll down past the rows in view it seems to randomly change colour.
My XML behind this is:
<DataGrid Grid.Row="2" Name="DataGridView1" CanUserSortColumns="False" CanUserReorderColumns="False" IsReadOnly="True" ItemsSource="{Binding}" Background="LightGray" RowBackground="#BDBDBF" AlternatingRowBackground="#E3E3E5" CellStyle="{StaticResource CellStyle}">
My 'CellStyle' Resource is
<local:ValueToBrushConverter x:Key="ValueToBrushConverter"/>
<Style x:Key="CellStyle" TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self}, Converter={StaticResource ValueToBrushConverter}}" />
</Style>
And finally my ValueToBrushConverter is:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DataGridCell dgc = (DataGridCell)value;
System.Data.DataRowView rowView = (System.Data.DataRowView)dgc.DataContext;
var input = rowView.Row.ItemArray[dgc.Column.DisplayIndex];
StreamWriter sw = new StreamWriter(#"C:\DM Data\Inputs.txt",true);
sw.WriteLine(input + " " + input.ToString().Length);
sw.Close();
sw.Dispose();
if (input.ToString().Length < 32)
return new SolidColorBrush(Colors.LimeGreen);
else if (input.ToString().Length >= 32)
return (SolidColorBrush)(new BrushConverter().ConvertFrom("#FF1616"));
else
return new SolidColorBrush(Colors.Black);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException();
}
Note the streamwriter is merely for my testing to see what the values are returning.
As said the code works initially but after stops working, this gif shows what i mean
https://gyazo.com/099873b8738887bddaf418c3b45129e3
You can see some values that should not be highlighted become highlighted and visa versa
This is because of the UI virtualization, i.e. the item containers are reused as you scroll through the items.
You can disable this by setting VirtualizingStackPanel.VirtualizationMode attached property to Standard, at the potential cost of some scrolling performance:
<DataGrid ... VirtualizingStackPanel.VirtualizationMode="Standard">
what is the actual difference between Recycling/Standard of VirtualizationMode property in VirtualizingStackPanel?
A better option would be to use a specific CellStyle for each column that binds to the actual source property of the item, e.g.:
<DataGridTextColumn Binding="{Binding Name}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="{Binding Name,
Converter={StaticResource ValueToBrushConverter}}" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>

Problems changing datagrid color using WPF C#

I am using a datagrid and changing the color of the rows according to their conditions and I am performing this programmatically.
follow the example
as my datagrid is bound to a datatable I load information straight from the datatable
private void UpdateCor () {
gvDados.UpdateLayout ();
for (int i = 0; i <dt.Rows.Count; i ++)
{
var rowContext = (DataGridRow)
gvDados.ItemContainerGenerator.ContainerFromIndex (i);
      if (rowContext! = null)
      {
            if (dt.Rows [i] ["situation"]. ToString (). Equals (1))
                     rowContext.Background = Brushes.Green;
            else
                     rowContext.Background = Brushes.Red;
      }
}
}
With this I can update the color of my grid even though it is not the best method to be approached. my problem is this, whenever I use the scroll to go down or up the bar the colors become outdated. How do I prevent this from happening? that even when I roll the bar the colors stay fixed?
This is a similar question to this question.
Can be done using datatrigger:
<DataGrid ItemsSource="{Binding YourItemsSource}">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="State1">
<Setter Property="Background" Value="Red"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="State2">
<Setter Property="Background" Value="Green"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
</DataGrid>
Often, XAML is too simple to express more complicated conditions. I prefer to put the logic which values should use which colors into a converter. This leads to a simpler XAML and much greater flexibility for the converter in C#.
<datagrid.rowstyle>
<style targettype="DataGridRow">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource Self},
Path=Item.situation, Converter={StaticResource ValueToBackgroundConverter}}"/>
</style>
</datagrid.rowstyle>
In C#:
class ValueToBackgroundConverter: IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is int) {
int quantity = (int)value;
if (quantity>=100) return Brushes.White;
if (quantity>=10) return Brushes.WhiteSmoke;
if (quantity>=0) return Brushes.LightGray;
return Brushes.White; //quantity should not be below 0
}
//value is not an integer. Do not throw an exception
// in the converter, but return something that is obviously wrong
return Brushes.Yellow;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
Formatting the various parts of a WPF Datagrid is notoriously difficult and Microsoft is not providing the necessary information how to do it. Read my article Codeproject: Guide to WPF DataGrid formatting using binding to get a better understanding how to do it easily.

WPF Listview alternate row background by data group not row

I couldn't find a solution for my problem/idea, and I hope that someone could help me out.
In WPF I have a CollectionViewSource depending on an IEnumerable<Item>.
An Item has the Name, Height, Age fields.
In Xaml, the ListView's ItemsSource="{Binding CollectionViewSource.View}".
I know, the Listview has an AlternationCount property which can change the row background color.
But I want to change the row background color only if the Age field data is different from the above row's Age data.
Like this, ordered by Age:
The row background color is alternated only when the Age data is different.
When I set another sort order to the list, the alternating should also be changed.
In this picture the list is ordered by Name:
But the background color depends on the Age data.
Is there any way to make a solution for this concept?
You can use the RelativeSource.PreviousData for your task.
First, create an IMultivalueConverter which will accept the values you want to compare and return the current alternation index based on them:
class ComparisonConverter : IMultiValueConverter
{
private int currentAlternation = 0;
public int AlternationCount { get; set; } = 2;
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// TODO: exception handling
if (values.Distinct().Count() != 1)
{
if (++currentAlternation >= AlternationCount)
{
currentAlternation = 0;
}
}
return currentAlternation;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
This converter accepts multiple values and returns an unchanged alternation index when all values are equal; otherwise, it first changes the alternation index to the next one and then returns a new index.
Now, create a MultiBinding that will provide the alternation index value to the Style, where you define your colors:
<!-- This is an incomplete ListView! Set the View and ItemsSource as required. -->
<ListView>
<ListView.Resources>
<local:ComparisonConverter x:Key="ComparisonConverter"/>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<!-- This is the DataTrigger for the alternation index 1 -->
<DataTrigger Value="1">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource ComparisonConverter}">
<Binding Path="Age"/>
<Binding Path="Age" RelativeSource="{RelativeSource PreviousData}"/>
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="Background" Value="Wheat"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
So in this style, the default Color for the alternation index 0 is Wheat. Using the DataTrigger, the alternation index 1 will generate the color Red.
The sort order changes will be reflected automatically, because the CollectionViewSource will rebuild the view, so the ListView will create all items from scratch using the MultiBinding for each item.
Run this and live life to fullest :
XAML :
<Window x:Class="WpfStackOverflow.Window6"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window6" Height="362.03" Width="563.91">
<Window.Resources>
<CollectionViewSource x:Key="CVS" Source="{Binding .}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Age"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid>
<ListView x:Name="LstView" ItemsSource="{Binding Source={StaticResource CVS}}">
<ListView.Resources>
<AlternationConverter x:Key="AltCnvKey">
<SolidColorBrush Color="Snow"/>
<SolidColorBrush Color="LightBlue"/>
<SolidColorBrush Color="Orange"/>
</AlternationConverter>
</ListView.Resources>
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn DisplayMemberBinding="{Binding Name}" Header="Name"/>
<GridViewColumn DisplayMemberBinding="{Binding Height}" Header="Height"/>
<GridViewColumn DisplayMemberBinding="{Binding Age}" Header="Age"/>
</GridView.Columns>
</GridView>
</ListView.View>
<ListView.GroupStyle>
<GroupStyle AlternationCount="3">
<GroupStyle.ContainerStyle>
<Style TargetType="GroupItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="GroupItem">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="StackPanel">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource AncestorType=GroupItem, Mode=FindAncestor}, Path=(ItemsControl.AlternationIndex), Converter={StaticResource AltCnvKey}}"/>
</Style>
</StackPanel.Resources>
<ItemsPresenter/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</Grid>
Code :
using System.Linq;
using System.Windows;
namespace WpfStackOverflow
{
/// <summary>
/// Interaction logic for Window6.xaml
/// </summary>
public partial class Window6 : Window
{
public Window6()
{
InitializeComponent();
this.DataContext = new[] { new { Age = 32, Name = "Name1", Height = 6 }, new { Age = 34, Name = "Name1", Height = 6 }, new { Age = 34, Name = "Name1", Height = 6 }, new { Age = 32, Name = "Name1", Height = 6 }, new { Age = 32, Name = "Name1", Height = 6 }, new { Age = 39, Name = "Name1", Height = 6 }, new { Age = 40, Name = "Name1", Height = 6 } }.ToList();
}
}
}

Separate text in a Textbox with a drawn line?

So here it goes, I making a c# project in wpf and im stuck at trying to make a text box with a line that separates text.
At the moment i got the textbox like this:
Instead of using "-------" i want to make a predefined line in the textbox. Is this possible ?
Agg. The textbox is editable in runtime
The drawn line should have the properties:
It should not be edit able !
It shouldn't only be an empty line
It should be a visible line that has the width line.width = box.width !
If you don't need a full textbox implementation then this might help. It's got real issues with not showing the cursor ect but might give you a start.
first add the following converter to your project.
public class TextLineConverter : MarkupExtension, IValueConverter
{
static TextLineConverter converter;
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string[] results = null;
string newText = value as string;
if (newText != null)
{
results = newText.Split('\r');
if (results.Length > 0)
for (int i = 0; i < results.Length; i++)
if (results[i].Length > 0)
if (results[i][0] == '\n')
results[i] = results[i].Substring(1, results[i].Length - 1);
}
return results;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
if (converter == null) converter = new TextLineConverter();
return converter;
}
public TextLineConverter()
{
}
}
And the following style.
<Style TargetType="TextBox">
<Style.Resources>
<Style TargetType="ListViewItem">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderThickness="0,0,0,2" BorderBrush="Black" >
<ContentPresenter Content="{Binding}" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<ListView Focusable="False" ItemsSource="{Binding Text, Converter={local:TextLineConverter}, UpdateSourceTrigger=PropertyChanged, RelativeSource={RelativeSource TemplatedParent}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
On rereading your question, i believe that the best way to do what you want is to create your own user-control that has an attribute specifically for this. Do some research on C# User Controls and perhaps you'll find out how to do this.

Categories

Resources