How to set a specific Datacontext for a DataTemplate in CellTemplate - wpf

Currently i bind to a List<T> so i have to do specific set foreach Column a separate DataTemplate
like this:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock TextAlignment="Center"
Text="{Binding ObColl[1].Std, UpdateSourceTrigger=PropertyChanged}"
Background="{Binding ObColl[1].DienstColor, TargetNullValue=Transparent,FallbackValue=Transparent}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
but i want is to create the DataTemplate one time as Resources
<DataGrid.Resources>
<DataTemplate x:Name="MyCellTemplate">
<TextBlock TextAlignment="Center"
Text="{Binding Std, UpdateSourceTrigger=PropertyChanged}"
Background="{Binding DienstColor, TargetNullValue=Transparent,FallbackValue=Transparent}" />
</DataTemplate>
</DataGrid.Resources>
and use it like
<DataGridTemplateColumn CellTemplate="{StaticResource MyCellTemplate} ??{Binding ObColl[1]}??"/>
But to do so i need to specific the DataContext (ObColl[Idx]) in my DataGridTemplateColumn
but how do i do this?
EDIT
the xaml should look like :
<DataGrid Name="dataGrid1"
ItemsSource="{Binding Itemlist, UpdateSourceTrigger=PropertyChanged}">
<DataGrid.Resources>
<DataTemplate x:Key="MyCellTemplate">
<TextBlock TextAlignment="Center"
Text="{Binding Std, UpdateSourceTrigger=PropertyChanged}"
Background="{Binding DienstColor, TargetNullValue=Transparent, FallbackValue=Transparent}" />
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<!-- Column 1 -->
<DataGridTemplateColumn CellTemplate="{StaticResource MyCellTemplate}"
DataContext={Binding ObColl[0]}/>
<!-- Column Header 2 -->
<DataGridTemplateColumn CellTemplate="{StaticResource MyCellTemplate}"
DataContext={Binding ObColl[1]}/>
</DataGrid.Columns>
</DataGrid>
the DataContext={Binding ObColl[1]} is the problem part because it doesn't exist ....

Ok, here is my understanding of you requirement... you have a MyRow class with two properties; MyRowheader and MyCellList. You want to display the MyRowheader value and one value from the MyCellList collection on each row of your DataGrid. This is how I would do that:
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding YourCollection}">
<DataGrid.Columns>
<DataGridTextColumn Header="Header" Binding="{Binding MyRowheader,
UpdateSourceTrigger=PropertyChanged}" />
<DataGridTemplateColumn Header="Cell list">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding MyCellList[1].Std, UpdateSourceTrigger=
PropertyChanged}" Background="{Binding MyCellListl[1].DienstColor, TargetNullValue=
Transparent, FallbackValue=Transparent}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Please let me know if I have misunderstood your requirement.
UPDATE >>>
So I did misunderstand your requirement. It seem as though you want one value from your MyCellList collection in each column, not row, of the DataGrid. In that case, my answer would be no, you can't setup your DataGrid.Columns using a DataTemplate or any other XAML saving feature. XAML is a verbose language... there are a few ways of writing it more efficiently, but not many. You will often find repeated code on XAML pages.
The only way that I can think of that you could write less code would be if you dynamically generated the columns from code. You can find a basic example of that in the Dynamically add Columns to DataGrid in wpf post. I don't know how much time that will save you though really.

Related

ListView split row cells in wpf

※ Add Info
I forgot to tell one thing is each list item have to get their own color (refer image)
Click Here!
I already tried to using stackpanel, but it coudln't display the color for each items.
I'm trying to find the way split the specific row in wpf.
Image
The following attachment is the format what i want to make by wpf.
To merge Row is not what I want.(becacuse I want to split the row data in one instance)
The data(1~3-3) is member property of each Instance.
Does anybody can solve this problem?
I'll look forward to you guys answer.
thank you
You can use a DataGrid instead like this:
<DataGrid Name="dg" Width="400" Height="300" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Id}"/>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTemplateColumn Header="Addresses">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Address1}" />
<TextBlock Text="{Binding Address2}"/>
<TextBlock Text="{Binding Address3}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Please change the bindings of the columns with your data context properties.

get the selected row of datagrid i.e on click in mvvm

i want to get the selected row of datagrid i.e on click in mvvm.i have below code i want to use it in MVVM,but not able to do so.Please let me know how to convert below 3 line s in mvvm using relay comand and Icommand
DataRowView dataRow = (DataRowView)dgProjectComponents.SelectedItem;
int index = dgProjectComponents.CurrentCell.Column.DisplayIndex;
string ProjectComponentID = Convert.ToString(dataRow.Row.ItemArray[2].ToString());
**Xaml of datagrid:**
<DataGrid SelectedItem="{Binding SelectedRow}" Background="{Binding ElementName=gd,Path=Background}" ItemsSource="{Binding ManualDataTable}" x:Name="dgProjectComponents">
<DataGrid.Columns>
<DataGridTemplateColumn Width="50" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate >
<TextBlock Height="10" Width="10" Background="{Binding ColorDefinition}" ></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding ProjectComponentID}" Visibility="Hidden" Width="100" Header="ProjectComponentID" />
<DataGridTextColumn Binding="{Binding Title}" IsReadOnly="True" Width="140" />
<DataGridTemplateColumn Width="50">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock><Hyperlink Command="{Binding Path=DataContext.DelProjectComponent,ElementName=ProjectTabWindow}"><Image Source="/img/Close.png" x:Name="imgProjectComponentDelete" Height="15" Width="20"></Image></Hyperlink></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataGrid>
You have data bound your ManualDataTable collection to the ItemsSource property of your DataGrid and your SelectedRow property to the SelectedItem property. If your SelectedRow property is of the same type as the items in your ManualDataTable collection, then you can use it to reference the selected item from the DataGrid.
Therefore, you have no need to find a certain column from a DataGridRow, because you can access the properties of your class as normal... so instead of your three lines of code, you can simply do this (assuming that you have an Id property in your class):
string ProjectComponentID = SelectedRow.Id;

WPF DataGrid Not Sorting

I am trying to get a WPF DataGrid to sort by a column by default and it isn't working. there is no sorting happening. The sort icons appear and if i click them then sorting happens.
Right now my table has just the one column. but is it still not sorting it by default.
Any ideas what i am missing?
<DataGrid.Columns>
<!--Ordinal-->
<DataGridTemplateColumn d:DataContext="{d:DesignInstance tabViewModels:ColumnViewModel}"
SortMemberPath="Ordinal"
>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<Label Content="#" ToolTip="Column Position" />
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<xctk:DoubleUpDown Value="{Binding Ordinal, UpdateSourceTrigger=PropertyChanged, TargetNullValue=''}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Ordinal}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
One solution would be to use a LINQ query to give the data already sorted to the DataGrid. Modify the getter of the Item Source with something like this:
List<Something> MyItemSource
{
get
{
return _myItemSource.OrderBy(x => x.Ordinal).ToList();
}
}
then it will for sure sort by Ordinal.

WPF DataGrid RowDetailsTemplate with Multiple Images (MVVM)

Goal
To add multiple images in a DataGrid's RowDetails template using the MVVM standards.
Background
I have an inspection window with a DataGrid designed to hold a damaged item's description along with the initials of the inspector. What is more, this DataGrid's RowDetailsTemplate needs to hold the pictures that the inspector took of the damaged item (so there might be more than one picture of the damaged item).
Problem
I have a DamagedWindow.xaml designed to create my DamagedItem entries. It looks like this:
DataGrid (.XAML)
<DataGrid ItemsSource="{Binding Pictures}" SelectedItem="{Binding SelectedPicture}" AutoGenerateColumns="False" Grid.Column="1" Grid.Row="2" Margin="5" HorizontalAlignment="Stretch" Name="DataGrid1" VerticalAlignment="Stretch" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Titre" Binding="{Binding Name}" Width="*" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<Image Height="100" Source="{Binding Path}" />
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
As you can see, my RowDetails template works fine in this DataGrid. I have a class named PicturesList which inherits an ObservableCollection Of my PictureModel(Name, Description, Path).
The text boxes that you see above the DataGrid are properties of my DamagedItemModel (Description, Initiales, PicturesList). So when the user clicks on the Accept (Checkmark) button, the DamagedItem is added to a DamagedItemsList which is then set as the ItemSource of the DataGrid from my MainWindow:
DataGrid (.XAML)
<DataGrid ItemsSource="{Binding Path=DamagedItems}" SelectedItem="{Binding Path=SelectedDamagedItem}" AutoGenerateColumns="False" Name="DataGrid1" Height="250" Margin="3" IsEnabled="True" CanUserAddRows="False" FontSize="16">
<DataGrid.Columns>
<DataGridTextColumn Header="Description" Width="*" Binding="{Binding Description}"/>
<DataGridTextColumn Header="Initiales" Width="70" Binding="{Binding Initiales}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<Image Height="100" Source="{Binding Pictures.Path}" />
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
Here when I select the row, I get an empty RowDetailsTemplate result ... Even though my object contains my images. See below for more details:
So here's my question, is it possible to add multiple images to a RowDetailsTemplate in a DataGrid while following MVVM standards? If it is, what am I doing wrong?
You can't bind that way. you need to do it like this:
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Pictures}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Height="100" Source="{Binding Path}"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
The reason your method was not working is that the Binding source is always one object which is set by Binding Path, and your Binding Path was Pictures.Path which leads to nothing because Pictures object does not have Path, it's its Items which have Path.
In general, Whenever you find yourself dealing with a collection of some kind think of a control which is suitable for showing a collection, like ListBox, DataGrid or the best of all ItemsControl.
Anything that goes inside ItemTemplate of these controls, have their DataContext automatically set to the correspondent item, so you don't have to worry about it. All you have to do is to set the ItemsSource to your collection and set the Binding Paths of things inside to the properties of the Type of that collection, So that it knows where to look for data for each item.
In this code you can think of it this way, Like you have some StackPanels, first one has : Image Source="{Binding Pictures(0).Path}", seconds one has Image Source="{Binding Pictures(1).Path}" and so on. this way all Binding Paths point to an object.

Accessing control between DataGridCells, dynamic cascading ComboBoxes

I have a DataGrid that two of its columns are ComboBoxes (one contains few but not this is the problem).
I want, that when the user changes the first Combo's value, the ComboBox in the other column should bind to a property of its (this property is a collection). Say the First ComboBox is Category, I want that when the user changes its value, the other CB is populated with the values of (first combo's selected category).Vendors.
How should I do it, I don't use MVVM, just simple WPF.
I don't know what should be the right way to implement it, I hope I started it right.
I think, if I could get the other ComboBox (which is located in a different DataGridCell) from the first's SelectionChangeHandler that would be the best, because then I can reset its source on each selection change of the first one.
Note that I have the capability of reaching the current (the first's) DataGridCell, I am just looking for an efficient way to access the right DataGridCell sibling and then get its child (second) combo.
Also note that the selected category should vary from row to row, and the second ComboBox should depend on this row's category.
I actually tried to implement it so that the CollectionViewSource.Source is bound to the current item (i.e. the row's DataContext) but it doesn't seem to work.
I prefer to set the second combo's CollectionViewSource (VendorsCollection) thru an Action trigger or handler at the 1st ComboBox's SelectionChange.
The other ComboBoxes in that field don't seem to make a problem as they're all bound to each other, I might use CollectionViewSource.Filter, anyway it's not a problem to access them as they are simple siblings, not like the first one which is a distant cousin located deep in another DataGridCell.
Here is what is what I tried so far:
<DataGrid>
<DataGrid.Resources>
<CollectionViewSource x:Key="CategoriesCollection" Source="{Binding Context.CategoriesList, Source={x:Static Application.Current}, IsAsync=True}" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Category">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding Category}" Text="{Binding Title}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<!--This is the first ComboBox-->
<ComboBox
IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Source={StaticResource CategoriesCollection}}"
DisplayMemberPath="Title"
SelectionChanged="cbCategories_SelectionChanged"
SelectedItem="{Binding Category}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Style">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBlock DataContext="{Binding Finish.Style.Vendor}" Text="{Binding Contact.Title}"/>
<TextBlock DataContext="{Binding Finish.Style}" Text="{Binding Title}"/>
<TextBlock Text="{Binding Finish.Title}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel>
<StackPanel.Resources>
<!--I want, that when the user selects a value in the first ComboBox,
the VendorsCollection below should be populated with the selected Category.Vendors,
or alternatively current row's data item.Category.Vendors,
I just donno how to access current row from these resources.-->
<CollectionViewSource x:Key="VendorsCollection" Source="{Binding Vendors, Source={StaticResource CategoriesCollection}}" />
<CollectionViewSource x:Key="StylesCollection" Source="{Binding Styles, Source={StaticResource VendorsCollection}}" />
<CollectionViewSource x:Key="FinishesCollection" Source="{Binding Finishes, Source={StaticResource StylesCollection}}" />
</StackPanel.Resources>
<ComboBox
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource VendorsCollection}}"
SelectedItem="{Binding Finish.Style.Vendor}"
DisplayMemberPath="Contact.Title"/>
<ComboBox
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource StylesCollection}}"
SelectedItem="{Binding Finish.Style}"
DisplayMemberPath="Title"/>
<ComboBox
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource FinishesCollection}}"
SelectedItem="{Binding Finish}"
DisplayMemberPath="Title"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I just came across your questions. Did you get your problem resolved? I think your question is similar to this one I got. Hope the solution there helps you too.

Resources