I am very new to WPF, and help appreciated:
Model:
Collection<Presenter>,
Presenter has a Collection<Presentation>,
Presentation has a TeachingSession property (which contains a DateTime? property)
I am trying to have a treeview display:
presenter name
[combobox of available Dates]
At the moment, each presenter name in the treeview is displaying correctly, and the first parent item expanded displays the combobox with the correctly selected date. However, comboboxes displaying at any one time are all 'in sync' - that is changing the value in a combobox (or expanding a different treeview item) changes the value for all comboboxes which can be seen, so they all display the same date.
<TreeView Name="PresenterTreeView" ItemsSource="{Binding}">
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Presenter}"
ItemsSource="{Binding Path=Presentations}">
<TextBlock Text="{Binding Path=FullName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Presentation}">
<ComboBox SelectedItem="{Binding Path=TeachingSession, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="SessionDate"
ItemsSource="{Binding Source={StaticResource availableDatesViewSource}}" >
</ComboBox>
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
I have compliled your example and I cant get the comboboxes to sync like you are describing.
Perhaps you will be able to see what I done different and it might be the fix, Or maybe I am just wrong and am missing somthing from your question?
This is the code I used:
Xaml:
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="340" Width="480" Name="UI" xmlns:local="clr-namespace:WpfApplication8">
<TreeView Name="PresenterTreeView" DataContext="{Binding ElementName=UI}" ItemsSource="{Binding Path=Presenters}" >
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Presenter}" ItemsSource="{Binding Path=Presentations}">
<TextBlock Text="{Binding FullName}" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Presentation}">
<ComboBox SelectedItem="{Binding TeachingSession.SessionDate}" ItemsSource="{Binding ElementName=UI, Path=Dates}" />
</HierarchicalDataTemplate>
</TreeView.Resources>
</TreeView>
</Window>
Code:
public partial class MainWindow : Window
{
private ObservableCollection<Presenter> _myProperty = new ObservableCollection<Presenter>();
private ObservableCollection<DateTime?> _myDates = new ObservableCollection<DateTime?>();
public MainWindow()
{
InitializeComponent();
DateTime time1 = DateTime.Now;
DateTime time2 = DateTime.Now.AddDays(1);
Dates.Add(time1);
Dates.Add(time2);
for (int i = 0; i < 20; i++)
{
Dates.Add(DateTime.Now.AddDays(i));
}
TeachingSession teach = new TeachingSession { SessionDate = time1 };
Presentation pres = new Presentation { TeachingSession = teach };
Presenter presenter = new Presenter { FullName = "Presenter1" };
presenter.Presentations = new ObservableCollection<Presentation>();
presenter.Presentations.Add(pres);
TeachingSession teach1 = new TeachingSession { SessionDate = time2 };
Presentation pres1 = new Presentation { TeachingSession = teach1 };
Presenter presenter1 = new Presenter { FullName = "Presenter1" };
presenter1.Presentations = new ObservableCollection<Presentation>();
presenter1.Presentations.Add(pres1);
Presenters.Add(presenter);
Presenters.Add(presenter1);
}
public ObservableCollection<Presenter> Presenters
{
get { return _myProperty; }
set { _myProperty = value; }
}
public ObservableCollection<DateTime?> Dates
{
get { return _myDates; }
set { _myDates = value; }
}
}
public class Presenter
{
public string FullName { get; set; }
public ObservableCollection<Presentation> Presentations { get; set; }
}
public class Presentation
{
public TeachingSession TeachingSession { get; set; }
}
public class TeachingSession
{
public DateTime? SessionDate { get; set; }
}
Result:
Because of sa_ddam213's answer, i was able to track the problem down to the default synchronisation with the CollectionViewSource used to bindItemsSource
The combobox needed the attribute:
IsSynchronizedWithCurrentItem="False"
Related
Using the MVVM pattern in a WPF application, I want to set on the 'Editing State' of a records in row.
Every time the user starts editing a record by clicking on Edit button, that row should switch to the 'editing' mode.
Finished, he can save all changes in the row by clicking the same or another button
How can I set edit mode (IsReadOnly=true/false) for All cells in selected Row on click "Edit" button?
Any help is appreciated!
This is my current code:
XAML
<Window x:Class="TotalRows.TotalRowsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TotalRows"
mc:Ignorable="d"
x:Name="xMainWindow"
Title="RowsTotalWindow" Height="450" Width="800">
<Window.DataContext>
<local:ExampleData/>
</Window.DataContext>
<Grid>
<StackPanel >
<DataGrid x:Name="myGrid" IsReadOnly="True" CanUserAddRows="False" SelectionMode="Single" CanUserDeleteRows="False"
ItemsSource="{Binding ItemsViewCollection}" RowDetailsVisibilityMode="Collapsed"
SelectedItem="{Binding SelectedItemRow, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel HorizontalAlignment="Stretch">
<ToggleButton x:Name="btnEditItem" Content="Edit" Width="50" Height="20" Margin="0 0 3 0"
Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.UpdateItemCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=DataContext}"/>
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn x:Name="gr" Binding="{Binding Group}" Header="Gr" Width="30" />
<DataGridTextColumn x:Name="one" Binding="{Binding Col_1}" Header="h1" Width="30" />
<DataGridTextColumn x:Name="two" Binding="{Binding Col_2}" Header="h2" Width="30" />
<DataGridTextColumn x:Name="tree" Binding="{Binding Col_3}" Header="h3" Width="30" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
</Window>
Code Behind
namespace TotalRows
{
public class ItemClass
{
public int Group { get; set; }
public string Title { get; set; }
public int Col_1 { get; set; }
public int Col_2 { get; set; }
public int Col_3 { get; set; }
}
public class ExampleData
{
private bool _IsReadMode;
public bool IsReadMode
{
get { return _IsReadMode; }
set
{
_IsReadMode = value;
OnPropertyChanged(nameof(IsReadMode));
}
}
private ItemClass _selectedItem = null;
public ItemClass SelectedItemRow
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged(nameof(SelectedItemRow));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<ItemClass> _items;
public ObservableCollection<ItemClass> Items
{
get
{
return _items;
}
set
{
if (_items != value)
{
_items = value;
OnPropertyChanged(nameof(Items));
}
}
}
private ICollectionView _itemsViewCollection;
public ICollectionView ItemsViewCollection
{
get
{
return _itemsViewCollection;
}
set
{
if (_itemsViewCollection != value)
{
_itemsViewCollection = value;
OnPropertyChanged(nameof(ItemsViewCollection));
}
}
}
public ICommand UpdateItemCommand { get; private set; }
public ExampleData()
{
IsReadMode = true;
UpdateItemCommand = new ViewModelCommand(param => updateItemCommand(param));
Items = new ObservableCollection<ItemClass>()
{
new ItemClass() {Group=1, Title="Item1", Col_1=100, Col_2=150, Col_3=250},
new ItemClass() {Group=2, Title="Item1", Col_1=50, Col_2=2, Col_3=200},
new ItemClass() {Group=2, Title="Item2", Col_1=50, Col_2=100, Col_3=40},
new ItemClass() {Group=3, Title="Item1", Col_1=60, Col_2=25, Col_3=230},
new ItemClass() {Group=3, Title="Item2", Col_1=30, Col_2=25, Col_3=0},
new ItemClass() {Group=3, Title="Item3", Col_1=9, Col_2=100, Col_3=20},
new ItemClass() {Group=4, Title="Item1", Col_1=46, Col_2=32, Col_3=30},
};
ItemsViewCollection = CollectionViewSource.GetDefaultView(Items);
ItemsViewCollection.GroupDescriptions.Add(new PropertyGroupDescription("Group"));
}
private void updateItemCommand(object param)
{
IsReadMode = !IsReadMode;
}
}
}
Do you realise f2 switches the current row into edit mode?
CommandManager.RegisterClassInputBinding(ownerType, new InputBinding(BeginEditCommand, new KeyGesture(Key.F2)));
CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(BeginEditCommand, new ExecutedRoutedEventHandler(OnExecutedBeginEdit), new CanExecuteRoutedEventHandler(OnCanExecuteBeginEdit)));
You could bind your edit button to a command in the ExampleData viewmodel and pass a reference to the specific ItemClass as a command parameter.
Use relativesource binding to get to that command.
ExampleData owns that collection so you can set properties on that instance and stash a reference or index to the last one they edited set the flag back. Or iterate through the whole collection.
Seems you know how to write a command but I recommend the community mvvm toolkit and relaycommand.
Your binding would be something like
<Button Command="{Binding DataContext.EditThisOneCommand
, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}">
The command parameter passes the row to an Icommand so will be a parameter passed to your command.
A similar command I happen to have.
private RelayCommand<Thing> _colourCommand;
public RelayCommand<Thing> ColourCommand
{
get
{
return _colourCommand
?? (_colourCommand = new RelayCommand<Thing>(
_thing =>
{
_thing.Row = Items.IndexOf(_thing);
},
_thing => CanUserClick));
}
}
You would of course have EditThisOneCommand
You then have to tell the UI to issue a BeginEditCommand.
And then you need to tell the UI to issue a CommitEditCommand when you finish.
CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(CommitEditCommand, new ExecutedRoutedEventHandler(OnExecutedCommitEdit), new CanExecuteRoutedEventHandler(OnCanExecuteCommitEdit)));
These commands are source from the datagrid.
You could instead just bind those datagrid commands to buttons and not have this flag.
A datagridrow has a property IsEditing. You might be able to bind that onewaytosource to your flag. You'd set that binding via a style.
Not sure why you'd want to, but you could take a look at that.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.datagridrow.isediting?view=windowsdesktop-7.0
I have a WPF DataGrid bound to a DataTable. The DataTable has columns containing strings and columns containing custom objects (Requirement):
public class PdfFormData
{
public string SupplierName { get; set; }
public List<Requirement> Requirements = new List<Requirement>();
public class Requirement
{
public string ID { get; set; }
public string SupplierStatus { get; set; }
public string SupplierComment { get; set; }
public string OEMStatus { get; set; }
}
}
I'm adding the columns to the DataGrid with this loop:
PHDataGrid.ItemsSource = dataSet.Tables["ReqIfTable"].DefaultView;
PHDataGrid.Columns.Clear();
foreach (DataColumn dataColumn in dataSet.Tables["ReqIfTable"].Columns)
{
if(dataColumn.DataType == typeof(ReqForms.PdfFormData.Requirement))
{
DataGridTemplateColumn dgColumn = new DataGridTemplateColumn();
dgColumn.Header = dataColumn.ColumnName;
dgColumn.CellTemplate = (DataTemplate)FindResource("dgTemplateRequirement");
dgColumn.CanUserSort = true;
dgColumn.IsReadOnly = true;
PHDataGrid.Columns.Add(dgColumn);
}
else
{
DataGridTextColumn dgColumn = new DataGridTextColumn();
dgColumn.Header = dataColumn.ColumnName;
Binding dgBinding = new Binding(dataColumn.ColumnName);
dgColumn.Binding = dgBinding;
dgColumn.CanUserSort = true;
dgColumn.IsReadOnly = true;
PHDataGrid.Columns.Add(dgColumn);
}
}
And I have defined a template for showing my Requirement objects:
<Window.Resources>
<DataTemplate x:Key="dgTemplateRequirement">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Requirement.SupplierStatus}" />
<TextBlock Text="{Binding Requirement.SupplierComment}" />
<TextBlock Text="{Binding Requirement.OEMStatus}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
The Binding to the Requirement properties (SupplierStatus, SupplierComment, ...) is not working and stays empty. How can I bind to the properties of my custom object that is stored in the DataTable?
The bindings do not work, because the DataContext of the the templated cell is a DataRowView.
You can either create a converter as in this related post or just access the members directly with an indexer binding path, where requirement is the name of your column in the DataTable, e.g.:
<TextBlock Text="{Binding Row[requirement].SupplierStatus}" />
I set the DataContext of the StackPanel to the Requirement object, so it is even easier to bind.
<DataTemplate x:Key="dgTemplateRequirement">
<StackPanel Orientation="Vertical" DataContext="{Binding Row[requirement]}">
<TextBlock Text="{Binding SupplierStatus}" />
<TextBlock Text="{Binding SupplierComment}" />
<TextBlock Text="{Binding OEMStatus}" />
</StackPanel>
</DataTemplate>
I found the solution in this post:
WPF DataGrid - Databind to DataTable cell in CellTemplates DataTemplate
You have to use a converter to reach from the table to the actual object.
I have a combobox declared in my xaml.
I have a ExtendedComboBoxItem : ComboBoxItem defined which has properties DisplayName and DisplayImage.
In my user control, I have a comboBox control where i defined a data template that is essentially a Image | TextBock
In code-behind, I ALWAYS ADD same 3 items to it, so I do something like:
List<ExtendedComboBoxItem > items = new List<ExtendedComboBoxItem >();
ExtendedComboBoxItem item1 = new ExtendedComboBoxItem ("A","imagePath");
ExtendedComboBoxItem item2 = new ExtendedComboBoxItem ("A","imagePath");
ExtendedComboBoxItem item3 = new ExtendedComboBoxItem ("A","imagePath");
items.Add(item1);
items.Add(item2);
items.Add(item3);
this.comboBox.ItemsSource = items;
Is there a XAML only way of doing the above or a better cleaner way to do this?
thanks!
I'm not 100% sure that this will work - there are some ifs and buts regarding the type you can put in the array: https://msdn.microsoft.com/en-us/library/ms753379%28v=vs.110%29.aspx#Requirements_for_a_Custom_Class_as_a_XAML_Element .
You can set up an array in the Resources section of your XAML:
<Window.Resources>
<x:Array Type="mynamespace:ExtendedComboBoxItem"
x:Key="MyArray">
<mynamespace:ExtendedComboBoxItem MyProp1="A" MyProp2="imagePath" />
<mynamespace:ExtendedComboBoxItem MyProp1="A" MyProp2="imagePath" />
<mynamespace:ExtendedComboBoxItem MyProp1="A" MyProp2="imagePath" />
</x:Array>
</Window.Resources>
...
<ComboBox ItemsSource="{StaticResource MyArray}" />
You can define a DataTemplate for the Items in the combobox, Refer below code.
<ComboBox x:Name="cbo">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding DisplayImage}"/>
<TextBlock Text="{Binding DisplayName}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ObservableCollection<ComboItem> combolst = new ObservableCollection<ComboItem>();
for (int i = 0; i < 10; i++)
{
combolst.Add(new ComboItem() { DisplayName = "Name" + i, DisplayImage = "Icon.png" });
}
cbo.ItemsSource = combolst;
}
}
public class ComboItem
{
public string DisplayName { get; set; }
public string DisplayImage { get; set; }
}
I am trying to bind the ComboBox below to the list of Characters in the ObservableCollection, but it wont show anything. Any ideas why?
XAML:
<TabControl ItemsSource ="{Binding TextEditors}"
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox> ItemsSource="{Binding TextLines}"
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ComboBox
ItemsSource="{Binding DataContext.InvCharacter, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}"
DisplayMemberPath="name"
SelectedValuePath="cid"
SelectedValue="{Binding cid}">
</ComboBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
this is the class i am referring to:
class TextEditorVM: IViewModel {
public ObservableCollection<TextLineVM> TextLines { get { return textLines; } set { textLines = value;} }
public ObservableCollection<T_Character> InvCharacters { get { return invCharacters; } set { invCharacters = value; } }
public TextEditorVM(T_Dialogue dialogue)
{
DialogueManager.Instance.Register(this);
this.TextLines = new ObservableCollection<TextLineVM>();
this.InvCharacters = new ObservableCollection<T_Character>();
}
}
and the MainVM:
class MainVM : IViewModel
{
public ObservableCollection<TextEditorVM> TextEditors { get { return textEditors; } set { textEditors = value; OnPropertyChanged("TextEditors"); }
}
my T_Character Class looks like this now :
public class T_Character
{
public String cid { get; set; }
public String name { get; set; }
public T_Character(String cid, String name)
{
this.cid = cid;
this.name = name;
}
}
The DataContext of the TabControl is of type MainVM. The RelativeSource of the ComboBox binding should not be the TabControl but rather the ListBox.
Your InvCharacters property is on your TextEditorVM object which is in your ObservableCollection, however your binding is referencing TabControl.DataContext, which is MainVM, and does not contain that property.
Switch your RelativeSource binding to reference TabItem (it gets created automatically when you bind TabControl.ItemsSource) or ListBox to reference your TextEditorVM object
<ComboBox ItemsSource="{Binding DataContext.InvCharacters,
RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}">
</ComboBox>
I have tried everything and got nowhere so I'm hoping someone can give me the aha moment.
I simply cannot get the binding to pull the data in the datagrid successfully.
I have a DataTable that contains multiple columns with of MyDataType
public class MyData
{
string nameData {get;set;}
bool showData {get;set;}
}
MyDataType has 2 properties (A string, a boolean)
I have created a test DataTable
DataTable GetDummyData()
{
DataTable dt = new DataTable("Foo");
dt.Columns.Add(new DataColumn("AnotherColumn", typeof(MyData)));
dt.Rows.Add(new MyData("Row1C1", true));
dt.Rows.Add(new MyData("Row2C1", false));
dt.AcceptChanges();
return dt;
}
I have a WPF DataGrid which I want to show my DataTable.
But all I want to do is to change how each cell is rendered to show [TextBlock][Button] per cell with values bound to the MyData object and this is where I'm having a tonne of trouble.
My XAML looks like this
<Window.Resources>
<ResourceDictionary>
<DataTemplate x:Key="MyDataTemplate" DataType="MyData">
<StackPanel Orientation="Horizontal" >
<Button Background="Green" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,0,0" Content="{Binding Path=nameData}"></Button>
<TextBlock Background="Green" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0" Text="{Binding Path=nameData}"></TextBlock>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<dg:DataGrid Grid.Row="1" ItemsSource="{Binding}" AutoGenerateColumns="True"
x:Name="dataGrid1" SelectionMode="Single" CanUserAddRows="False"
CanUserSortColumns="true" CanUserDeleteRows="False" AlternatingRowBackground="AliceBlue"
AutoGeneratingColumn="dataGrid1_AutoGeneratingColumn" />
</Grid>
Now all I do once loaded is to attempt to bind the DataTable to the WPF DataGrid
dt = GetDummyData();
dataGrid1.ItemsSource = dt.DefaultView;
The TextBlock and Button show up, but they don't bind, which leaves them blank.
Could anyone let me know if they have any idea how to fix this.
This should be simple, thats what Microsoft leads us to believe.
I have set the Column.CellTemplate during the AutoGenerating event and still get no binding.
Please help!!!
Edit: Updated to reflect the input of Aran Mulholland (see comment)
Apparently the DataGrid is passing the entire DataRowView to each cell. That's why the binding doesn't work. Your DataTemplate expects the DataContext to be of type MyData, but instead it is of type DataRowView. My proposed (somewhat hack-ish) workaround to get the DataContext you want is to create a custom DataGridTemplateColumn that will extract the necessary item from the DataRowView. The code is below:
<Window x:Class="DataGridTemplateColumnSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dg="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ResourceDictionary>
<DataTemplate x:Key="MyDataTemplate" DataType="DataRowView">
<StackPanel Orientation="Horizontal">
<Button Background="Green" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="5,0,0,0" Content="{Binding Path=nameData}"></Button>
<TextBlock Background="Green" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,0,0" Text="{Binding Path=nameData}"></TextBlock>
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
<Grid>
<dg:DataGrid Grid.Row="1" AutoGenerateColumns="True" x:Name="dataGrid1" SelectionMode="Single"
CanUserAddRows="False" CanUserSortColumns="true" CanUserDeleteRows="False"
AlternatingRowBackground="AliceBlue" AutoGeneratingColumn="dataGrid1_AutoGeneratingColumn"
ItemsSource="{Binding}" VirtualizingStackPanel.VirtualizationMode="Standard" />
</Grid>
</Window>
using System.Data;
using System.Windows;
using Microsoft.Windows.Controls;
namespace DataGridTemplateColumnSample
{
public partial class Window1
{
public Window1()
{
InitializeComponent();
DataContext = GetDummyData().DefaultView;
}
private static DataTable GetDummyData()
{
var dt = new DataTable("Foo");
dt.Columns.Add(new DataColumn("OneColumn", typeof(MyData)));
dt.Columns.Add(new DataColumn("AnotherColumn", typeof(MyData)));
dt.Rows.Add(new MyData("Row1C1", true), new MyData("Row1C2", true));
dt.Rows.Add(new MyData("Row2C1", false), new MyData("Row2C2", true));
dt.AcceptChanges();
return dt;
}
private void dataGrid1_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var column = new DataRowColumn(e.PropertyName);
column.Header = e.Column.Header;
column.CellTemplate = (DataTemplate)Resources["MyDataTemplate"];
e.Column = column;
}
}
public class DataRowColumn : DataGridTemplateColumn
{
public DataRowColumn(string column) { ColumnName = column; }
public string ColumnName { get; private set; }
protected override FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
var row = (DataRowView) dataItem;
var item = row[ColumnName];
cell.DataContext = item;
var element = base.GenerateElement(cell, item);
return element;
}
}
public class MyData
{
public MyData(string name, bool data) { nameData = name; showData = data; }
public string nameData { get; set; }
public bool showData { get; set; }
}
}
Note: This approach only appears to work with container virtualization off or in Standard mode. If the VirtualizationMode is set to Recycling the template is not applied.
After finding this thread and having trouble with the code shown here, I ran across this thread on MSDN, and it works much better! No virtualization problems at all so far as I've seen.
http://social.msdn.microsoft.com/Forums/en/wpf/thread/8b2e94b7-3c44-4642-8acc-851de5285062
Code:
private void dataGrid1_AutoGeneratingColumn(object sender, Microsoft.Windows.Controls.DataGridAutoGeneratingColumnEventArgs e)
{
if (e.PropertyType == typeof(MyData))
{
MyDataGridTemplateColumn col = new MyDataGridTemplateColumn();
col.ColumnName = e.PropertyName; // so it knows from which column to get MyData
col.CellTemplate = (DataTemplate)FindResource("MyDataTemplate");
e.Column = col;
e.Column.Header = e.PropertyName;
}
}
public class MyDataGridTemplateColumn : DataGridTemplateColumn
{
public string ColumnName
{
get;
set;
}
protected override System.Windows.FrameworkElement GenerateElement(DataGridCell cell, object dataItem)
{
// The DataGridTemplateColumn uses ContentPresenter with your DataTemplate.
ContentPresenter cp = (ContentPresenter)base.GenerateElement(cell, dataItem);
// Reset the Binding to the specific column. The default binding is to the DataRowView.
BindingOperations.SetBinding(cp, ContentPresenter.ContentProperty, new Binding(this.ColumnName));
return cp;
}
}