WPF Edit Templated Column by Code - c#

I'm trying to edit the contents of an StackPanel inside a templated DataGrid Column by Code. Unfortunately i cannot find the StackPanel from Code. Can anybody help me, please?
This is my DataTemplate:
<UserControl.Resources>
<DataTemplate x:Key="ReservationContainerTemplate">
<StackPanel Orientation="Horizontal" Background="Black" />
</DataTemplate>
</UserControl.Resources>
This is how I create this Column:
var colReservations = new DataGridTemplateColumn();
colReservations.Header = "Nordplatz";
DataTemplate dt = null;
dt = dataGrid1.FindResource("ReservationContainerTemplate") as DataTemplate;
colReservations.CellTemplate = dt;
dataGrid1.Columns.Add(colReservations);
What I need to do is, writing to this StackPanel inside DataTemplate.

I was able to get the Object by following Code:
DataRowView n = (DataRowView)dataGrid1.Items[i];
//var m = dataGrid1.SelectedItem.Cells[0].Text;
DataTemplate Template = dataGrid1.FindResource("ReservationContainerTemplate") as DataTemplate;
StackPanel stp = Template.LoadContent() as StackPanel;
Great, I've got my Object, but how can I modify it? Under this conditions i've only got a copy of the Object, changes are not reflected to the original one.
Does anybody have got an idea?

Related

Binding a collectionviewsource in datagrid celltemplate

I've got a thorny problem and I'm hoping you can help. I'm creating a datagrid which involves dynamically creating columns. Here's some pseudocode for my classes:
GameLibrary
ObservableCollection<Game> Games
Game
ObservableCollection<CustomField> CustomFields
Customfield
ObservableCollection<string> Values
The datagrid is bound to a CollectionViewSource that uses GameLibrary.Games as its Source. The datagrid displays the other properties from Game in each row as I've set up the columns, and then I've got it dynamically created a column for each CustomField in CustomFields and display the relevant CustomField's Values in an itemscontrol in the cell.
This all works great, no problem. Now, though, I'm wanting to sort the Values alphabetically to display. I know best practice for this is using a CollectionViewSource, and I have managed to get one set up, attached to the DataTemplate and displaying in the itemscontrol - but it only works if, as a test, I set the CVS's source to be something external to the datagrid. This displays, but of course it displays the same thing in every row.
How do I bind the DataTemplate's CVS to something in the current row of the table? It's easy enough when not using the CVS, because I can use the binding's Path and just say "CustomFields[i].Values", but I don't know how that translates across to the CVS Source.
Here's what I have now, which works great:
FrameworkElementFactory listbox = new FrameworkElementFactory(typeof(ItemsControl));
Binding b = new Binding();
string pathb = "CustomFields[" + i + "].Values";
b.Path = new PropertyPath(pathb);
b.Mode = BindingMode.TwoWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
listbox.SetBinding(ItemsControl.ItemsSourceProperty, b);
listbox.SetValue(ItemsControl.PaddingProperty, new Thickness(5));
dock.AppendChild(listbox);
DataTemplate dt = new DataTemplate { VisualTree = dock };
dt.Seal();
newcolumn.CellTemplate = dt;
gameDataDisplay.Columns.Add(newcolumn);
And here's what I want:
DataTemplate dt = new DataTemplate { VisualTree = dock };
CollectionViewSource listboxCVS = new CollectionViewSource();
SortDescription listboxsortDescription = new SortDescription(".", ListSortDirection.Ascending);
listboxCVS.SortDescriptions.Add(listboxsortDescription);
listboxCVS.Source = SOMETHING HERE BUT I DONT KNOW WHAT;
dt.Resources.Add("customCVS" + i, listboxCVS);
FrameworkElementFactory listbox = new FrameworkElementFactory(typeof(ItemsControl));
Binding b = new Binding();
b.Source = listboxCVS;
listbox.SetBinding(ItemsControl.ItemsSourceProperty, b);
listbox.SetValue(ItemsControl.PaddingProperty, new Thickness(5));
dock.AppendChild(listbox);
dt.Seal();
newcolumn.CellTemplate = dt;
gameDataDisplay.Columns.Add(newcolumn);
I've also tried instead of using a CVS binding to a property in CustomFields that returns a sorted list of the Values and that displays fine, but I know it's not best practice and it doesn't update until you scroll the item offscreen and back, so I think that's a dead end.
Thank you for any help you can offer,
Tom.
PS: The ObservableCollections here aren't strictly ObservableCollections, they're a derived class with a couple extra methods, but they act exactly the same for all practical purposes. Just mentioning here for completeness.
I solved this a different way. Rather than creating a new datatemplate programmatically for each custom column, I defined a datatemplate in the window's resources then used a ContentPresenter to fill in the bindings.
XAML:
<DataTemplate x:Key="customfieldtemplate">
<DockPanel x:Name="customfieldHolder" VerticalAlignment="Center">
<DockPanel.Resources>
<CollectionViewSource x:Key="customfieldview" x:Name="customfieldview" Source="{Binding Values}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="."/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<CollectionViewSource x:Key="customfieldsview" x:Name="customfieldsview" Source="{Binding PossibleValues}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="."/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</DockPanel.Resources>
<customcontrols:MultiComboBox Width="17" DockPanel.Dock="Right" Margin="5,0" ShowText="False" x:Name="customfieldMiniCombo" ItemsSource="{Binding Source={StaticResource customfieldsview}}" SelectedItems="{Binding Values}" SelectionMode="Multiple" BorderBrush="{DynamicResource MahApps.Brushes.TextBox.Border}" Background="{DynamicResource MahApps.Brushes.ThemeBackground}" Foreground="{DynamicResource MahApps.Brushes.Text}"/>
<ItemsControl ItemsSource="{Binding Source={StaticResource customfieldview}}" Padding="5"/>
</DockPanel>
</DataTemplate>
Codebehind:
for (int i = 0; i < maindatafile.CurrentGameLibrary.CustomFields.Count; i++)
{
CustomColumn newcolumn = new CustomColumn();
newcolumn.Header = maindatafile.CurrentGameLibrary.CustomFields[i].Name;
gameDataDisplay.Columns.Add(newcolumn);
var template = FindResource("customfieldtemplate");
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(ContentPresenter));
factory.SetValue(ContentPresenter.ContentTemplateProperty, template);
Binding newBinding = new Binding("CustomFields[" + i + "]");
newBinding.Mode = BindingMode.TwoWay;
newBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
factory.SetBinding(ContentPresenter.ContentProperty, newBinding);
DataTemplate dt = new DataTemplate { VisualTree = factory };
newcolumn.CellTemplate = dt;
}
The multicombobox user control (found here) in the template contains an "allowed" set of values to add to or remove from the observablecollection. Its ItemsSource is another property in CustomField, whose getter returns the list of allowable values for that CustomField. The multicombobox's SelectedItems property uses the same binding as the ItemsControl.
End result? The cell displays the list of values in the appropriate CustomField, with a lil dropdown button next to it. That dropdown contains all the possible values for that field, with the ones currently in the list selected. The list updates live as you select and deselect values in this combobox, and also updates live when those values are changed in other parts of the program.

UWP GridView in C# with DataTemplate

I have a Grid where I want to put a GridView in every cell of it from the code-behind in the .xaml.cs. The problem is that I do not know how to create a GridView with a DataTemplate to bind stuff in it in C#
DataTemplate Template= new DataTemplate();
GridView gridView = new GridView();
gridView.ItemsSource = book;
Thats all I have. Help is appreciated
J.
I want to put a GridView in every cell of it from the code-behind in the .xaml.cs.
You could make a DataTemplate in App.xaml file like follow.
<Application.Resources>
<ResourceDictionary>
<DataTemplate x:Key="template">
<StackPanel>
<TextBlock Text="{Binding }"/>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
And use it in the code behind.
private void SetUpUI()
{
var gridView = new GridView();
//DataTemplate usage.
gridView.ItemTemplate = (DataTemplate)App.Current.Resources["template"];
gridView.ItemsSource = new List<string> { "fas", "fasd", "fasf" };
RootLayout.Children.Add(gridView);
}

Custom column template for DataGrid

I have a DataGrid bound to a DataTable.DefaultView, which renders the grid using auto generation of columns. That part works fine. However, for some columns I would like to use a custom template. The problem is that columns in the table change on each load, so the solution needs to be generic.
I can hook into the AutoGeneratingColumn event as described here, but still have problem with defining the template binding:
<UserControl.Resources>
<DataTemplate x:Key="customCellTemplate">
<TextBlock Text="{Binding ???"></TextBlock>
</DataTemplate>
</UserControl.Resources>
(...)
<DataGrid ItemsSource="{Binding DefaultView}" AutoGeneratingColumn="DataGrid_AutoGeneratingColumn">
</DataGrid>
And my code behind:
private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
string colName = e.PropertyName;
if (someCondition)
{
var templateColumn = new DataGridTemplateColumn();
templateColumn.Header = colName;
templateColumn.CellTemplate = (DataTemplate)Resources["customCellTemplate"];
templateColumn.SortMemberPath = colName;
e.Column = templateColumn;
}
As you can see I don't know how to define the binding in the column template, because the column name changes.
EDIT:
In addition to the accepted answer - sometimes it's easier to create the entire template programmatically as described here:
http://fczaja.blogspot.com/2013/12/wpf-datagrid-custom-template-for.html
Using a StaticResource forces you to keep it the same -- remember, static means there's just one instance, so if you change its binding for one column, you will change it for all of them. So it will have to be like this:
<DataTemplate x:Key="customCellTemplate">
<TextBlock Text="{Binding}"></TextBlock>
</DataTemplate>
I was thinking you could use this template in a dynamic way by wrapping it in another DataTemplate using a ContentControl. Set the Content property dynamically, and use the static template for the ContentTemplate:
private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
string colName = e.PropertyName;
if (someCondition)
{
string xaml = #"<DataTemplate xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""><ContentControl Content=""{0}"" ContentTemplate=""{1}"" /></DataTemplate>";
var tmpl = (DataTemplate)XamlReader.Load(string.Format(xaml, "{Binding " + colName + "}", "{StaticResource customCellTemplate}"));
var templateColumn = new DataGridTemplateColumn();
templateColumn.CellTemplate = tmpl;
templateColumn.Header = colName;
templateColumn.SortMemberPath = colName;
e.Column = templateColumn;
}
}
The only catch is that, with this setup, I believe "customCellTemplate" will have to be defined at the application level.

Changing from xaml design into C# coding

I am currently try to programatically get the ListBox
I tried to find many ways but, I can't make this works.
Here is the xaml part of code:
<ListBox Grid.Row="2" Grid.ColumnSpan="2" x:Name="PeerList" Margin="10,10,0,10">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" FontSize="{StaticResource PhoneFontSizeMedium}" Margin="40,0,0,0"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I want this same operation to be done programatically.
Someone familiar to XAML to C# help me to solve this. .
It is something like this
ListBox listbox = new ListBox();
DataTemplate dataTemplate = new DataTemplate();
FrameworkElementFactory elementFactory = new FrameworkElementFactory(typeof(TextBlock));
elementFactory .SetBinding(TextBlock.TextProperty, new Binding("DisplayName"));
dataTemplate.VisualTree = elementFactory;
listbox.ItemTemplate = dataTemplate ;
If you want Programatically display the name of the peers in this list means follow #Hiệp Lê Answer.
Otherwise if you want only to get the name of the peers. Just follow this.
void SearchPeers()
{
List<string> name = new List<string>();
var peers = await PeerFinder.FindAllPeersAsync();
for(int i=0;i<peers.Count;i++)
{
string peerName = peers.DisplayName;
name.Add(peerName);
}
}
This will get you the name of the peers available.

nested xaml elements - rewrite it in code

I need to create a DataGridColumn from code.
The XAML equivalent would be:
<data:DataGridTemplateColumn Header="Name" Width="100">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" TextTrimming="WordEllipsis"></TextBlock>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
I've started like that:
DataGridTemplateColumn column = new DataGridTemplateColumn
{
Header = "Name",
Width = 100,
};
TextBlock inside = new TextBlock {TextTrimming = TextTrimming.CharacterEllipsis};
But I don't know how to 'merge' such puzzles. There are nested elements in XAML, how to achieve this from code?
A good way to do this is to pack the entire XAML snippet into a string and call XamlReader.Load() or XamlReader.Parse() on it. The bonus feature of this approach is that it'll work in Silverlight as well (with some fiddling), where you can't build DataTemplates in code.
Almost there, change your code to this and it should work:
DataGridTemplateColumn column = new DataGridTemplateColumn
{
Header = "Name",
Width = 100,
};
FrameworkElementFactory ftb = new FrameworkElementFactory(typeof(TextBlock));
Binding b = new Binding("Name");
ftb.SetValue(TextBlock.Text, b);
ftb.SetValue(TextBlock.TextTrimming, TextTrimming.CharacterEllipsis);
DataTemplate ct = new DataTemplate();
ct.VisualTree = ftb;
column.CellTemplate = ct;
Another method besides the above is to define your datatemplate in XAML within your resources then dynamically load it in the code:
XAML:
<Window.Resources>
<DataTemplate x:Key="myCellTemplate">
<TextBlock Text="{Binding Name}" TextTrimming="WordEllipsis" />
</DataTemplate>
</Window.Resources>
Code:
DataGridTemplateColumn column = new DataGridTemplateColumn
{
Header = "Name",
Width = 100,
};
column.CellTemplate = this.FindResource("myCellTemplate") as DataTemplate;

Categories

Resources