Returning bool from ViewModel to View binding - c#

I want a button to change the visibility of a label once i click it.
xaml View:
<local:ButtonRenderer Text="Connect" BackgroundColor="#6DCFF6" TextColor="White" Command="{Binding viewTemperature}" CornerRadius="10" WidthRequest="200" IsVisible="{Binding !isConnecting}"/>
<Label Text="PlaceholderText" TextDecorations="Underline" TextColor="White" Margin="0,5,0,0" HorizontalTextAlignment="Center" IsVisible="{Binding !isConnecting}"/>
ViewModel
viewTemperature = new Command(async () =>
{
isConnecting = true;
await _navigation.PushModalAsync(new TemperaturePage());
}) ;
public bool isConnecting
{
get
{
return _isConnecting;
}
set
{
_isConnecting = value;
PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(_isConnecting.ToString()));
}
}
I have put breakpoints in my code, and the isConnected is being changed to true in my viewmodel. However, the visibility of my label is not changed. I suspect that PropertyChanged isn't supposed to change bool values?

You cannot do IsVisible="{Binding !isConnecting}", this will not work.
You can either make a InvertBoolConverter, or, the simplier option, use Triggers. Here is a sample:
<Label Text="PlaceholderText" TextDecorations="Underline" TextColor="White" Margin="0,5,0,0" HorizontalTextAlignment="Center"
IsVisible="{Binding isConnecting}">
<Label.Triggers>
<DataTrigger TargetType="Label" Binding="{Binding isConnecting}" Value="True">
<Setter Property="IsVisible" Value="False" />
</DataTrigger>
<DataTrigger TargetType="Label" Binding="{Binding isConnecting}" Value="False">
<Setter Property="IsVisible" Value="True" />
</DataTrigger>
</Label.Triggers>
</Label>

You can improve your code in ViewModel
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private bool isconnecting ;
public bool isConnecting
{
get
{
return isconnecting;
}
set
{
if (isconnecting != value)
{
isconnecting = value;
NotifyPropertyChanged();
}
}
}

Related

Binding doesn't appear on label on XAML

I have these lines of code on my XAML
<ContentPage.BindingContext>
<x:Reference Name="messagesPage" />
</ContentPage.BindingContext>
....
<Label Text="{Binding ConversationPartner.contactName[0]}" FontSize="Title" TextColor="Black"
VerticalOptions="Center" FontAttributes="Bold" HorizontalOptions="StartAndExpand">
<Label.Triggers>
<DataTrigger TargetType="Label" Binding="{Binding ConversationPartner.contactID[1], Converter={StaticResource isViewerConverter}}" Value="False">
<Setter Property="Text" Value="{Binding ConversationPartner.contactName[1]}"/>
</DataTrigger>
</Label.Triggers>
</Label>
What I want to happen is that a name on the label represented by ConversationPartner.contactName[0] must appear on my application but it doesn't.
Here's the code behind
public partial class MessagesPage : ContentPage
{
DataClass dataClass = DataClass.GetInstance;
public ICommand CloseMsg => new Command(async () => await Navigation.PopModalAsync(true));
public ICommand SendCommand => new Command(Send);
ContactModel ConversationPartner;
public MessagesPage(ContactModel input)
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
ConversationsList = new ObservableCollection<ConversationModel>();
ConversationPartner = input;
/// some cloud firestore code here
}
}
Found a solution.
I made an observable collection so that I could bind easily.
My code behind:
public MessagesPage(ContactModel input)
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
ConversationsList = new ObservableCollection<ConversationModel>();
convoers = new ObservableCollection<string> {input.contactName[0], input.contactName[1], input.contactID[1] };
ConversationPartner = input;
...
}
then in my XAML,
<Label Text="{Binding convoers[0]}" FontSize="Title" TextColor="Black"
VerticalOptions="Center" FontAttributes="Bold" HorizontalOptions="StartAndExpand">
<Label.Triggers>
<DataTrigger TargetType="Label" Binding="{Binding convoers[2], Converter={StaticResource isViewerConverter}}" Value="False">
<Setter Property="Text" Value="{Binding convoers[1]}"/>
</DataTrigger>
</Label.Triggers>
</Label>

How to make a TextBox read-only based on the item selected in the ComboBox?

I would be so greatful if you could help meee, thx!
HERE IS THE XAML
<ComboBox Margin="8" Name="cmbox" SelectionChanged="cmbox_SelectionChanged" >
<ComboBoxItem IsSelected="True">ESCALONADO</ComboBoxItem>
<ComboBoxItem>INTEGRAL</ComboBoxItem>
</ComboBox>
<TextBox IsReadOnly="{Binding Testcmb}" Grid.Column="1" Margin="8"/>
HERE IS A LITTLE BIT OF THE CODE BEHIND
I tried to attribute the value of the property Testcmb based on the selected item on the combobox, and then make the bindin to the textbox to turn it as a IsReadOnly="true". Is the sintax right?
private void cmbox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(cmbox.SelectedIndex > 0)
{
(Object).Testcmb = true;
}
else
{
(Object).Testcmb = false;
}
}
private bool _testcmb;
public bool Testcmb
{
get { return _testcmb; }
set
{
if (this._testcmb != value)
{
this._testcmb = value;
this.NotifyPropertyChanged("Testcmb");
};
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
Welcome to StackOverflow. Your syntax is indeed correct, however you are mixing data binding with event handlers and all of this within view's code-behind.
You can do what you want using Style which is more clear for this kind of UI logic, as for me. Here is XAML-only solution:
<ComboBox x:Name="cbox">
<ComboBoxItem IsSelected="True" Content="ESCALONADO"/>
<ComboBoxItem Content="INTEGRAL" />
</ComboBox>
<TextBox Text="Some text...">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="IsReadOnly" Value="True" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIndex, ElementName=cbox}" Value="0">
<Setter Property="IsReadOnly" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

Bind Button IsEnabled property to DataTemplate items state and one additional condition

I have an object with editable parameters collection which are bound as a ItemsSource to ItemsControl, and a property which checks if all parameter values are ok. This property bound to button's IsEnabled.
I also want to disable the button when any of textbox has validation error (Validation.HasError == true).
Thanks in advance.
XAML:
<Window x:Class="MyWPFTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<ItemsControl ItemsSource="{Binding Path=MyObject.Parameters}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
<TextBox Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button IsEnabled="{Binding Path=MyObject.IsParametersOkay}">OK</Button>
</StackPanel>
</Window>
Code:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
namespace MyWPFTest
{
public partial class MainWindow : Window
{
ObjectWithParameters _MyObject = new ObjectWithParameters();
public ObjectWithParameters MyObject { get { return _MyObject; } }
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
}
public class ObjectWithParameters : INotifyPropertyChanged
{
ObservableCollection<Parameter> _Parameters = new ObservableCollection<Parameter>();
public ObservableCollection<Parameter> Parameters { get { return _Parameters; } }
public event PropertyChangedEventHandler PropertyChanged;
public ObjectWithParameters()
{
var p1 = new Parameter("Parameter 1", 0); p1.PropertyChanged += ParameterChanged; Parameters.Add(p1);
var p2 = new Parameter("Parameter 2", 0); p2.PropertyChanged += ParameterChanged; Parameters.Add(p2);
}
void ParameterChanged(object sender, PropertyChangedEventArgs e)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsParametersOkay"));
}
public bool IsParametersOkay
{
get { return Parameters.FirstOrDefault(p => p.Value < 0) == null; }
}
}
public class Parameter : INotifyPropertyChanged
{
double val;
public double Value
{
get { return val; }
set { val = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("Value")); }
}
public string Name { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public Parameter(string name, double value) { Name = name; Value = value; }
}
}
Check out MultiTriggers.
<Style.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" Value="#EEEEEE" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasItems" Value="false" />
<Condition Property="Width" Value="Auto" />
</MultiTrigger.Conditions>
<Setter Property="MinWidth" Value="120"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="HasItems" Value="false" />
<Condition Property="Height" Value="Auto" />
</MultiTrigger.Conditions>
<Setter Property="MinHeight" Value="95"/>
</MultiTrigger>
</Style.Triggers>
This is the way I solved the problem. May be it's not a very elegant solution, but it works.
I added a new property IsFormOkay to MainWindow class, which checks both controls and parameters validity. Then I bound Button.IsEnabled to this property and added TextChanged event for TextBox to notify about IsFormOkay.
Here is code added to MainWindow:
public event PropertyChangedEventHandler PropertyChanged;
public bool IsFormOkay { get { return IsValid(Items) && MyObject.IsParametersOkay; } }
public bool IsValid(DependencyObject obj)
{
if (Validation.GetHasError(obj)) return false;
for (int i = 0, n = VisualTreeHelper.GetChildrenCount(obj); i < n; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (!IsValid(child)) return false;
}
return true;
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("IsFormOkay"));
}
And changes to XAML:
<StackPanel>
<ItemsControl x:Name="Items" ItemsSource="{Binding Path=MyObject.Parameters}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
<TextBox TextChanged="TextBox_TextChanged" Text="{Binding Path=Value, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button IsEnabled="{Binding Path=IsFormOkay}" Content="OK" />
</StackPanel>

Disable ValidatesOnDataErrors if CheckBox is Checked WPF

I am trying to disable ValidatesOnDataErrors on a TextBox if a certain checkbox is checked.
I have tried placing a trigger on textbox to enable or disable validation based on the checkbox seems like the trigger gets hit but does not disable validation. I am using IDataErrorInfo for validation in the .cs code. Here is the code I have tried, this has been a headache so hope you can help.
.xaml
<TextBox Name="txtFoundERTReading" Height="23" Canvas.Left="125" TextWrapping="Wrap" Canvas.Top="136" Width="120">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=cbFoundERTReading, Path=IsChecked}" Value="False">
<Setter Property="Text" Value="{Binding Found.ERTReading, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=cbFoundERTReading, Path=IsChecked}" Value="True">
<Setter Property="TextBox.IsEnabled" Value="False" />
<Setter Property="Text" Value="{Binding Found.ERTReading, Mode=TwoWay, ValidatesOnDataErrors=False, UpdateSourceTrigger=PropertyChanged}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Instead of changing the ValidatesOnDataErrors property at run time, the best approach is to have a boolean property in viewmodel and do validation only if it is true. The boolean property can bound to IsChecked property of a Checkbox.
public string Name
{
get { return name; }
set { name = value; RaisePropertyChanged("Name"); }
}
public string this[string columnName]
{
get
{
if (CanValidate)
{
if (columnName == "Name")
{
if (!ValidateName())
{
return "Error";
}
}
}
return "";
}
}
private bool canValidate;
public bool CanValidate
{
get { return canValidate; }
set { canValidate = value; RaisePropertyChanged("CanValidate"); RaisePropertyChanged("Name");}
}
private bool ValidateName()
{
if (String.IsNullOrEmpty(Name))
{
return false;
}
return true;
}
The XAML looks like below,
<StackPanel>
<TextBox Margin="5" Text="{Binding Name, Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged}"/>
<CheckBox Margin="5" Content="Can validate" IsChecked="{Binding CanValidate, Mode=TwoWay}"/>
</StackPanel>
Use this,
Validation.ErrorTemplate="{x:Null}"

Best practice for changing TextBox Border color?

Suppose you have a View with multiple Texboxes like this
<TextBox Text="{Binding myText1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" />
each already containing some text. If the user changes this text the Textbox Border should change to Orange and if he undos his changes it should get it's default color.
At the moment I do 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 generic / simpler way to do this?
Edit
I am already using IDataErrorInfo + System.ComponentModel.DataAnnotations for Error validation. Maybe there is a similar way in this case but I didn't found anything useful to reduce my xaml and code to an minimum.
EDIT 2.0
i think you doesn't really understand my problem so i will provide a better sample of how it actual looks like:
View Xaml (no codebehind)
<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
}
what i excspect should be something like
View
<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
}
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>
Here I have property Text in ViewModel that is bound to TextBox and at the beginning i preserved the initial Text value . and then whenever user type i compare it in setter of Text property and set Bool Property accordingly , this bool property will specify which color to Bind using converter.Ignore minor issues hope you will get an idea.
You can probably turn this to a custom control/UserControl and add a IsDirtyDependencyProperty, and the IsDirtyColorDependencyProperty (or attached dependency property). That way you replace all your textboxes with this and not have to repeat the code over and over.
Coincidentally I just had a problem almost identical to yours, and I solved it by wrapping the TextBox in a Border. Furthermore, this also solved a problem whereby BorderBrush colours cannot be changed on Windows 8 machines.
I would thus recommend this approach. The code is quite straightforward, whereby you just add your textBox to the Border and change the border's BorderBrush property.
You can use Attached behavior for that
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);
}
}
And than in xaml
<TextBox Text="{Binding myText1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}" TextChangedAttachedBehavior.Changed = "True" />

Categories

Resources