I have a data grid.
the item source MySource is an observableCollection<myClass>.
The class myClass has a property BackgroundOfRow - its type is Brush.
I want to bind the RowBackground attribute to this property in the xaml.
how can I do it?
my xaml now is:
<DataGrid AutoGenerateColumns="False"
ItemSource="{Binding Source={StaticResource myViewModel}, Path=MySource}">
<DataGrid.Columns>
<DataGridTextColumn Header="First Name"
Binding="{Binding Path=FirstName}"
FontFamily="Arial"
FontStyle="Italic" />
<DataGridTextColumn Header="Last Name"
Binding="{Binding Path=LastName}"
FontFamily="Arial"
FontWeight="Bold" />
</DataGrid.Columns>
</DataGrid>
You can bind the Background property in the RowStyle of DataGrid:
View:
<DataGrid ItemsSource="{Binding EmployeeColl}>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{Binding BackgroundOfRow}"/>
</Style>
</DataGrid.RowStyle>
</DataGrid>
Model:
public class Employee
{
public int ID { get; set; }
public int Name { get; set; }
public int Surname { get; set; }
public Brush BackgroundOfRow { get; set; }
}
ViewModel:
private ObservableCollection<Employee> employeeColl;
public ObservableCollection<Employee> EmployeeColl
{
get { return employeeColl; }
set
{
employeeColl = value;
OnPropertyChanged("EmployeeColl");
}
}
private void PopulateDataGrid()
{
employeeColl = new ObservableCollection<Employee>();
for (int i = 0; i < 100; i++)
{
if(i%2==0)
employeeColl.Add(new Employee() { ID = i, BackgroundOfRow = Brushes.CadetBlue});
else
employeeColl.Add(new Employee() { ID = i, BackgroundOfRow = Brushes.Green });
}
}
Related
i have a xaml page in namespace Dca.KnxPanel.View with a Datagrid defined as
<DataGrid Grid.Row="2" Grid.Column="2" Name="GroupAddressDataGrid" ItemsSource="{Binding Source={x:Static viewModel:DcaAppVm._ProjectGroupAddress}}" AutoGenerateColumns="False" Margin="14,0,-14,0" Grid.RowSpan="2" >
<Border Background="GhostWhite" BorderBrush="Gainsboro" BorderThickness="1">
</Border>
<DataGrid.Columns>
<DataGridTextColumn Header="Group Address" Binding="{Binding Path=Address_Value}" Width="Auto"/>
<DataGridTextColumn Header="Description" Binding="{Binding Path=Datapoint_Name}" Width="Auto"/>
<DataGridTextColumn Header="Length" Binding="{Binding Path=Datapoint_Object_Size}" Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
where the DCA_GroupAddressList and DCA_GroupAddress are defined as in the namespace Dca.KnxPanel.Model
namespace Dca.KnxPanel.Model
{
[DebuggerDisplay("{Address_Value} {Datapoint_Name} {Datapoint_Object_Size}")]
public class DCA_GroupAddress
{
public DCA_GroupAddress(GroupAddress data) {
Address_Value = data.AddressValue.ToString();
Datapoint_Name = data.Name;
if (data.ObjectSize != null)
{
Datapoint_Object_Size = data.ObjectSize.Value;
}
}
public string Address_Value { get; set; }
public string Datapoint_Name { get; set; }
public ObjectSize Datapoint_Object_Size { get; set; }
}
public class DCA_GroupAddressList : ObservableCollection<DCA_GroupAddress>
{
public static DCA_GroupAddressList dca_groupaddresslist { get; set; }
public void Add(GroupAddress item)
{
this.Add(new DCA_GroupAddress(item));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action != null)
{
base.OnCollectionChanged(e);
}
}
}
}
the problem is that the Binding data in DataGrid not works but i don't understand why...
Can you help me?
i have tried many solution and at the end this is what works:
<DataGrid Grid.Row="2" Grid.Column="2" Name="GroupAddressDataGrid" ItemsSource="{Binding _ProjectGroupAddress}" AutoGenerateColumns="False" Margin="14,0,-14,0" Grid.RowSpan="2" >
<Border Background="GhostWhite" BorderBrush="Gainsboro" BorderThickness="1">
</Border>
<DataGrid.Columns>
<DataGridTextColumn Header="Group Address" Binding="{Binding Path=Address_Value}" Width="Auto"/>
<DataGridTextColumn Header="Description" Binding="{Binding Path=Datapoint_Name}" Width="Auto"/>
<DataGridTextColumn Header="Length" Binding="{Binding Path=Datapoint_Object_Size}" Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
The class DCA_GroupAddress now is:
public class DCA_GroupAddress : INotifyPropertyChanged
{
private string address_value, datapoint_name;
private ObjectSize datapoint_object_size;
public DCA_GroupAddress(GroupAddress data) {
Address_Value = data.AddressValue.ToString();
Datapoint_Name = data.Name;
if (data.ObjectSize != null)
{
Datapoint_Object_Size = data.ObjectSize.Value;
}
}
public string Address_Value {
get { return address_value; }
set {
address_value = value;
OnPropertyChanged(new PropertyChangedEventArgs("address_value"));
}
}
public string Datapoint_Name
{
get { return datapoint_name; }
set
{
datapoint_name = value;
OnPropertyChanged(new PropertyChangedEventArgs("datapoint_name"));
}
}
public ObjectSize Datapoint_Object_Size
{
get { return datapoint_object_size; }
set
{
datapoint_object_size = value;
OnPropertyChanged(new PropertyChangedEventArgs("datapoint_object_size"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
}
and i have define a container
public ObservableCollection<DCA_GroupAddress> _ProjectGroupAddress { get; set; } = new ObservableCollection<DCA_GroupAddress>();
my question is why i need to add these code:
GroupAddressDataGrid.Items.Clear();
GroupAddressDataGrid.ItemsSource = _ProjectGroupAddress;
in the UI class constructor? Seems that Items are not empty when the DataGrid is created so the software not use the ItemsSource. I need to clear the items before the binding works...
Thanks
This is frequent question, but still don't know how to make it work. I'm performing a logger and want to set red color on cell. XAML:
<Window.Resources>
<local:LogLevelToColorConverter x:Key="colorConverter"/>
</Window.Resources>
<Grid>
<DataGrid x:Name="dgLog" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Date and time" Binding="{Binding DateTime}" Width="120"/>
<DataGridTextColumn Header="Message1" Binding="{Binding Message}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Foreground"
Value="{Binding Color}" />
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Message2" Binding="{Binding Message}" Foreground="{Binding Level,Converter={StaticResource colorConverter}}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
Code:
namespace DGTest {
public class LogLevelToColorConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is string level) {
var brush = Brushes.White;
if (level == "WARN") {
brush = Brushes.Yellow;
}
else if (level == "ERROR") {
brush = Brushes.Red;
}
return brush;
}
return Brushes.White;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
public class LogMessage {
public string DateTime { get; set; }
public string Level { get; set; }
public string Message { get; set; }
public Brush Color { get; set; }
}
public partial class MainWindow : Window {
readonly ObservableCollection<LogMessage> logMessages = new ObservableCollection<LogMessage>();
public MainWindow() {
InitializeComponent();
dgLog.ItemsSource = logMessages;
logMessages.Add(new LogMessage { DateTime = DateTime.Now.ToString(), Level = "ERROR", Message = "Test message", Color = Brushes.Red });
}
}
Tried to pass Brush in "Color" field directly - not working (Message1 column). Tried with Converter (Message2 column), still no result. Don't know what's wrong.
MainWindow View:
<Grid>
<DataGrid x:Name="dgLog" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Date and time" Binding="{Binding DateTime}" Width="120"/>
<DataGridTextColumn Header="Message" Binding="{Binding Message}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="{Binding Color}" />
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
LogMessage Class:
public class LogMessage
{
public string DateTime { get; set; }
public string Level { get; set; }
public string Message { get; set; }
public Brush Color { get; set; }
}
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
readonly ObservableCollection<LogMessage> logMessages = new ObservableCollection<LogMessage>();
public MainWindow()
{
InitializeComponent();
dgLog.ItemsSource = logMessages;
logMessages.Add(new LogMessage
{
DateTime = DateTime.Now.ToString(),
Level = "ERROR",
Message = "Test message",
Color = Brushes.Red
});
}
}
Result screenshot:
I have an ObservableCollection of class
Public class Object
{
public string Name;
public Employee Employee;
}
public class Employee
{
// few properties
}
Here is my XMAL code for CollectionViewSource:
<CollectionViewSource x:Key="cvsTasks"
Source="{Binding Reels}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Name" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
Here is my DataGrid code:
<DataGrid ItemsSource="{Binding Source={StaticResource cvsTasks}}"/>
Now CollectionViewSource having PropertyGroupDescription on Name and I want to bind my DataGrid with Employee property of CollectionViewSource .
I dont know, if i understand your question correctly.
View:
<Window.Resources>
<CollectionViewSource x:Key="cvsTasks" Source="{Binding List}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Name" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid >
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Source={StaticResource cvsTasks}}">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="EmpID" Binding="{Binding Employee.ID}"/>
<DataGridTextColumn Header="EmpDeportment" Binding="{Binding Employee.Department}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
ViewModel:
public class ViewModel {
public System.Collections.ObjectModel.ObservableCollection<Model.Root> List { get; set; }
public ViewModel() {
List = new System.Collections.ObjectModel.ObservableCollection<Model.Root> {
new Model.Root {
Name = "Peter",
Employee = new Model.Employee {
ID = 1,
Department = "IT"
}
},
new Model.Root {
Name = "Hans",
Employee = new Model.Employee {
ID = 2,
Department = "accounting"
}
},
new Model.Root {
Name = "Bilbo",
Employee = new Model.Employee {
ID = 3,
Department = "ceo"
}
}
};
}
}
Model:
public class Model {
public class Employee {
public int ID { get; set; }
public string Department { get; set; }
}
public class Root {
public string Name { get; set; }
public Employee Employee { get; set; }
}
}
And the result:
Hi have a Datagrid with rowCheckBoxes and an header checkbox for checking all.
The header checkbox bind a method in MainViewModel who will update the property of my model data to true. But the checkboxes are still unchecked.
<DataGrid AutoGenerateColumns="False" x:Name="grdLignes" HorizontalAlignment="Stretch" Margin="0,0,0,0"
VerticalAlignment="Stretch" CanUserAddRows="False" CanUserDeleteRows="False" Grid.ColumnSpan="2"
ItemsSource="{Binding CmLignes, UpdateSourceTrigger=PropertyChanged}">
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=hasLink, Mode=OneTime}" Value="True"/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<CheckBox Command="{Binding RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}, Path=DataContext.ToggleCheckAll}" CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}"/>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="rowCheck" VerticalAlignment="Center" HorizontalAlignment="Center" IsChecked="{Binding Path=hasLink, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Ref Mag" Binding="{Binding Path=refMag}" IsReadOnly="True"/>
<DataGridTextColumn Header="Ref Fourn" Binding="{Binding Path=refFourn}"/>
<DataGridTextColumn Header="Désignation" Binding="{Binding Path=design}"/>
<DataGridTextColumn Header="Quantité" Binding="{Binding Path=qte, StringFormat=N2}"/>
<DataGridTemplateColumn Header="Fournisseur">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<!--<ComboBox ItemsSource="{Binding FournList, RelativeSource={RelativeSource AncestorType=Window}}" SelectedItem="{Binding Path=fourn}"/>-->
<ComboBox ItemsSource="{Binding Path=fournList}" SelectedItem="{Binding Path=selectedFourn, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Fourn princ" Binding="{Binding Path=fournPrinc}" IsReadOnly="True"/>
<DataGridTextColumn Header="Pièce" Binding="{Binding Path=numPiece}" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
Then the MainViewModel:
public class MainViewModel : INotifyPropertyChanged
{
private ContremarqueRepository cmRepos;
private ObservableCollection<Contremarque> cmLignes;
public ObservableCollection<Contremarque> CmLignes
{
get { return cmLignes; }
set
{
cmLignes = value;
OnPropertyChanged("CmLignes");
}
}
public ICommand ToggleCheckAll { get; set; }
public MainViewModel()
{
Collection<Contremarque> cms = cmRepos.getAll(doPiece);
CmLignes = new ObservableCollection<Contremarque>(cms);
ToggleCheckAll = new Command(ActionToggleCheckAll);
}
private void ActionToggleCheckAll(object param)
{
bool isChecked = (bool)param;
if (isChecked)
{
foreach (Contremarque contremarque in CmLignes)
{
contremarque.hasLink = true;
}
}
OnPropertyChanged("CmLignes");
}
}
This is the Contremarque class:
public class Contremarque
{
public bool hasLink { get; set; }
public string refMag { get; set; }
public string refFourn { get; set; }
public string design { get; set; }
public double qte { get; set; }
public string fournPrinc { get; set; }
public List<string> fournList { get; set; }
public string selectedFourn { get; set; }
public string numPiece { get; set; }
public int dlNo;
public override string ToString()
{
string str = string.Empty;
foreach (var prop in this.GetType().GetProperties())
{
str += string.Format("{0} = {1} ", prop.Name, prop.GetValue(this, null));
}
return str;
}
}
The propertyChanged should update the state of my checkboxes isn't it?
Your class Contremarque should implement INotifyPropertyChanged and property hasLink should look like that:
public class Contremarque : INotifyPropertyChanged
{
private bool _hasLink;
public bool hasLink
{
get { return _hasLink; }
set
{
_hasLink= value;
OnPropertyChanged();
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
My little project looks now quite different. Now I have ObservableCollection PackagesList with data which I want to bind with whole DataGrid (via Binding Path) and ObservableCollection DestinationItememsSource where I store cases for DataGridComboboxColumn (as ItemsSourceBinding). SelectedItem property in DatagridComboboxColumn is one value from PackageInfo (and it is one of DestinationNames cases ofc). Binding on DatagridTextBoxColumns is ok.
XAML:
<Window x:Class="test01.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="400" Loaded="Window_Loaded">
<Grid>
<Border x:Name="mainBorder" Margin="20">
<DataGrid x:Name="mainDataGrid" x:Uid="mainDataGrid" AutoGenerateColumns="False"
AlternationCount="2" SelectionMode="Single" HorizontalAlignment="Stretch">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=id}"
Header="ID" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Path=name}"
Header="Name" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Path=quantity}"
Header="Quantity" Width="Auto" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Path=price}"
Header="Price" Width="Auto" IsReadOnly="True"/>
<DataGridComboBoxColumn ItemsSource="{Binding DestinationItemsSource}"
SelectedItemBinding="{Binding Path=destination, UpdateSourceTrigger=PropertyChanged}"
Header="Destination" Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
</Border>
</Grid>
</Window>
C#:
public class PackageInfo
{
public int id { get; set; }
public string name { get; set; }
public int quantity { get; set; }
public double price { get; set; }
public string destination { get; set; }
}
public class DestinationNames : INotifyPropertyChanged
{
public string name { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public DestinationNames(string value)
{
this.name = value;
}
public string DestinationName
{
get { return name; }
set
{
name = value;
OnPropertyChanged("DestinationName");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public partial class MainWindow : Window
{
public ObservableCollection<DestinationNames> DestinationItememsSource { get; set; }
public ObservableCollection<PackageInfo> PackagesList { get; set; }
public MainWindow()
{
InitializeComponent();
}
public void Window_Loaded(object sender, RoutedEventArgs e)
{
LoadPackages();
}
public void LoadPackages()
{
DestinationItememsSource = new ObservableCollection<DestinationNames>();
DestinationItememsSource.Add(new DestinationNames("London"));
DestinationItememsSource.Add(new DestinationNames("Plymouth"));
DestinationItememsSource.Add(new DestinationNames("Birmingham"));
DestinationItememsSource.Add(new DestinationNames("Cambridge"));
PackagesList = new ObservableCollection<PackageInfo>();
PackagesList.Add(new PackageInfo { id = 1, name = "Potato", quantity = 3, price = 2.2, destination = "London" });
PackagesList.Add(new PackageInfo { id = 2, name = "Tomato", quantity = 5, price = 3.8, destination = "Plymouth" });
PackagesList.Add(new PackageInfo { id = 3, name = "Carrot", quantity = 1, price = 5.1, destination = "London" });
PackagesList.Add(new PackageInfo { id = 4, name = "Pea", quantity = 6, price = 1.8, destination = "Plymouth" });
PackagesList.Add(new PackageInfo { id = 5, name = "Bean", quantity = 2, price = 1.5, destination = "Birmingham" });
mainDataGrid.ItemsSource = PackagesList;
}
}
How to bind this DatagridComboboxColumn properly? Should I use INotifyCollectionChanged ?? What if I want to all data in datagrid will be automatically synced with ObservableCollection ?? Please help with some example.
Have PackageInfo implement INotifyPropertyChanged.
An ObservableCollection automatically implements INotifyCollectionChanged.
You will need to add List or ObservableCollection Destinations as a property of PackageInfo
NO class DestinationNames
Just a class DestinationName
Your binding to DestinationItememsSource does not work, because it isnt part of the PackageInfo object. You also cant bind to the Windows DataContext via FindAncestor or ElementName-binding because DataGrid-Columns wont be added to the visual tree.
One solution I can think of is using the CollectionViewSource:
<Window.Resources>
<CollectionViewSource Source="{Binding RelativeSource={RelativeSource Self}, Path=DestinationItememsSource}" x:Key="destinations">
<!--here you can also add Group and SortDescriptions-->
</CollectionViewSource>
</Window.Resources>
<DataGridComboBoxColumn ItemsSource="{Binding Source={StaticResource ResourceKey=destinations}}".../>
I cant test it atm, because I'm still downloading VS 2012.^^
Anotherone would be to simply add a List of Destinations to your PackageInfo object (but this would cause a lot of redundancies).