Question

I would like to have the text of the Label next to my ComboBox turn red when the ComboBox is in error, but with how I currently have it set up the Label's text color only updates on the initial load of the control. How do I go about having the validation of the Label happen when the selection in the ComboBox has changed? Or is there another way to update the Label's style?

I have the following XAML:

<Style x:Key="labelStyle" TargetType="Label">
    <Setter Property="HorizontalContentAlignment" Value="Left" />
    <Setter Property="VerticalContentAlignment" Value="Center" />
    <Setter Property="Validation.ErrorTemplate">
        <Setter.Value>
            <ControlTemplate>
                <AdornedElementPlaceholder>
                    <Border BorderBrush="Transparent" BorderThickness="0" />
                </AdornedElementPlaceholder>
            </ControlTemplate>
        </Setter.Value>
    </Setter>

    <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
            <Setter Property="Background" Value="White"/>
            <Setter Property="Foreground" Value="Red"/>
        </Trigger>
    </Style.Triggers>
</Style>

<Label Style="{StaticResource labelStyle}" Content="{Binding Path=Label, ValidatesOnDataErrors=True}" />

<ComboBox ItemsSource="{Binding Path=ItemList}" SelectedItem="{Binding Path=SelectedItem, ValidatesOnDataErrors=True}"/>

And then in code:

public string this[string propertyName]
{
    get
    {
        if (propertyName == "Label")
        {
            if (this.IsRequired && !DelayValidation && SelectedItem == "")
                return Label + " required";
        }

        return null;
    }
}
Was it helpful?

Solution

With how you have done it you would need to also raise property changed on the 'Label' property whenever your 'SelectedItem' property changes.

You could do it without having to have extra properties on your model/viewmodel (or whatever you are binding too) with a xaml only solution. If you had a combo box like:

<ComboBox Name="comb" ItemsSource="{Binding Strings}" SelectedValue="{Binding Stringy,ValidatesOnDataErrors=True}" />

You could make a label like:

<Label Content="Select a string">
    <Label.Style>
        <Style TargetType="Label">
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=comb,Path=(Validation.HasError)}" Value="True">
                    <Setter Property="Foreground" Value="Red" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Label.Style>
</Label>

Or, if you wanted this on multiple controls (or if you do not want to have this style/trigger in the label's explicit style) you could make an attached property/behaviour like:

public class LabelValidationHelper
{
    public static FrameworkElement GetDetailControl(DependencyObject obj)
    {
        return (FrameworkElement)obj.GetValue(DetailControlProperty);
    }

    public static void SetDetailControl(DependencyObject obj, FrameworkElement value)
    {
        obj.SetValue(DetailControlProperty, value);
    }

    public static readonly DependencyProperty DetailControlProperty = DependencyProperty.RegisterAttached("DetailControl", typeof(FrameworkElement), typeof(LabelValidationHelper), new UIPropertyMetadata(null, OnDetailControlChanged));       

    private static void OnDetailControlChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        if (args.NewValue == null)
            return;

        var label = (Label)sender;

        var style = new Style(typeof(Label), label.Style);

        var binding = new Binding();
        binding.Source = args.NewValue;
        binding.Path = new PropertyPath(Validation.HasErrorProperty);

        var trigger = new DataTrigger();
        trigger.Binding = binding;
        trigger.Value = true;

        var setter = new Setter();
        setter.Property = Label.ForegroundProperty;
        setter.Value = Brushes.Red;

        trigger.Setters.Add(setter);

        style.Triggers.Add(trigger);

        label.Style = style;
    }

}

And you could use it like:

<Label Content="Select a string" example:LabelValidationHelper.DetailControl="{Binding ElementName=comb}" />

OTHER TIPS

You can do it with style only.

<Style TargetType="Label">
        <Setter Property="FontSize" Value="20"/>
        <Setter Property="FontWeight" Value="Bold"/>
        <Setter Property="Padding" Value="0,5"/>
        <Style.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(Target).(Validation.HasError)}" Value="True">
                <Setter Property="Foreground" Value="Red" />
            </DataTrigger>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(Target).(Validation.HasError)}" Value="False">
                <Setter Property="Foreground" Value="Gray" />
            </DataTrigger>
        </Style.Triggers>
    </Style>

and use it like this:

<Label Target="{Binding ElementName=NominaalBox}" Content="Nominaal (gram):"/>
<TextBox x:Name="NominaalBox" Text="{Binding Path=NominaalGram, Mode=TwoWay, TargetNullValue='', UpdateSourceTrigger=PropertyChanged}"/>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top