Best practice for changing border color of TextBox?

Suppose you have a View with several Texboxes like this

<TextBox Text="{Binding myText1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />

each of them already contains some text. If the user changes this text, then the "Text Area" field should change to "Orange", and if he cancels his changes, he should get the default color.

At the moment, I am doing it like this:

<TextBox Height="23"  Text="{Binding myText1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" BorderThickness="2">
   <TextBox.Style>
        <Style TargetType="TextBox">
             <Style.Triggers>
                 <DataTrigger Binding="{Binding myDirtyText1, UpdateSourceTrigger=PropertyChanged}" Value="True">
                     <Setter Property="BorderBrush" Value="Orange"/>
                 </DataTrigger>
             </Style.Triggers>
        </Style>
    </TextBox.Style>
</TextBox>

Is there a more general / easier way to do this?

Edit

I already use IDataErrorInfo+ System.ComponentModel.DataAnnotationsto check for errors. Perhaps in this case there is a similar way, but I did not find anything useful to reduce my xaml and code to a minimum.

EDIT 2.0

I think you really don't understand my problem, so I will give a better example of how this looks:

View Xaml (no code)

<Grid Margin="12">
    <Label Content="Name:" Height="28" HorizontalAlignment="Left" VerticalAlignment="Top" Width="79" />
    <TextBox Height="23" HorizontalAlignment="Left" Margin="102,2,0,0" VerticalAlignment="Top" Width="170" BorderThickness="2"
             Text="{Binding NameD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding dirtyName, UpdateSourceTrigger=PropertyChanged}" Value="True">
                        <Setter Property="BorderBrush" Value="Orange"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

    <Label Content="Anzeigetext:" Height="28" HorizontalAlignment="Left" Margin="0,34,0,0" VerticalAlignment="Top" Width="79" />
    <TextBox BorderThickness="2" Height="23" HorizontalAlignment="Left" Margin="102,36,0,0" VerticalAlignment="Top" Width="170" 
             Text="{Binding AnzeigetextD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding dirtyAnzeigetext, UpdateSourceTrigger=PropertyChanged}" Value="True">
                        <Setter Property="BorderBrush" Value="Orange"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

    <Label Content="Preis:" Height="28" HorizontalAlignment="Left" Margin="0,68,0,0" VerticalAlignment="Top" Width="79" />
    <TextBox BorderThickness="2" Height="23" HorizontalAlignment="Left" Margin="102,70,0,0" VerticalAlignment="Top" Width="170" 
             Text="{Binding PreisD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, StringFormat=\{0:c\}}">
        <TextBox.Style>
            <Style TargetType="TextBox">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding dirtyPreis, UpdateSourceTrigger=PropertyChanged}" Value="True">
                        <Setter Property="BorderBrush" Value="Orange"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

    <Button Content="Speichern" Height="23" HorizontalAlignment="Left" Margin="102,110,0,0" VerticalAlignment="Top" Width="75" Command="{Binding SaveCommand}"/>
    <Button Content="Abbrechen" Height="23" HorizontalAlignment="Left" Margin="197,110,0,0" VerticalAlignment="Top" Width="75" Command="{Binding CancelCommand}"/>
</Grid>

ViewModel

public class MenuangebotVM : DetailVM, IContains
{
    #region private Values
    private Menuangebot myOriginal = new Menuangebot();
    private Menuangebot myValue = new Menuangebot();
    #endregion // private Values

    #region Properties

    #region Detail Properties
    public int Id { get { return myOriginal.Id; } }
    public bool? Result { get; private set; }
    public string Beschreibung { get { return "Einrichtung"; } }

    [Required]
    [RegularExpression(@"^[0-9a-zA-ZäöüßÄÖÜß''-'\s]{2,40}$")]
    public string NameD
    {
        get { return myValue.Name; }
        set
        {
            myValue.Name = value;
            RaisePropertyChanged(() => Reg(() => NameD));
            RaisePropertyChanged(() => Reg(() => dirtyName));
        }
    }
    public bool dirtyName
    {
        get { return (!isNew && myValue.Name != myOriginal.Name) ? true : false; }
    }

    [Required]
    [RegularExpression(@"^[0-9a-zA-ZäöüßÄÖÜß''-'\s]{2,25}$")]
    public string AnzeigetextD
    {
        get { return myValue.Anzeigetext; }
        set
        {
            myValue.Anzeigetext = value;
            RaisePropertyChanged(() => Reg(() => AnzeigetextD));
            RaisePropertyChanged(() => Reg(() => dirtyAnzeigetext));
        }
    }
    public bool dirtyAnzeigetext
    {
        get { return (!isNew && myValue.Anzeigetext != myOriginal.Anzeigetext) ? true : false; }
    }

    [Required]
    public decimal PreisD
    {
        get { return myValue.Preis; }
        set
        {
            myValue.Preis = value;
            RaisePropertyChanged(() => Reg(() => PreisD));
            RaisePropertyChanged(() => Reg(() => dirtyPreis));
        }
    }
    public bool dirtyPreis
    {
        get
        {
            var value = myValue.Preis;
            var Original = myOriginal.Preis;

            return (!isNew && value != Original) ? true : false;
        }
    }

    #endregion //Detail Properties
    #endregion //Properties

// more code

}

, , -

<Grid Margin="12">
    <Label Content="Name:" Height="28" HorizontalAlignment="Left" VerticalAlignment="Top" Width="79" />
    <TextBox Height="23" HorizontalAlignment="Left" Margin="102,2,0,0" VerticalAlignment="Top" Width="170" BorderThickness="2"
             Text="{Binding NameD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, EditesOnDataChanges=true}">

    </TextBox>

    <Label Content="Anzeigetext:" Height="28" HorizontalAlignment="Left" Margin="0,34,0,0" VerticalAlignment="Top" Width="79" />
    <TextBox BorderThickness="2" Height="23" HorizontalAlignment="Left" Margin="102,36,0,0" VerticalAlignment="Top" Width="170" 
             Text="{Binding AnzeigetextD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, EditesOnDataChanges=true}">
    </TextBox>

    <Label Content="Preis:" Height="28" HorizontalAlignment="Left" Margin="0,68,0,0" VerticalAlignment="Top" Width="79" />
    <TextBox BorderThickness="2" Height="23" HorizontalAlignment="Left" Margin="102,70,0,0" VerticalAlignment="Top" Width="170" 
             Text="{Binding PreisD, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, StringFormat=\{0:c\, EditesOnDataChanges=true}}">
    </TextBox>

    <Button Content="Speichern" Height="23" HorizontalAlignment="Left" Margin="102,110,0,0" VerticalAlignment="Top" Width="75" Command="{Binding SaveCommand}"/>
    <Button Content="Abbrechen" Height="23" HorizontalAlignment="Left" Margin="197,110,0,0" VerticalAlignment="Top" Width="75" Command="{Binding CancelCommand}"/>
</Grid>

ViewModel

public class MenuangebotVM : DetailVM, IContains
{
    #region private Values
    private Menuangebot myOriginal = new Menuangebot();
    private Menuangebot myValue = new Menuangebot();
    #endregion // private Values

    #region Properties

    #region Detail Properties
    public int Id { get { return myOriginal.Id; } }
    public bool? Result { get; private set; }
    public string Beschreibung { get { return "Einrichtung"; } }

    [Required]
    [RegularExpression(@"^[0-9a-zA-ZäöüßÄÖÜß''-'\s]{2,40}$")]
    [Default(myOriginal.Name)] //<-- added
    public string NameD
    {
        get { return myValue.Name; }
        set
        {
            myValue.Name = value;
            RaisePropertyChanged(() => Reg(() => NameD));
        }
    }

    [Required]
    [RegularExpression(@"^[0-9a-zA-ZäöüßÄÖÜß''-'\s]{2,25}$")]
    [Default(myOriginal.Anzeigetext)] //<-- added
    public string AnzeigetextD
    {
        get { return myValue.Anzeigetext; }
        set
        {
            myValue.Anzeigetext = value;
            RaisePropertyChanged(() => Reg(() => AnzeigetextD));
        }
    }

    [Required]
    [Default(myOriginal.Preis)] //<-- added
    public decimal PreisD
    {
        get { return myValue.Preis; }
        set
        {
            myValue.Preis = value;
            RaisePropertyChanged(() => Reg(() => PreisD));
        }
    }    
    #endregion //Detail Properties
    #endregion //Properties

// more code

}
+5
4
public class ViewModel:INotifyPropertyChanged
    {
        private string initialText;
        public ViewModel()
        {
            Text = "ABCD";
            initialText = Text;
            DefaultBorder = true;
        }
        private string text;
        public string Text
        {
            get { return text; }
            set { text = value;
            if (value == initialText)
                DefaultBorder = true;
            else
                DefaultBorder = false;
                Notify("Text"); }
        }

        private bool defaultBorder;
        public bool DefaultBorder
        {
            get { return defaultBorder; }
            set { defaultBorder = value; Notify("DefaultBorder"); }
        }

        private void Notify(string propertyName)
        {
            if(PropertyChanged!=null)
                PropertyChanged(this,new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

public class MyConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value != null && value is bool && !(bool)value)
                return new SolidColorBrush(Colors.Orange);
            else
                return new SolidColorBrush(Colors.Navy); //Or default whatever you want

        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

    <Window.Resources>
    <local:MyConverter x:Key="MyConverter"/>
</Window.Resources>
<Grid>
    <TextBox BorderThickness="4" BorderBrush="{Binding DefaultBorder, Converter={StaticResource MyConverter}}" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>

Text ViewModel, TextBox, Text. , Text Bool , bool , . , .

0

, /UserControl IsDirtyDependencyProperty IsDirtyColorDependencyProperty ( ). .

0

, , , , TextBox Border. , , BorderBrush Windows 8.

. , Border border BorderBrush.

0

Attached-

public static class TextChangedAttachedBehavior
{
    public static bool GetChanged(DependencyObject obj)
    {
        return (bool)obj.GetValue(ChangedProperty);
    }

    public static void SetChanged(DependencyObject obj, string value)
    {
        obj.SetValue(ChangedProperty, value);
    }

    public static readonly DependencyProperty ChangedProperty =
        DependencyProperty.RegisterAttached("Changed", typeof(bool),
        typeof(TextChangedAttachedBehavior), new PropertyMetadata(false, HookupBehavior));

    private static void HookupBehavior(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var textBox = d as TextBox;
        if (textBox == null) 
            return;
        textBox.TextChanged += TextBoxOnTextChanged;
    }

    private static void TextBoxOnTextChanged(object sender, TextChangedEventArgs args)
    {
        var textBox = sender as TextBox;
        if (textBox == null)
            return;
        textBox.BorderBrush = new SolidColorBrush(Colors.Orange);
    }
}

xaml

<TextBox Text="{Binding myText1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" TextChangedAttachedBehavior.Changed = "True" />
0

All Articles