RadGridView Bind Column Width to Another Column? - wpf

I have a RadGridView (Telerik) and it has a handful of columns. In the row details template I have another grid view displaying sub-items that have the same columns. This works great for the initial display, but I would like the column widths for the details template to follow the widths of the main grid (details template does not have headers).
I tried giving the main column a name and binding to 'Width' and 'ActualWidth' properties of that column by name but it didn't seem to take and didn't give any binding errors.
Is there any way to bind the width of a column on one RadGridView to the width of a column on another RadGridView?
EDIT
Per the suggestion below I tried binding the view to the tag and going that way and it doesn't seem to work. It works for the textblock but doesn't set the column width. Here is a video of what I see:
https://www.screencast.com/t/BiHmiarQExV
Here is the code I'm using:
<telerik:RadGridView Grid.Row="2" ItemsSource="{Binding RenumberNotes}" x:Name="tgr" AutoGenerateColumns="False"
RowDetailsVisibilityMode="Visible">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn UniqueName="columnName" DataMemberBinding="{Binding CurrentValue}" Width="100" />
</telerik:RadGridView.Columns>
<telerik:RadGridView.RowStyle>
<Style TargetType="telerik:GridViewRow" BasedOn="{StaticResource {x:Type telerik:GridViewRow}}">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=telerik:RadGridView}}" />
</Style>
</telerik:RadGridView.RowStyle>
<telerik:RadGridView.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="10,10,10,10">
<TextBlock Text="Column width: " />
<TextBlock
Text="{Binding Tag.Columns[columnName].ActualWidth, RelativeSource={RelativeSource AncestorType=telerik:GridViewRow}}" />
</StackPanel>
<telerik:RadGridView AutoGenerateColumns="False" ItemsSource="{Binding Subnotes}">
<telerik:RadGridView.Columns>
<telerik:GridViewColumn Header="test" Width="{Binding Tag.Columns[columnName].ActualWidth, RelativeSource={RelativeSource AncestorType=telerik:GridViewRow}}" />
</telerik:RadGridView.Columns>
</telerik:RadGridView>
</StackPanel>
</DataTemplate>
</telerik:RadGridView.RowDetailsTemplate>
</telerik:RadGridView>
As you can see, it updates the textblock properly so the binding is correct, but the column width is not updated.

Is there any way to bind the width of a column on one RadGridView to the width of a column on another RadGridView?
Yes. You could define a RowStyle that binds the Tag property of the GridViewRow to the parent RadGridView and then use this one to bind to the Columns collection of the grid. Here is an example for you:
<telerik:RadGridView x:Name="tgr" AutoGenerateColumns="False" RowDetailsVisibilityMode="VisibleWhenSelected">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn UniqueName="columnName" DataMemberBinding="{Binding Name}" Width="100" />
</telerik:RadGridView.Columns>
<telerik:RadGridView.RowStyle>
<Style TargetType="telerik:GridViewRow" BasedOn="{StaticResource {x:Type telerik:GridViewRow}}">
<Setter Property="Tag" Value="{Binding RelativeSource={RelativeSource AncestorType=telerik:RadGridView}}" />
</Style>
</telerik:RadGridView.RowStyle>
<telerik:RadGridView.RowDetailsTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="10,10,10,10">
<TextBlock Text="Column width: " />
<TextBlock Text="{Binding Tag.Columns[columnName].ActualWidth, RelativeSource={RelativeSource AncestorType=telerik:GridViewRow}}" />
</StackPanel>
</DataTemplate>
</telerik:RadGridView.RowDetailsTemplate>
</telerik:RadGridView>
Edit:
It works for a Textblock as you have shown, but binding the column width the same way doesn't seem to do anything.
Right, that's because the column itself is not part of the visual tree. You will have to write some code then. You could simply handle the Loaded event of the inner RadGridView:
private void RadGridView_Loaded(object sender, RoutedEventArgs e)
{
RadGridView subGridView = (RadGridView)sender;
subGridView.Columns[0].Width = columnName.Width;
}
XAML:
<telerik:RadGridView Grid.Row="2" ItemsSource="{Binding RenumberNotes}" x:Name="tgr" AutoGenerateColumns="False"
RowDetailsVisibilityMode="Visible">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn x:Name="columnName" DataMemberBinding="{Binding CurrentValue}" Width="100" />
</telerik:RadGridView.Columns>
<telerik:RadGridView.RowDetailsTemplate>
<DataTemplate>
<telerik:RadGridView AutoGenerateColumns="False" ItemsSource="{Binding Subnotes}"
Loaded="RadGridView_Loaded">
<telerik:RadGridView.Columns>
<telerik:GridViewColumn Header="test" />
</telerik:RadGridView.Columns>
</telerik:RadGridView>
</DataTemplate>
</telerik:RadGridView.RowDetailsTemplate>
</telerik:RadGridView>
This is cleaner and doesn't break any pattern.

Related

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;

How to set a specific Datacontext for a DataTemplate in CellTemplate

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.

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.

WPF/Xaml Datagrid wont fire ValidatesOnDataErros when binding is in DataGridTextColumn

I have a datagrid that is bound to an observable collection from my viewmodel. This all works fine and displays my data in the datagrid.
What i need to do now is validate some columns when the user chnages the text. I am using the IDataErrorInfo to do this.
If i do the following:-
**<TextBox
Width="100"
Text="{Binding Path=CallCode,
Mode=TwoWay,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }"/>**
This works and triggers the validation code in my viewmodel, however if i add this code to the datagrid as below it doesnt do anything!:-
<Border x:Name="body"
DockPanel.Dock="Top"
Grid.Row="2"
Grid.Column="0">
<!-- Results -->
<DataGrid x:Name="Results"
ItemsSource="{Binding CallCodesList}"
AutoGenerateColumns="False"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
CanUserAddRows="False">
<DataGrid.Columns >
**<DataGridTextColumn
Header="Call Code"
CanUserSort="True"
Width="100"
Binding="{Binding CallCode,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged />**
<DataGridCheckBoxColumn Width="70"
Binding="{Binding Path=HasSpeech}"
Header="Speech"
IsThreeState="True">
<DataGridCheckBoxColumn.ElementStyle>
<Style TargetType="CheckBox">
<Setter Property="IsChecked" Value="{Binding HasSpeech}" />
</Style>
</DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>
</DataGrid>
</Border>
Is there someting i am missing or can it not see something because it is in a datagrid, this is all new so currently stuck :(
Any help with this would be great.
It seams that the code is fine, i don't know if the wpftoolkit's data grid allow IDataErrorInfo validation, but in this article (Validation in WPF Toolkit’s DataGrid) you can see a good example of using validation on wpftoolkit's data grid, but using the IDataError interface way.
I hope this help to you...

Silverlight Template databinding issue

I am using the Telerik framework for creating a RadGridView with child elements in Silverlight 4.
<telerikGrid:RadGridView x:Name="itemsGrid" Grid.Row="1" AutoGenerateColumns="{Binding AutoGenerateColumn, Mode=TwoWay}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" ItemsSource="{Binding Items, Mode=TwoWay}" CanUserInsertRows="True" ShowInsertRow="True" CanUserDeleteRows="True">
<telerikGrid:RadGridView.Columns>
<telerikGrid:GridViewToggleRowDetailsColumn />
<telerikGrid:GridViewDataColumn Header="Item 1" DataMemberBinding="{Binding Item1}" UniqueName="Item1" />
<telerikGrid:GridViewComboBoxColumn Header="Group" UniqueName="Group"
ItemsSource="{Binding Groups.Items}" SelectedValueMemberPath="GroupId" DisplayMemberPath="GroupNames" />
<telerikGrid:GridViewComboBoxColumn Header="Supplier" UniqueName="Supplier"
ItemsSource="{Binding Suppliers}" SelectedValueMemberPath="CompanyS" DisplayMemberPath="CompanyName" />
<telerikGrid:GridViewDataColumn Header="Not in use" DataMemberBinding="{Binding inUse}" UniqueName="inUse" />
</telerikGrid:RadGridView.Columns>
<telerikGrid:RadGridView.ChildTableDefinitions>
<telerikGrid:GridViewTableDefinition />
</telerikGrid:RadGridView.ChildTableDefinitions>
<telerikGrid:RadGridView.HierarchyChildTemplate>
<DataTemplate>
<StackPanel>
<telerikGrid:RadGridView x:Name="childGrild" Grid.Row="1" AutoGenerateColumns="False" ItemsSource="{Binding ChildList}" CanUserInsertRows="True" CanUserDeleteRows="True" IsReadOnly="False" ShowInsertRow="True" ShowGroupPanel="False">
<telerikGrid:RadGridView.Columns>
<telerikGrid:GridViewDataColumn Header="Part S" DataMemberBinding="{Binding PartS}" UniqueName="PartS" />
<telerikGrid:GridViewComboBoxColumn Header="Tools" ItemsSource="{Binding ElementName=control, Path=DataContext.Tools}" DataMemberBinding="{Binding PartS}" SelectedValueMemberPath="Id" DisplayMemberPath="Name" />
</telerikGrid:RadGridView.Columns>
</telerikGrid:RadGridView>
</StackPanel>
</DataTemplate>
</telerikGrid:RadGridView.HierarchyChildTemplate>
</telerikGrid:RadGridView>
As you can see, each row in the grid has a child GridView, the problem is however that i cant get the Combobx in the gridview to display any data, its always empty. If i move it outside the child tempate it works fine, and the Items which are in the Tools list is shown.
To access the list, which is a property of the controls datacontext, i gave the control a Name (control), and used Path=DataContex.Tools, which i read should work.
Anyone has any idea why the ComboBox is not populated with data? I have checked that all the propertie names are correct, and that there actually are data there to show.
RadGridView and ElementName don't mix very well. In your case, the 'control' element can't be found. You could use a DataContextProxy as shown in the link. It solved an issue I had that was similar.

Resources