I have a grid of text blocks, some of which need to be red, some need to be the default text color. What can I return from my binding to the Foreground property to tell the framework to use the default text color ?
I have a grid of text blocks, some of which need to be red, some need to be the default text color. What can I return from my binding to the Foreground property to tell the framework to use the default text color ?
Share Improve this question asked Nov 20, 2024 at 9:08 John JohnJohn John 3731 silver badge7 bronze badges 3- 4 I'd be inclined to use a style with a data trigger to change the colours rather than explicitly returning a colour from the VM property. – Richard Deeming Commented Nov 20, 2024 at 9:23
- 1 I really , really dislike this kind of stuff in MVC frameworks. You always end up with the logic that dictates how things are shown split in 2 separate files. Sigh. Guess that's what I'm stuck doing. – John John Commented Nov 20, 2024 at 10:45
- Returning DependencyProperty.UnsetValue does not solve the problem and I could not find another option that can solve it. – EldHasp Commented Nov 20, 2024 at 19:45
1 Answer
Reset to default 0You could use an attached DependencyProperty
for this. This method will preserve the default taking into account the style of the control when the bound value is null
.
public static class AttachedProperty
{
public static readonly DependencyProperty ForegroundOrDefaultProperty =
DependencyProperty.RegisterAttached(
"ForegroundOrDefault",
typeof(Brush),
typeof(AttachedProperty),
new FrameworkPropertyMetadata((obj, e) =>
{
if (!(obj is TextBlock tb))
return;
if (e.NewValue is Brush br)
tb.Foreground = br;
else
tb.ClearValue(TextBlock.ForegroundProperty);
}));
public static Brush GetForegroundOrDefault(DependencyObject obj)
{
return obj.GetValue(ForegroundOrDefaultProperty) as Brush;
}
public static void SetForegroundOrDefault(DependencyObject obj, object value)
{
obj.SetValue(ForegroundOrDefaultProperty, value);
}
}
An example in action -
public class Person : ViewModel
{
public string Name { get; set; }
public SolidColorBrush Color { get; set; }
}
public class MainViewModel : ViewModel
{
public List<Person> People { get; } = new List<Person>
{
new Person
{
Name = "Larry",
Color = Brushes.Blue,
},
new Person
{
Name = "Curly",
},
new Person
{
Name = "Moe",
}
};
}
XAML:
<Grid>
<Grid.Resources>
<Style x:Key="DefaultTextBlockStyle"
TargetType="TextBlock">
<Setter Property="Foreground" Value="Red"/>
</Style>
</Grid.Resources>
<Grid.DataContext>
<local:MainViewModel />
</Grid.DataContext>
<ListBox ItemsSource="{Binding People}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"
local:AttachedProperty.ForegroundOrDefault="{Binding Color}"
Style="{StaticResource DefaultTextBlockStyle}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
The red color from the style is preserved when the bound color is null. Unfortunately the solution is a bit ad-hoc since you need an attached property for every conceivable control and property you might want to do this with.
However, if you don't care about the style and are ok with the property reverting to the absolute default value for the property as defined by the DependencyProperty
metadata (in this case, black), then a binding converter offers a more general purpose solution:
public class ValueOrDefaultConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value != null)
return value;
else
return Binding.DoNothing;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
XAML:
<TextBlock Text="{Binding Name}"
Foreground="{Binding Color, Converter={StaticResource ValueOrDefaultConverter}}"
Style="{StaticResource DefaultTextBlockStyle}" />