DataGridComboBoxColumn doesn't display my collection - c#

I just want to do a simple thing, I just want to show my list of versions into a combobox in a datagrid.
The column "Versions" displays "Collections"... and I don't know why!
Here the code into my xaml:
<DataGrid Name="DataGridTableau" Grid.Column="0" Grid.Row="0"
AutoGenerateColumns="False"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto" CanUserAddRows="True"
ItemsSource="{Binding }"
>
<DataGrid.Columns>
<DataGridTextColumn Header="Sofware" Width="*" IsReadOnly="True" Binding="{Binding Path=Software}">
</DataGridTextColumn>
<DataGridComboBoxColumn Header="Version" Width="*" IsReadOnly="True"
ItemsSource="{Binding Path=Versions}">
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
And into xaml.cs :
public ObservableCollection<ElementTableau> Elements;
public VueEtapeSelection()
{
InitializeComponent();
Elements = new ObservableCollection<ElementTableau>();
List<string> versions = new List<string>();
versions.Add("3.0");
versions.Add("3.1");
Elements.Add(new ElementTableau("Excel", versions));
this.DataGridTableauEKs.DataContext = Elements;
}
public class ElementTableau
{
private string sofware;
public string Software
{
get { return software; }
set { software = value; }
}
private List<string> versions;
public List<string> Versions
{
get { return versions; }
set { versions = value; }
}
public ElementTableau(string software, List<string> versions)
{
this.software = software;
this.versions=versions
}
}
Thanks for your help!

Try setting the ItemsSource using a style:
<DataGrid.Columns>
<DataGridTextColumn Header="Sofware" Width="*" IsReadOnly="True" Binding="{Binding Path=Software}" />
<DataGridComboBoxColumn>
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=Versions}" />
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="ItemsSource" Value="{Binding Path=Versions}" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
Binding the ItemsSource property of the column doesn't work, because it is evaluated in the context of the column itself which has no parent FrameworkElement. Using the style instead sets the ItemsSource as soon as the ComboBox is being rendered. The ComboBox is integrated in the logical tree and its DataContext can be evaluated, that's why it works.
Edit:
To bind your selected items, add some setters like this:
<Setter Property="SelectedItem" Value="{Binding Path=SelectedVersion}" />

Related

Cannot get Nested Object to show in DataGrid as Nested Datgrid Row

I am new to WPF and XAML but am picking it up fairly quick I think.
I have a custom project I'm working on that has some objects built out like below. All the data is stored in XML files and loaded into the objects on startup. Note, I'm changing names for sack of privacy on the code/application.
Overarching Class, let's call it Classrooms in a school
Contains a property of Classrooms as ObservableCollection
Uses INotifyPropertyChanged
Primary Object Class, let's call it Classroom. Contains unique properties about the object and a property of Students as ObservableCollection
Uses INotifyPropertyChanged
Secondary Object Class, let's call it. Contains unique properties about the student and a property of Supplies as ObservableCollection
Uses INotifyPropertyChanged
Third Object Class, let's call it SupplyItem. Contains unique properties about the object.
Uses INotifyPropertyChanged
My desire is to have a DataGridView on my MainWindow have a bound instance of the Overarching Collection Class. Each row should represent a Primary Object (Classroom) and show it's properties.
Each Row should have an expander button to expand the row and view a DataGrid with rows for each of the Secondary Object's (Student) associated to the Primary Objects.
The Third object will come later in a separate data grid. I need to get this first part working.
I am struggling to make this work. I have tried so many different ways to do this through XAML I've lost count. Nothing is working.
Intellisense in XAML shows that each property and ItemSource is mapped appropriately. There are no build errors. However, when debugging, I can see eah Primary Object listed in the rows. On expanding Rows that have Secondary Objects, the DataTable Appears, but there is no data listed in the table.
I have also used the Live Property Explorer and Live Visual Tree. I can see all the Primary Objects in the Visual Tree as Rows in the data grid. I can even go into the associated Secondary objects and see the necessary properties for those. I cannot however, get this to display in a working application.
Please help! What am I doing wrong? Greatly appreciate the help.
Overarching Classrooms Class
public class Classrooms : ClassroomBaseClass
{
private static ObservableCollection<Classroom> _colls;
public ObservableCollection<Classroom> Colls
{
get { return _colls; }
set
{
_colls = value;
OnPropertyChanged(nameof(ObservableCollection<Classroom>));
}
}
}
Primary Object Class (Classroom)
public class Classroom : ClassroomBaseClass
{
private Guid? _classroomId;
public Guid? ClassroomId
{
get { return _classroomId; }
set
{
_classroomId = value;
OnPropertyChanged(nameof(ClassroomId));
}
}
private ObservableCollection<Student> _students;
public ObservableCollection<Job> Students
{
get { return _students; }
set
{
_students = value;
OnPropertyChanged(nameof(Students));
}
}
}
Secondary Object Class, Student
public class Student : StudentBaseClass
{
private Guid? _studentId;
public Guid? StudentId
{
get { return _studentId; }
set
{
_studentId = value;
OnPropertyChanged(nameof(StudentId));
}
}
private ObservableCollection<SupplyItem> _supplyitems;
public ObservableCollection<SupplyItem> SupplyItems
{
get { return _supplyitems; }
set
{
_supplyitems = value;
OnPropertyChanged(nameof(SupplyItems));
}
}
}
Third Object Class, SupplyItem
public class SupplyItem : SupplyItemBaseClass
{
private Guid? _supplyId;
public Guid? SupplyId
{
get { return _supplyId; }
set
{
_supplyId = value;
OnPropertyChanged(nameof(SupplyId));
}
}
}
XAML - Top of XAML
xmlns:appObjects="clr-namespace:App.Objects"
<Window.DataContext>
<appObjects:Classrooms/>
</Window.DataContext>
XAML
<DataGrid x:Name="ClassroomDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected"
AutoGenerateColumns="False" RowDetailsVisibilityChanged="ClassroomDataGrid_RowDetailsVisibilityChanged"
Margin="30,60,30,10"
ItemsSource="{Binding Path=Colls}">
<DataGrid.Resources>
<Style BasedOn="{StaticResource {x:Type DataGridColumnHeader}}"
TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="#666666" />
<Setter Property="Foreground" Value="#ffffff" />
<Setter Property="FontSize" Value="15" />
<Setter Property="FontStyle" Value="Normal" />
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="GUID" Binding="{Binding Path=ClassroomId,UpdateSourceTrigger=PropertyChanged}"
CanUserResize="True"
Width="200" />
<DataGridTemplateColumn Header="Students Expander" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="appObjects:Classroom">
<Expander Width="50"
Expanded="Expander_Expanded"
Collapsed="Expander_Collapsed"
IsExpanded="{Binding Expanded,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate >
<DataGrid x:Name="StudentDataGrid"
IsReadOnly="True"
Margin="15,8,8,8"
Width="1000"
AutoGenerateColumns="False"
ItemsSource="{Binding Path=Coll.Students}">
<DataGrid.Columns>
<DataGridTextColumn Header="Student Id"
Binding="{Binding Path =StudentId,UpdateSourceTrigger=PropertyChanged}"
Width="Auto"
Visibility ="Hidden"/>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid.Columns>
</DataGrid>
in the StudentDataGrid ItemsSource should be ItemsSource="{Binding Path=Students}" instead of ItemsSource="{Binding Path=Coll.Students}"
also make sure you have some columns in that DataGrid which are not hidden:
<DataGrid x:Name="StudentDataGrid"
IsReadOnly="True"
Margin="15,8,8,8"
Width="1000"
AutoGenerateColumns="False"
ItemsSource="{Binding Path=Students}">
<DataGrid.Columns>
<DataGridTextColumn Header="Student Id"
Binding="{Binding Path=StudentId}"
Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
Thanks to both #ASh and #sfaust for helping me understand this. The answer was in the DataGrid.RowDetailsTemplate / DataTemplate. We had to set the DataType here to the Classroom class as that is what each row represents. Then the DataGrid ItemSource is set to the Classroom ObservableCollection property for Students.
Here's the updated XAML code
'''
<DataGrid.Resources>
<Style BasedOn="{StaticResource {x:Type DataGridColumnHeader}}"
TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Background" Value="#666666" />
<Setter Property="Foreground" Value="#ffffff" />
<Setter Property="FontSize" Value="15" />
<Setter Property="FontStyle" Value="Normal" />
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="GUID" Binding="{Binding Path=ClassroomId,UpdateSourceTrigger=PropertyChanged}"
CanUserResize="True"
Width="200" />
<DataGridTemplateColumn Header="Students Expander" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate DataType="appObjects:Classroom">
<Expander Width="50"
Expanded="Expander_Expanded"
Collapsed="Expander_Collapsed"
IsExpanded="{Binding Expanded,UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate DataType="{x:Type appObjects:Classroom}">
<DataGrid x:Name="StudentDataGrid"
IsReadOnly="True"
Margin="15,8,8,8"
Width="1000"
AutoGenerateColumns="False"
ItemsSource="{Binding Path=Students}">
<DataGrid.Columns>
<DataGridTextColumn Header="Student Id"
Binding="{Binding Path =StudentId,UpdateSourceTrigger=PropertyChanged}"
Width="Auto"
Visibility ="Visible"/>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid.Columns>
</DataGrid>
'''

WPF DataGrid different controls in the same column - incorrect binding

I am developing the application which performs checks on the list of items. Each item has the list of the checks that need to be performed on it. Each check can be one of 3 types: CheckBox, ComboBox, TextBox.
I would like to have Datagrid with 2 columns (one for item name, second for list of checks). Second column contains another DataGrid with 2 columns (one for check name, second for check control). The purpose is to have different types of controls in the same column bound with the Check models.
The problem is that binding with CheckValue doesn't work, however bindings with all the other properties work fine.
The last column contains CheckBoxes, TextBox and ComboBox, however they are not filled with any values.
Does anyone know what is wrong with below code?
Here are examples of model classes
public class Item
{
public string ItemName { get; set; }
public ObservableCollection<Check> Checks { get; set; }
public Item()
{
Checks = new ObservableCollection<Check>();
}
}
public enum CheckType
{
CheckBox,
ComboBox,
TextBox
}
public abstract class Check
{
public string CheckName { get; set; }
public CheckType CheckType { get; protected set; }
public abstract object CheckValue { get; set; }
}
public class CheckBox : Check
{
private bool checkValue;
public CheckBox()
{
CheckType = CheckType.CheckBox;
}
public override object CheckValue
{
get
{
return checkValue;
}
set
{
checkValue = (bool)value;
}
}
}
public class ComboBox : Check
{
private List<string> checkValue;
public ComboBox()
{
CheckType = CheckType.ComboBox;
}
public override object CheckValue
{
get
{
return checkValue;
}
set
{
checkValue = value as List<string>;
}
}
}
public class TextBox : Check
{
private string checkValue;
public TextBox()
{
CheckType = CheckType.TextBox;
}
public override object CheckValue
{
get
{
return checkValue;
}
set
{
checkValue = value as string;
}
}
}
public class MainViewModel
{
public ObservableCollection<Item> Items { get; set; }
public MainViewModel()
{
Items = new ObservableCollection<Item>();
Item item = new Item();
item.ItemName = "First item";
Check check1 = new CheckBox() { CheckName = "Check 1", CheckValue = true };
Check check2 = new CheckBox() { CheckName = "Check 2", CheckValue = false };
Check text1 = new TextBox() { CheckName = "Check 3", CheckValue = "Please enter check" };
Check combo1 = new ComboBox() { CheckName = "Check 4", CheckValue = new List<string> { "Value1", "Value2" } };
item.Checks.Add(check1);
item.Checks.Add(check2);
item.Checks.Add(text1);
item.Checks.Add(combo1);
Items.Add(item);
}
}
And finally here is XAML code of the main window.
<Window x:Class="ItemTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm ="clr-namespace:ItemTest"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm:MainViewModel x:Key="mainViewModel"/>
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource mainViewModel}}">
<DataGrid ItemsSource="{Binding Path=Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Item" Binding="{Binding ItemName}" />
<DataGridTemplateColumn Header="Checks">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DataGrid ItemsSource="{Binding Checks}" AutoGenerateColumns="False" HeadersVisibility="None">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding CheckName}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding CheckType}" Value="CheckBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox IsChecked="{Binding CheckValue}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding CheckType}" Value="ComboBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ComboBox ItemsSource="{Binding CheckValue}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding CheckType}" Value="TextBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding CheckValue}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
Just set the ItemControl's Content property:
<ContentControl Content="{Binding}">
WPF will automatically set DataTemplate's DataContext to its parent ContentControl's Content. But in your XAML you don't set the Content property (you only specify ContentControl's Style, but forget to set its Content).
And don't forget to set UpdateSourceTrigger=PropertyChanged on your control bindings, otherwise you may see no updates in your viewmodel.
XAML example working, with binding for BindingList :
<DataGrid x:Name="dataGridParametros"
Grid.Row="1"
Margin="5"
AutoGenerateColumns="False"
HeadersVisibility="All"
ItemsSource="{Binding}"
RowHeaderWidth="20"
SelectionUnit="FullRow"
ScrollViewer.CanContentScroll="True"
CanUserAddRows="false"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
FontFamily="Arial"
CellEditEnding="dataGridParametros_CellEditEnding" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding IdParametro}" Header="Id" FontFamily="Arial" IsReadOnly="True" Visibility="Hidden"/>
<DataGridTextColumn Binding="{Binding Codigo}" Header="Código" FontFamily="Arial" IsReadOnly="True"/>
<DataGridTextColumn Width="200" Binding="{Binding Mnemonico}" Header="Mnemonico" FontFamily="Arial" IsReadOnly="True" />
<DataGridTextColumn Width="250*" Binding="{Binding Descricao}" Header="Descrição" FontFamily="Arial" IsReadOnly="True" />
<DataGridTemplateColumn Header="Valor" Width="150">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding TipoCampo}" Value="CheckBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox IsChecked="{Binding Valor , Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding TipoCampo}" Value="ComboBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<ComboBox ItemsSource="{Binding Valor , Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding TipoCampo}" Value="TextBox">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding Valor , Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>

Refresh a single item in a WPF ItemCollection

I have a DataGrid that is bound to some XML data.
When I make changes in the XML data, the DataGrid does not refresh to reflect those changes.
My "simple" way of fixing this is to call MyDataGrid.Items.Refresh() every time I make a change.
However, this is laggy and seems pretty inefficient.
How can I refresh just a single row, rather than the entire data grid? I have easy access to the DataGridRow as well as the XmlElement that is changed, but I just don't know what function to call.
Been stuck on this problem for 3-4 hours now and have tried dozens of solutions, but just can't get it to work.
Below is relevant code.
A) Defining the style.
<!-- Field Value Style -->
<local:FieldValueConverter x:Key="FieldValueConverter"/>
<local:Node x:Key="Node"/>
<Style x:Key="fieldValueStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="{Binding ., Converter={StaticResource FieldValueConverter}}"/>
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"/>
<Setter Property="Focusable" Value="False"/>
</Style>
B) Defining the DataGrid
<DataGrid x:Name="FieldPanelDataGrid" DockPanel.Dock="Left"
AutoGenerateColumns="False"
DataContext="{Binding ElementName=ObjectPanelListBox, Path=SelectedItem}"
ItemsSource="{Binding}"
IsSynchronizedWithCurrentItem="True"
IsReadOnly="True"
CanUserResizeRows="False"
CanUserResizeColumns="True"
KeyboardNavigation.IsTabStop="False"
Visibility="Visible"
SelectionMode="Single">
<DataGrid.Resources>
<Style TargetType="DataGridRow">
<EventSetter Event="MouseDoubleClick" Handler="FieldCell_MouseDoubleClick"/>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="FieldCell_PreviewMouseLeftButonDown"></EventSetter>
<EventSetter Event="PreviewKeyDown" Handler="FieldCell_PreviewKeyDown"></EventSetter>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn x:Name="FieldName" Header="Name" CanUserSort="False" ElementStyle="{StaticResource fieldNameStyle}"/>
<DataGridTextColumn x:Name="FieldValue" Header="Value" Width="*" ElementStyle="{StaticResource fieldValueStyle}"/>
</DataGrid.Columns>
</DataGrid>
I suggest to use an ObservableCollection as ItemSource and the entries in the ObservableCollection have to implement INotifyPropertyChanged. Then you have the benefit if the rows change, the ObservableCollection will tell that your UI and it will update.
Example:
Your entry class:
public class MyXmlObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string fieldName;
public string FieldName
{
get { return fieldName; }
set
{
fieldName = value;
NotifyPropertyChanged("FieldName");
}
}
NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Your code for the UserControl (ViewModel, Controller or Code behind):
public ObservableCollection<MyXmlObject> MyCollection { get; set; }
And as I mentioned in your xaml you simply bind the collection to the ItemsSource
<DataGrid ItemsSource="{Binding MyCollection}" .../>
Now only the items beeing changed get updated.

Edit Datagrid row background based on object value

I have the following class:
public class Order
{
public int Id { get; set; }
public string OrdName { get; set; }
public int Quant { get; set; }
public int Supplied { get; set; }
}
and this DataGrid:
<DataGrid x:Name="dgOrders" Margin="5" CanUserAddRows="False" CanUserDeleteRows="False"
SelectionMode="Extended" ItemsSource="{Binding}"
SelectionUnit="FullRow" VerticalScrollBarVisibility="Auto"
Height="300" Width="700" HorizontalAlignment="Left" AutoGenerateColumns="False" Grid.Row="2">
<DataGrid.Columns>
<DataGridTextColumn Header="Order Name" Binding="{Binding OrdName}" IsReadOnly="True"/>
<DataGridTextColumn Header="Quantity" Binding="{Binding Quant}" IsReadOnly="True"/>
<DataGridTextColumn Header="Supplied" Binding="{Binding Supplied}" IsReadOnly="True"/>
</DataGrid.Columns>
</DataGrid>
What I want is that when the Quantity and Supplied properties are equal the row background color will change.
I tried it with an Event Trigger and a Converter but with no luck (possibly I didn't implement them in the xaml correctly).
also trying to do this from the code behind didn't work (tried to get the row instance like this suggests but I keep getting null for the row).
A Binding cannot be set on the Value property of a DataTrigger. Therefore, you'll need to add an extra property into your data type class:
public bool AreQuantityAndSuppliedEqual
{
return Quantity == Supplied;
}
Then, with this property, you can use the following Style:
<Style x:Key="EqualRowStyle" TargetType="{x:Type DataGridRow}">
<Style.Triggers>
<DataTrigger Binding="{Binding AreQuantityAndSuppliedEqual}" Value="True">
<Setter Property="Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
It would be used like so:
<DataGrid ItemsSource="{Binding YourItems}"
RowStyle="{StaticResource EqualRowStyle}" ... />

Why can't I use DynamicResource with DataGridColumn.CellStyle

So, for example I have some MVVM WPF application with simple model:
public class MyObject
{
public string F1 { get; set; }
public string F2 { get; set; }
}
and simple view model that creates 3 rows:
public class MyViewModel
{
public ObservableCollection<MyObject> Objects { get; set; }
public MyViewModel()
{
Objects = new ObservableCollection<MyObject>
{
new MyObject{F1 = "V1",F2 = "B1"},
new MyObject{F1 = "V2",F2 = "B2"},
new MyObject{F1 = "V3",F2 = "V3"}
};
}
}
And in view I have a DataGrid with manually defined columns and for each column I set CellStyle. Both styles defined in Window.Resources block. But for first column, I use StaticResource and for the second DynamicResource
View XAML:
<Window x:Class="WpfApplication12.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" x:Name="WholeWindow">
<Window.Resources>
<Style x:Key="BaseCellClass" TargetType="DataGridCell">
<Setter Property="Foreground" Value="Blue" />
</Style>
</Window.Resources>
<Grid>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding ElementName=WholeWindow, Path=ViewModel.Objects}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding F1}" Header="F1" CellStyle="{StaticResource BaseCellClass}" />
<DataGridTextColumn Binding="{Binding F2}" Header="F2" CellStyle="{DynamicResource BaseCellClass}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
So the problem is: in the second column, the resource doesn't get applied to the column.
You could create resources for the properties in your DataGridCell Style and then reference them as a DynamicResource within the Style definition:
Based on your example it would look like this:
<Window.Resources>
<SolidColorBrush x:Key="ForegroundBrush" Color="Blue"/>
<Style x:Key="BaseCellClass" TargetType="DataGridCell">
<Setter Property="Foreground" Value="{DynamicResource ForegroundBrush}" />
</Style>
</Window.Resources>
<Grid>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding ElementName=WholeWindow, Path=ViewModel.Objects}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding F1}" Header="F1" CellStyle="{StaticResource BaseCellClass}" />
<DataGridTextColumn Binding="{Binding F2}" Header="F2" CellStyle="{StaticResource BaseCellClass}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
The resources would of course be located in separate resource files.
I found solution with using a little service. In few words I write in xaml this code:
<wpfApplication12:DataGridColumnDynamicStyleService TargetGrid="{Binding ElementName=Grid}">
<wpfApplication12:DataGridColumnDynamicStyleService.ColumnStyles>
<wpfApplication12:DataGridColumnStyleBinding ColumnTag="C1" DynamicStyle="{DynamicResource BaseCellClass}" />
</wpfApplication12:DataGridColumnDynamicStyleService.ColumnStyles>
</wpfApplication12:DataGridColumnDynamicStyleService>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding ElementName=WholeWindow, Path=ViewModel.Objects}" x:Name="Grid">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding F1}" Header="F1" wpfApplication12:DataGridColumnDynamicStyle.ColumnTag="C1" />
<DataGridTextColumn Binding="{Binding F2}" Header="F2" wpfApplication12:DataGridColumnDynamicStyle.ColumnTag="C2" />
</DataGrid.Columns>
</DataGrid>
Here, as you can see, I use attached property ColumnTag to identify columns. And I create a service control that defines styles for columns and set target datagrid as TargetGrid
If you want to see all the code, here is the link to the solution on google drive

Categories