Skip to main content

Answered Question DataGrid Row Background based on bound value + readonly rowRSS Feed

(0)

InquisitorJax
Inquisit...

Member

Member

84 points

62 Posts

DataGrid Row Background based on bound value + readonly row

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
ccoombs

Contributor

Contributor

6042 points

870 Posts

Re: DataGrid Row Background based on bound value + readonly row

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
sladapter

All-Star

All-Star

18016 points

3,321 Posts

Re: DataGrid Row Background based on bound value + readonly row

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

sladapter
Software Engineer
Aprimo, Inc

Please remember to mark the replies as answers if they answered your question

ccoombs
ccoombs

Contributor

Contributor

6042 points

870 Posts

Re: Re: DataGrid Row Background based on bound value + readonly row

 woah, thanks sladapter.  didn't know you could do that.  that's a much better method.

InquisitorJax
Inquisit...

Member

Member

84 points

62 Posts

Re: Re: DataGrid Row Background based on bound value + readonly row

 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?

InquisitorJax
Inquisit...

Member

Member

84 points

62 Posts

Re: Re: DataGrid Row Background based on bound value + readonly row

 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
    }
}

 

 

sladapter
sladapter

All-Star

All-Star

18016 points

3,321 Posts

Re: Re: DataGrid Row Background based on bound value + readonly row

I did not see that your Person implemented INotifyPropertyChanged interface.

sladapter
Software Engineer
Aprimo, Inc

Please remember to mark the replies as answers if they answered your question

InquisitorJax
Inquisit...

Member

Member

84 points

62 Posts

Re: Re: DataGrid Row Background based on bound value + readonly row

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));
            }
        }

    }
}

sladapter
sladapter

All-Star

All-Star

18016 points

3,321 Posts

Answered Question

Re: Re: DataGrid Row Background based on bound value + readonly row

 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;
        }         
    }

sladapter
Software Engineer
Aprimo, Inc

Please remember to mark the replies as answers if they answered your question

InquisitorJax
Inquisit...

Member

Member

84 points

62 Posts

Re: Re: DataGrid Row Background based on bound value + readonly row

 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.

InquisitorJax
Inquisit...

Member

Member

84 points

62 Posts

Answered Question

Re: Re: DataGrid Row Background based on bound value + readonly row

 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
balukr54

Member

Member

199 points

174 Posts

Re: Re: Re: DataGrid Row Background based on bound value + readonly row

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
varadhg

Member

Member

22 points

21 Posts

Re: DataGrid Row Background based on bound value + readonly row

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

private void CheckBox_Checked(object sender, RoutedEventArgs e){

DataGridRow gridRow = DataGridRow.GetRowContainingElement((sender as FrameworkElement));

int index = gridRow.GetIndex();

if (index % 2 == 0)

gridRow.Background = new SolidColorBrush(Color.FromArgb(255, 108, 108, 108)); // some random color

else

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,

varadhg

NickVerschueren
NickVers...

Member

Member

16 points

6 Posts

Re: DataGrid Row Background based on bound value + readonly row

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.


Nick Verschueren - Euricom

rarich01
rarich01

Member

Member

5 points

4 Posts

Re: DataGrid Row Background based on bound value + readonly row

 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
Pashec

Member

Member

8 points

4 Posts

Re: DataGrid Row Background based on bound value + readonly row

 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   	}
 

 

varadhg
varadhg

Member

Member

22 points

21 Posts

Re: DataGrid Row Background based on bound value + readonly row

Ref: your code Line #15 --> var b = new Binding("") --> this should be --> new Binding("YourBindingProperty")....

Pashec
Pashec

Member

Member

8 points

4 Posts

Re: DataGrid Row Background based on bound value + readonly row

 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?

varadhg
varadhg

Member

Member

22 points

21 Posts

Re: DataGrid Row Background based on bound value + readonly row

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...

 

Pashec
Pashec

Member

Member

8 points

4 Posts

Re: DataGrid Row Background based on bound value + readonly row

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

varadhg
varadhg

Member

Member

22 points

21 Posts

Re: DataGrid Row Background based on bound value + readonly row

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

 

Thanks,

varadhg

Pashec
Pashec

Member

Member

8 points

4 Posts

Re: Re: DataGrid Row Background based on bound value + readonly row

  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.

  • Unanswered Question
  • Answered Question
  • Announcement