Skip to main content
Microsoft Silverlight
Home Forums Silverlight Programming Silverlight Controls and Silverlight Toolkit DataGrid Row Background based on bound value + readonly row
21 replies. Latest Post by Pashec on October 23, 2009.
(0)
Inquisit...
Member
84 points
62 Posts
10-15-2008 11:12 AM |
I need to change the color of the rowbackground depending on the value of a bound item.
The value is editable from the grid, so the when the value changes (on the bound object or changed from grid), the row background needs to change accordingly.
I also need to be able to make only a specific row readonly depending on a bound property.
ccoombs
Contributor
6042 points
870 Posts
10-15-2008 11:39 AM |
you can set the e.Row.Background during the LoadingRow event, extracting the bound instance of your class through e.Row.DataContext. though, you'll probably have to set the background accordingly after an edit.
you can disallow editing by setting e.Row.IsEnabled = False on some event, perhaps the PreparingCellForEdit event. enable it later, if necessary.
sladapter
All-Star
18016 points
3,321 Posts
10-15-2008 12:11 PM |
Use DataBinding to do this instead of setting the cell background color in the loaded event.
Read this thread for why and how you want to do it this way:
http://silverlight.net/forums/p/27465/93474.aspx#93474
10-15-2008 1:10 PM |
woah, thanks sladapter. didn't know you could do that. that's a much better method.
10-16-2008 2:44 AM |
Thanks for the responses guys, but I've already looked into the posts mentioned
(including this one)
The problem is that the value converter is not fired when the value of the property on the bound object is changed.(It's only fired when the rows are loaded)
I have the grid bound to a collection (TwoWay) that implements INotifyCollectionChanged,
Here's a xaml snippet:
<my1:DataGrid x:Name="dgrdCompetencies" RowBackground="{Binding Converter={StaticResource rvc}}">
where rvc = resource pointer to class as illustrated by Yi-Lun
Another problem is that it seems when the ValueConverter is fired, it is only fired once for the parent object, and not for each item in the collection.
eg: The UserControl DataContext is set to Person class, and the ItemSource of the Grid is set to "Addressses" property of Person. When the value converter Convert method is fired, it only gets fired once on grid load with the Person instance as the value parameter.
Am I just missing something?
10-16-2008 3:19 AM |
Sample app:
What I want is the datagridrow background to change to red whenever the street property on address is "red"
The Data:
namespace DatagridRow{ public class Person { public Person() { Addresses = new ObservableCollection<Address>(); Addresses.Add(new Address {Street = "Red"}); Addresses.Add(new Address {Street = "Green"}); } public string Name { get; set; } public ObservableCollection<Address> Addresses { get; set; } } public class Address { public string Street { get; set; } }}
Page.xaml:
<UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="DatagridRow.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:DatagridRow="clr-namespace:DatagridRow" Width="400" Height="300"> <UserControl.Resources> <DatagridRow:RowBackgroundValueConverter x:Key="rvc"/> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White"> <data:DataGrid x:Name="dg" RowBackground="{Binding Converter={StaticResource rvc}}" ItemsSource="{Binding Addresses, Mode=TwoWay}"> </data:DataGrid> </Grid></UserControl>
Page.xaml.cs
namespace DatagridRow{ public partial class Page : UserControl { private Person _person; public Page() { InitializeComponent(); Loaded += new RoutedEventHandler(Page_Loaded); } void Page_Loaded(object sender, RoutedEventArgs e) { _person = new Person(); DataContext = _person; } }}
ValueConverter:
namespace DatagridRow{ public class RowBackgroundValueConverter : IValueConverter { #region IValueConverter Members public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { Address boundItem = value as Address; if (boundItem != null) { if (boundItem.Street.ToUpper() == "RED") { return new SolidColorBrush(Colors.Red); } return new SolidColorBrush(Colors.White); } return new SolidColorBrush(Colors.White); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; } #endregion }}
10-16-2008 8:48 AM |
I did not see that your Person implemented INotifyPropertyChanged interface.
10-16-2008 8:58 AM |
I don't think that solves the problem (my production stuff does implement the interface)
Anyways - here's the updated code for completeness:
using System.Collections.ObjectModel;using System.ComponentModel;namespace DatagridRow{ public class Person : INotifyPropertyChanged { public Person() { Addresses = new ObservableCollection<Address>(); Addresses.Add(new Address {Street = "Red"}); Addresses.Add(new Address {Street = "Green"}); } private string _name; public string Name { get { return _name; } set { _name = value; OnPropertyChanged("Name"); } } public ObservableCollection<Address> Addresses { get; set; } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } } public class Address : INotifyPropertyChanged { private string _street; public string Street { get { return _street; } set { _street = value; OnPropertyChanged("Street"); } } public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }}
10-16-2008 1:32 PM |
I don't think you can bind to RowBackgroundColor. You need to bind the the background color of each column control:
Here is my test code:
<Grid x:Name="LayoutRoot" Background="White"> <data:DataGrid x:Name="dg"> <data:DataGrid.Columns> <data:DataGridTemplateColumn> <data:DataGridTemplateColumn.CellTemplate> <DataTemplate> <Grid Background="{Binding Color, Converter={StaticResource rvc}}"> <TextBlock Text="{Binding Name}"></TextBlock> </Grid> </DataTemplate> </data:DataGridTemplateColumn.CellTemplate> </data:DataGridTemplateColumn> </data:DataGrid.Columns> </data:DataGrid> </Grid>
public class TestData: INotifyPropertyChanged { string _color; public string Color { get { return _color; } set { _color = value; this.NotifyChange("Color"); } } string _name; public string Name { get { return _name; } set { _name = value; this.NotifyChange("Name"); } } void NotifyChange(params string[] properties) { if (PropertyChanged != null) { foreach (string p in properties) PropertyChanged(this, new PropertyChangedEventArgs(p)); } } public event PropertyChangedEventHandler PropertyChanged;
}
public partial class Page : UserControl { public Page() { InitializeComponent(); List<TestData> list = new List<TestData>(); for(int i =0; i< 50; i++) { TestData d = new TestData{Name="Test" + i.ToString()}; if (i % 3 == 0) d.Color = "Red"; list.Add(d); } this.dg.ItemsSource = list; } }
10-16-2008 5:21 PM |
ah, now that seems to make a bit more sense. Thanks sladapter, I'll give it a crack when I get to work tomorrow morning.
10-17-2008 3:39 AM |
Excellent. Got it working with some minor changes:
it needs to be an editable grid, so I changed the xaml to look like so:
<data:DataGrid x:Name="dg" AutoGenerateColumns="False" ItemsSource="{Binding Addresses, Mode=TwoWay}"> <data:DataGrid.Columns> <data:DataGridTemplateColumn> <data:DataGridTemplateColumn.CellTemplate> <DataTemplate> <Grid> <TextBox Text="{Binding Street, Mode=TwoWay}" Background="{Binding Street, Converter={StaticResource rvc}}"></TextBox> </Grid> </DataTemplate> </data:DataGridTemplateColumn.CellTemplate> </data:DataGridTemplateColumn> </data:DataGrid.Columns> </data:DataGrid>
Changes:I had to set the AutoGenerateColumns = false, because now I have to define these DataGridTemplateColumns.Changed the TextBlock to TextBox, and moved the Background binding to the TextBox background, while also making the binding TwoWay, so that background changes when the value is edited.
application:I initially only needed this to change the background of the entire row to a specific colour, depending on the EditState of the object that the row is bound to, but doing this on a cell level, one can easily see that this can be extended to hook up object validators to the ValueConverters - lots of possibilities :)
I still think this is a hell of a lot of hoops to jump through just to get the rowbackground to change - would have been way easier if I could have somehow got the reference to the row, given a specific object, that way the code could have been as simple as: (while leaving AutoGenerateColumns = true)
//psuedo-code: private void ObjectPropertyChanged(Address address){ if (address.Street == "red")
{ foreach(DataGridRow row in dgAddresses.Rows) { if (row.DataContext == address) { row.BackGround = new SolifColorBrush(Colors.Red); }
balukr54
199 points
174 Posts
02-26-2009 12:49 AM |
Hi How can i change the background of a particular row(entire row) in datagrid. i have a checkbox column when users checks/unchecks it the color has to change. thanks in advance balu.
varadhg
22 points
21 Posts
05-17-2009 3:15 PM |
The best & easy way to change background of a row [say u have check box on each row ..] and when the check box is checked we have an event
gridRow.Background = new SolidColorBrush(Color.FromArgb(255, 108, 108, 108)); // some random color
gridRow.Background = _new SolidColorBrush(Color.FromArgb(255, 8, 18, 108)); // some random color
If u bind the datagrid under observableCollection, then for some reason the row background is not updated immediately, takes around ~2-4 sec...!!
Thanks,
NickVers...
16 points
6 Posts
05-31-2009 2:06 PM |
I actually found a very easy way of getting this to work consistenly and without too much code. The trick is to bind the background property of the row with a valueconverter during the LoadingRow event and then to call OnApplyTemplate on the row during the CellEditEnded event.
It should look something like this:
1 public partial class myPage : Page 2 { 3 RowColorConverter rowColorConverter; 4 5 myPage() 6 { 7 InitializeComponent(); 8 9 rowColorConverter = new RowColorConverter(); 10 page.myGrid.LoadingRow += new EventHandler(myGrid_LoadingRow); 11 page.myGrid.CellEditEnded += new EventHandler(myGrid_CellEditEnded); 12 } 13 14 void myGrid_CellEditEnded(object sender, DataGridCellEditEndedEventArgs e) 15 { 16 e.Row.OnApplyTemplate(); 17 } 18 19 void myGrid_LoadingRow(object sender, DataGridRowEventArgs e) 20 { 21 Binding b = new Binding("myProperty") 22 { 23 Mode = BindingMode.OneWay, 24 Converter = rowColorConverter, 25 ValidatesOnExceptions = true 26 }; 27 28 e.Row.SetBinding(DataGridRow.BackgroundProperty, b); 29 } 30 } 31 32 public class RowColorConverter : IValueConverter 33 { 34 public RowColorConverter() { } 35 36 #region IValueConverter Members 37 38 public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 39 { 40 switch (value as string) 41 { 42 case "WE": 43 return new System.Windows.Media.SolidColorBrush(Colors.LightGray); 44 45 case "H": 46 return new System.Windows.Media.SolidColorBrush(Color.FromArgb(255, 220, 240, 220)); 47 48 case "OH": 49 return new System.Windows.Media.SolidColorBrush(Color.FromArgb(255, 220, 220, 220)); 50 51 case "E": 52 return new System.Windows.Media.SolidColorBrush(Color.FromArgb(255, 220, 220, 240)); 53 54 case "I": 55 return new System.Windows.Media.SolidColorBrush(Color.FromArgb(255, 240, 220, 220)); 56 57 default: 58 return new System.Windows.Media.SolidColorBrush(Colors.White); 59 } 60 } 61 62 public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 63 { 64 throw new NotImplementedException(); 65 } 66 #endregion 67 } 68 69
All you need to do is replace the myPage, myGrid and myProperty in this example to your Silverlight Page, DataGrid en the property you want the background color to depend on.
rarich01
5 points
4 Posts
06-13-2009 7:25 PM |
NickVerschueren... you da man! I spent two days trying to bind the datagrid's RowBackground property directly in xaml to no avail... until I stumbled upon this post. Thanks a million.
Pashec
8 points
10-20-2009 3:29 AM |
Hi Nick,
Your solution is great. But it seems to don't work as expected due Silverlight bug (I know that Yi Lun-Luo said that this is not a bug but he didn't provide correct workaround for this http://forums.silverlight.net/forums/t/27467.aspx). So what is my main problem?
I did all as you wrote. When my datagrid first loads everything is OK. The color is changed according to the binding property. The problem arises when I begin when I change ViewNavLink.Change property (actually it changes automatically by event). The row doesn't change its color immediately. The color is changed only after I scroll on my datagrid. Also background color of rows changes randomly. I use Silverlight 3.
Here is my piece of code.
1 public partial class GridViewDataGridControl : UserControl { 2 private NavLinkRowColorConverter NavLinkRowColorConverter = new NavLinkRowColorConverter(); 3 4 public GridViewDataGridControl() { 5 InitializeComponent(); 6 GridViewDataGrid.LoadingRow += Grid_LoadingRow; 7 GridViewDataGrid.CellEditEnded += myGrid_CellEditEnded; 8 } 9 10 void myGrid_CellEditEnded(object sender, DataGridCellEditEndedEventArgs e) { 11 e.Row.OnApplyTemplate(); 12 } 13 14 private void Grid_LoadingRow(object sender, DataGridRowEventArgs e) { 15 var b = new Binding("") 16 { 17 Mode = BindingMode.OneWay, 18 Converter = NavLinkRowColorConverter, 19 ValidatesOnExceptions = true 20 }; 21 22 e.Row.SetBinding(BackgroundProperty, b); 23 } 24 } 25 26 public class NavLinkRowColorConverter : IValueConverter { 27 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { 28 ViewNavLink nl = (ViewNavLink)value; 29 if (nl.New) { 30 return new SolidColorBrush(Colors.Yellow); 31 } 32 if (nl.Changed) { 33 return new SolidColorBrush(Colors.Red); 34 } 35 return new SolidColorBrush(Colors.White); 36 } 37 38 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { 39 throw new NotImplementedException(); 40 } 41 }
10-20-2009 8:06 PM |
Ref: your code Line #15 --> var b = new Binding("") --> this should be --> new Binding("YourBindingProperty")....
10-21-2009 2:43 AM |
My DataGrid works with ObservableCollection<ViewNavLink> and I need to pass ViewNavLink (root element) to my converter. So var b =new Binding("") is OK. Also I tried JIC to change my binding as you said but it didn't give success.
Guys, any other ideas?
10-21-2009 8:20 AM |
ok! I had so many issues using observablecollection and rowcolor change!! finally i used a regular collection with pagedCollectionView and call the refresh() method when data changes!
anyhow, see if u can create a sample project (& upload) that reproduces the issue...
10-21-2009 11:19 AM |
Varadhg,
Below is the link to my sample project (I didn't find the way how to attach it directly to this forum). You can see that rows which have IsNew==true should be Yellow. Try to set IsNew=true (click on the appropriate checkbox) at runtime - row became Yellow only after some play with scrolling.
http://rapidshare.com/files/295995866/TestGridColoring.zip.html
Let me know if you will not be able to download my project
10-21-2009 4:41 PM |
Here's the modified project uploaded
http://cid-2dbf03829f442604.skydrive.live.com/self.aspx/.Public/TGridRowBGround.zip
As u can see, there are lots of work around i've to make, to set a simple row background color! but at leat it works.
since you're using scroll bar, right now scrolling and selecting a checkbox, selects changes the row color but scrolls up, i'll try
to fix this later this week, but if u find a solution let me know.. or if u improve this code also let me know...
Already opened a thread (long long back!) and so far no response from MS !! (Thank you Microsoft Silver light!)
http://betaforums.silverlight.net/forums/p/118350/270580.aspx#270580
10-23-2009 3:50 AM |
Thanks for working example. If I optimize it or find another solution I let you know.
Also it will be very interesting to get clean workaround for this bug from the SL team.