I am trying to control the foreground color of cells in a XamTreeGrid. I do this within a converter like the following:
<Style TargetType="{x:Type dataPresenter:CellValuePresenter}">
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Self},
Converter={StaticResource ForegroundConverter}}" />
</Style>
Inside the converter I need to know the name of the field the CellValuePresenter is for (the coloring is dependent on the name of the field).
The converter looks like the following:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var cellValuePresenter = value as CellValuePresenter; // works
var fieldName = cellValuePresenter.Field.Name; // Field is null!!
Any idea what is happening? Is there another way for me to retrieve the field from a CellValuePresenter (from inside the converter)? I am using 16.2.20162.1006.
Thanks,
Damon
Hello Damon,
Since Invoke is executing synchronously on the calling thread, which itself is waiting for Invoke to finish, the field is expected not to be found at this point yet. BeginInvoke on the other hand is executing asynchronously and the calling thread does not wait for it. This way when the BeginInvoke is executed, the field is successfully found.
By using the Loaded event of the (Tree)CellValuePresenter, you should be able to achieve the desired functionality.
If you have any further questions, please let me know.
From inside the converter I'd need to use Dispatcher.CurrentDispatcher.Invoke instead of Dispatcher.CurrentDispatcher.BeginInvoke as I need to return a result from the Convert method. You would think it would work, but changing to Invoke actually has the same issue - the 'Field' property is always null. Strange. I tried this in your sample solution with the code below:
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture) { String fieldName = null; Dispatcher.CurrentDispatcher.Invoke(new Action(() => { var cvp = value as CellValuePresenter; var field = cvp.Field; fieldName = field.Name; // BREAKS - field is null
}));
if (fieldName == "Name") return Brushes.Red;
return Binding.DoNothing; }
I'll try the Loaded event option.
Thank you for the code-snippets you have provided.
In order to successfully get the Field of the CellValuePresenter inside the Converter for it's Foreground property, you can simply place your code within a Dispatcher's action in order to synchronize the executions.
For example:
object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture){ Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => { var cvp = value as CellValuePresenter; var field = cvp.Field; })); return Binding.DoNothing;}
return Binding.DoNothing;}
Another approach I can suggest you is to handle your Foreground logic for the cells inside the Loaded event of the (Tree)CellValuePresenter. This will ensure that whenever the CellValuePresenter gets (pre)loaded, you will be able to set the Foreground at the right time.(Note that it depends if you are handling the Loaded event of the CellValuePresenter or the TreeCellValuePresenter, depending on your scenario)
private void TreeCellValuePresenter_Loaded(object sender, RoutedEventArgs e){ var cvp = (sender as TreeCellValuePresenter); if (cvp.Field.Name == "Name") cvp.Foreground = Brushes.Red;}
I have attached a sample application, that uses the approach from above.