The basics of binding a list of objects to a DataGrid are discussed here How do I bind a List<CustomObject> to a WPF DataGrid?.
My setup is slightly more complex:
I have RowModels which contain metadata for the row as well as a list of CellModels. The CellModels again contain some metadata and a Value property which is shown in the dataGrid.
To fill my DataGrid I did set
ItemsSource="{Binding RowModelList, UpdateSourceTrigger= PropertyChanged}"
and mapped the columns one-by-one via
<DataGridTextColumn Binding="{Binding CellModelList[0].Value, UpdateSourceTrigger= PropertyChanged}"/>
<DataGridTextColumn Binding="{Binding CellModelList[1].Value, UpdateSourceTrigger= PropertyChanged}"/>
...
This way i could also specify a ColumnHeader for each column and modify the presentation of each column.
However i would like to also use a DataTrigger in my DataGridCell-Style to access the metadata of my CellModels.
this works, but.. well:
Of course I could create a Style for each column like
<Style x:Key="CellStyleColumnZero" TargetType="DataGridCell">
<Style.Triggers>
<DataTrigger Binding="{Binding CellModelList[0].Enabled}" Value="False">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
Question:
I need to set Properties of the DataGridCell depending on the metadata in my CellModel.
I am sure there is a more elegant solution (than the one shown above) which doesn't require customizing every single column. I still need to be able to manually change single columns (especially the ColumnHeaders) which is why i manually declared the DataGridTextColumns one-by-one in the first place.
Do you have any suggestions how to approach this properly?
This was answered in a followup question.
The solution i used was creating an attached property for DataGridCell in which i store the CellModel and access the properties from there.
See the linked question for code:
Structure of WPF DataGrid - change cells depending on value
Related
I've tried many options, but none seem to work fine for me. Here is the problem:
Dynamic columns. I get a list of dictionaries. All dictionaries may have different keys.
E.g.
[ [key1: 111], [key2: 222] ]
[ [key1: 333], [key4: 444] ]
[ [key5: 777] ]
I need to display a grid with all keys as columns like this:
[key1] [key2] [key4] [key5]
111 222
333 444
777
This is pretty easy, I just generate columns in code either for GridView or DataGrid.
But there is a second requirement:
I need to color particular cells depending on value in them.
E.g. for 111 bg color should be green, for 333 it should be red etc.
Currently I can only make one part work. I can dynamically create columns, but then I can't color them. Or I can create static columns and create custom cell templates with bindings for bg colors. But, then in every template I have to specify the binding. So the number of templates will be big.
Is there a way to achive what I need, without generating template for each column?
Personally I would like to avoid generating templates in code.
Not sure I understand your problem completely, but it should be quite simple. DataGrid is amazingly flexible.
You almost have it, as far I understand. Now you want one global CellTemplate that can be obtained from XAML resources(so you don't hardcode it into code).
The CellTemplate can change colors based on triggers. You can either hardcode it into XAML or provide Dictionary that can map from "VALUE" to "COLOR" and just bind CellTemplate's Background to that Dictionary.
When you generate dynamically columns, you set DataGridTextColumn.CellTemplate to LoadDynamicallyXaml("yourresource"). When you generate columns, you have access to your DataGrid Resources, and you can obtain the CellTemplate from there too, that's cleaner way.
Loading XAML at runtime?
You can define a Style for a TextBlock that contains your DataTriggers and then just apply that Style to a TextBlock in your dynamically created columns... this method should be good for up to around 8 different value/colour pairs before the code gets too long:
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding}" Value="111">
<Setter Property="TextElement.Foreground" Value="LightGreen" />
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="222">
<Setter Property="TextElement.Foreground" Value="LightBlue" />
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="333">
<Setter Property="TextElement.Foreground" Value="LightPink" />
</DataTrigger>
</Style.Triggers>
</Style>
The other method that you can use is to use a Converter class with your Binding. You can find a detailed example of doing this in many online posts... here are a few:
How to set Foreground of DataGrid Column with ValueConverter
Datagrid AutoGenerateColumns="True" forecolor IValueConverter
I am trying to figure out the best way to approach this. I have a treeview that is using Hierarchal data from sql server tables. Using linq to generate the dbml and then binding the data to the treeview. Here is the part I am having trouble with. Say the treeview starts and goes Categories----->Authors----->Books------>CheckedOut
So If I wanted to color each item(red) and all the parent nodes(red) where the item is overdue based on a view I created in sql server (I have four different categories to highlight based on dates stored in the db) what would be the best approach for this in C# WPF?
you can use a style trigger to trigger action when item in your tree view item meets certain condition
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Highlight}" Value="True">
<Setter Property="Background" Value="youcolor" />
</DataTrigger>
</Style.Triggers>
Firstly let me clarify (if the title wasn't clear enough): I'm discussing System.Windows.Controls.DataGrid from the .NET 4.0 Framework, not the toolkit edition.
I have a data grid which i read in from a csv which can change with every load.
I am trying to create a simple search, I have been trying to use;
http://www.codeproject.com/KB/WPF/FirefoxLikeSearchWithMVVM.aspx
http://manfred-ramoser.blogspot.com/2009/11/search-highlighting-in-wpf-datagrid.html
and others, but there all seem very complicated for a simple task, can anyone help, just to be clear a filtering system is not want i am after, thank you.
For my project i did a search function this way :
i added a 'FilterResult' Boolean property to the ViewModel of my data. It is implementing NotifyPropertyChanged.
So i perform the filter on my list of object with simple foreach loop and i set/unset FilterResult
And in the 'Style' of the DataGridRow i put a Setter on Visibility depending upon this FilterResult
<Style TargetType="{x:Type DataGridRow}" x:Key="EventColorRowStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding FilterResult}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
This simpler and much faster than using the Filter of a CollectionView, but still i can point you on what msdn says about it : http://msdn.microsoft.com/en-us/library/ff407126.aspx
I have a requirement to show a list of items in a context menu. In addition to this, I need to show the frequently used items (configurable by user) on the top, followed by a separator, and then the standard list of all the items. I know, I can add all the items to context menu at runtime but I want to explore different options too. The question is - is it possible to:
Bind the standard list in xaml and then add the frequently used items at runtime.
OR
Bind the context menu to two separate list
OR
Any other better option
Please note that I need to maintain two separate lists due to some technical reasons.
I am not showing any existing code because this question may be considered as a generic question and may apply to any control.
The second option is doable using a CompositeCollection, however the binding capabilities are a bit dimished (cannot use DataContext, ElementName or RelativeSource) in the CollectionContainer.Collection-Binding.
This answer of mine on another question shows two ways in which you can bind. If you cannot make do with those restrictions you will have to create the composite collection in code-behind.
I would manage my menus in the ViewModels, not in the XAML. My ViewModel would be responsible for returning a collection that combines both the standard Menu collection, and the custom UserCollection.
Usually I separate the items with a null value, and use a DataTrigger to draw the template as a Separator if the item is null.
Something like this:
myMenu.AddRange(UserMenu);
myMenu.Add(null);
myMenu.AddRange(StandardMenu);
and the XAML...
<ContextMenu ItemsSource="{Binding MyMenu}">
<ContextMenu.Resources>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Template"
Value="{StaticResource MyMenuItemTemplate}" />
<Style.Triggers>
<DataTrigger Binding="{Binding }" Value="{x:Null}">
<Setter Property="Template"
Value="{StaticResource MySeparatorTemplate}" />
</DataTrigger>
</Style.Resources>
</Style>
</ContextMenu.Resources>
</ContextMenu>
I have a very simple object called CellData. Is defined as:
public sealed class CellData
{
internal string DisplayText
{
get;
set;
}
public string Color
{
get;
set;
}
public override string ToString()
{
return this.DisplayText;
}
}
I can get it to display using the WPF toolkit DataGrid just fine. However, I want to be able to change the background color of each cell based on what data is in the cell. I'm having trouble understanding what type of binding I need to do because I can't see to get to the CellData object in my DataTrigger. I have tried the following, and several other variations but I can't get it to work:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(CellData).Color, Mode=OneWay}" Value="1">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
I am pretty new to XAML databidinding so any suggestions would be greatly appreciated. Thank you.
I'm guessing you have a RowData object that contains several CellData objects, that you have bound the ItemsSource of the DataGrid to a list of RowData objects, and that you are using DataGridTextColumns or other DataGridBoundColumns bound to the properties on RowData, maybe just by using AutoGenerateColumns="True".
The problem is that the DataContext for the cell is actually the RowData, not the CellData. The Binding is only used for the Text property of the TextBlock and TextBox. This is useful if you want to have the triggers based on other properties of the RowData object, but makes it difficult in scenarios like yours where you have a rich data structure for the cell data.
If you are creating the columns explicitly, you can just use the property for the column again in the trigger:
<DataGridTextColumn Binding="{Binding Foo}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<!-- DataContext is the row, so start
binding path with Foo property -->
<DataTrigger Binding="{Binding Foo.Color}" Value="1">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
Unfortunately, this won't let you share the style between columns since it is specific to the column name. If you want to do that, you may need to create a custom DataGridColumn subclass that has a property of type Binding that it applies to the DataContext property of the object returned by GenerateElement and GenerateEditingElement.
(Using a Binding with RelativeSource of Self as you did in your sample gives you the element in the visual tree rather than its DataContext, which won't help you get to the CellData object.)
You can use a ValueConverter: create a binding between the cell's background color and the Color property of your object, then add a ValueConverter to ensure that your data is properly converter to the object needed to set the background.