Hello,
I'm having problems implementing a custom template with the CellValuePresenter.
Inside my template I have a ContentControl control that I programmatically add my own controls to, based on the underlying cell's Data Record Data Item.
The problem is I can't find an elegant way of gaining a reference to the Cell I am dealing with, as at the CellValuePresenter level I can't see any exposed properties that provide this. The CVP does provide the DataContext (to provide the DataRecord) and the Field but when binding from XAML you can only pass in one property reference in the "Path" attribute :
<ContentControl Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext, Converter={StaticResource textFormatterHelper} }" />
Ideally when binding I want to set the Path attribute value to the CellValuePrenter itself, so then I can get access to both the DataContext and Field, so I can get a reference to the Cell I'm trying to template.
As mentioned, I need access to the Data Record's Data Item and the Cell, so passing in the CVP's Value or Content properties is not sufficient with what I'm trying to do.
I'm quite new to WPF so there might be an easy solution to this!
Thanks,
Jamie
Hi Josh,
I've been experiencing some strange issues with the above solution when scrolling comes into play, all of the cell values get mixed up as you move the scroller up and down. e.g.
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { CellValuePresenter cvp = value as CellValuePresenter;
TextBlock tb = new TextBlock(); tb.Text = cvp.Record.Cells[cvp.Field].Value.ToString();
return tb; }
What I mean by mixed up is the contents of the cells change their value randomly to another value on a completely different row (but on the same field).
I've managed another workaround though which appears to render rows correctly when scrolling, by using a MultiBinding object :
<MultiBinding Converter="{StaticResource textFormatterHelper}"> <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="DataContext"></Binding> <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Field"></Binding> </MultiBinding>
Cheers,
Thanks a lot, just what I wanted :)
Hi,
You can bind the ContentControl like this:
<ContentControl Content="{Binding RelativeSource={x:Static RelativeSource.TemplatedParent}, Converter={StaticResource ConfigConv}}" />
The value converter can access the DataRecord and Field, as seen below:
public class ConfigurableCellValueConverter : IValueConverter{ public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) { CellValuePresenter cvp = value as CellValuePresenter; if (cvp == null) return null; DataRecord record = cvp.Record; Field field = cvp.Field; Grid root = new Grid(); // TODO: Create the visuals to put into the cell... return root; } public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) { throw new System.NotImplementedException(); }}
Josh
My underlying data source is an XML document with attributes attached to specific nodes, with each of these nodes representing a Cell in the XamDataGrid
This allows me to store meta-data against each Cell as attached attributes. e.g.
<DocElement> <Customer> <FirstName color='Red'>John</FirstName> <LastName>Smith</LastName> </Customer></DocElement>
(I know embedding presentation information and business data together isn't "best practise" but it is a requirement of the task I have been given!)
So why do I need to access a Cell? When templating each cell using CellValuePresenter I would like to pass in a reference to the Cell OR CellValuePresenter, so I then have a reference to the Field, DataRecord etc. From there I can cast the DataRecord.DataItem to an XmlElement that contains a collection of child nodes, which represent each Cell's display value and their custom meta-data. I can then create my custom controls based on the cell's value and meta-data.