I have a class EmployeeViewModel with 2 properties "FirstName" and "LastName". The class also has a dictionary with the changes of the properties. (The class implements INotifyPropertyChanged and IDataErrorInfo, everything is fine.
In my view there is a textbox:
<TextBox x:Name="firstNameTextBox" Text="{Binding Path=FirstName}" />
How can I change the background color of the textbox, if the original value changed? I thought about creating a trigger which sets the background color but to what should I bind? I don't want to created an additional property for every control which holds the state wheter the one was changed or not.
Thx
Just use a MultiBinding with the same property twice but have Mode=OneTime on one of the bindings. Like this:
Public Class MVCBackground
Implements IMultiValueConverter
Public Function Convert(ByVal values() As Object, ByVal targetType As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements System.Windows.Data.IMultiValueConverter.Convert
Static unchanged As Brush = Brushes.Blue
Static changed As Brush = Brushes.Red
If values.Count = 2 Then
If values(0).Equals(values(1)) Then
Return unchanged
Else
Return changed
End If
Else
Return unchanged
End If
End Function
Public Function ConvertBack(ByVal value As Object, ByVal targetTypes() As System.Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object() Implements System.Windows.Data.IMultiValueConverter.ConvertBack
Throw New NotImplementedException()
End Function
End Class
And in the xaml:
<TextBox Text="{Binding TestText}">
<TextBox.Background>
<MultiBinding Converter="{StaticResource BackgroundConverter}">
<Binding Path="TestText" />
<Binding Path="TestText" Mode="OneTime" />
</MultiBinding>
</TextBox.Background>
</TextBox>
No extra properties or logic required and you could probably wrap it all into your own markup extension. Hope that helps.
You will need to use a value converter (converting string input to color output) and the simplest solution involves adding at least one more property to your EmployeeViewModel. You need to make some sort of a Default or OriginalValue property, and compare against that. Otherwise, how will you know what the "original value" was? You cannot tell if the value changed unless there is something holding the original value to compare against.
So, bind to the text property and compare the input string to the original value on the view model. If it has changed, return your highlighted background color. If it matches, return the normal background color. You will need to use a multi-binding if you want to compare the FirstName and LastName together from a single textbox.
I have constructed an example that demonstrates how this could work:
<Window x:Class="TestWpfApplication.Window11"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpfApplication"
Title="Window11" Height="300" Width="300"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<local:ChangedDefaultColorConverter x:Key="changedDefaultColorConverter"/>
</Window.Resources>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock>Default String:</TextBlock>
<TextBlock Text="{Binding Path=DefaultString}" Margin="5,0"/>
</StackPanel>
<Border BorderThickness="3" CornerRadius="3"
BorderBrush="{Binding ElementName=textBox, Path=Text, Converter={StaticResource changedDefaultColorConverter}}">
<TextBox Name="textBox" Text="{Binding Path=DefaultString, Mode=OneTime}"/>
</Border>
</StackPanel>
And here is the code-behind for the Window:
/// <summary>
/// Interaction logic for Window11.xaml
/// </summary>
public partial class Window11 : Window
{
public static string DefaultString
{
get { return "John Doe"; }
}
public Window11()
{
InitializeComponent();
}
}
Finally, here is the converter you use:
public class ChangedDefaultColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string text = (string)value;
return (text == Window11.DefaultString) ?
Brushes.Transparent :
Brushes.Yellow;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And even though I wrapped a border around the TextBox (because I think that looks a little better), the Background binding can be done exactly the same way:
<TextBox Name="textBox" Text="{Binding Path=DefaultString, Mode=OneTime}"
Background="{Binding ElementName=textBox, Path=Text, Converter={StaticResource changedDefaultColorConverter}}"/>
If you're using the MVVM paradigm, you should consider the ViewModels as having the role of adapters between the Model and the View.
It is not expected of the ViewModel to be completely agnostic of the existence of a UI in every way, but to be agnostic of any specific UI.
So, the ViewModel can (and should) have the functionality of as many Converters as possible. The practical example here would be this:
Would a UI require to know if a text is equal to a default string?
If the answer is yes, it's sufficient reason to implement an IsDefaultString
property on a ViewModel.
public class TextViewModel : ViewModelBase
{
private string theText;
public string TheText
{
get { return theText; }
set
{
if (value != theText)
{
theText = value;
OnPropertyChanged("TheText");
OnPropertyChanged("IsTextDefault");
}
}
}
public bool IsTextDefault
{
get
{
return GetIsTextDefault(theText);
}
}
private bool GetIsTextDefault(string text)
{
//implement here
}
}
Then bind the TextBox
like this:
<TextBox x:Name="textBox" Background="White" Text="{Binding Path=TheText, UpdateSourceTrigger=LostFocus}">
<TextBox.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding IsTextDefault}" Value="False">
<Setter Property="TextBox.Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Resources>
</TextBox>
This propagates text back to the ViewModel upon TextBox
losing focus, which causes a recalculation of the IsTextDefault
. If you need to do this a lot of times or for many properties, you could even cook up some base class like DefaultManagerViewModel
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With