TextChanged Event is not getting fired - MVVM Xamarin Forms - xaml

I have a Entry control inside DataTemplate of Bindable StackLayout. I have implemented TextChanged event of Entry control with Behavior but event is not getting fired.
This is xaml code
<Frame Padding="20,14,10,10"
Grid.Column="1"
BorderColor="LightGray">
<Entry MaxLength="5"
Keyboard="Numeric"
HeightRequest="42">
<Entry.Behaviors>
<helpers:EventToCommandBehavior Command="{Binding RefreshCommand}"
EventName="TextChanged"></helpers:EventToCommandBehavior>
</Entry.Behaviors>
</Entry>
</Frame>
Behavior classes in a single file
namespace Product.Helpers
{
public class EventToCommandBehavior : BehaviorBase<VisualElement>
{
Delegate eventHandler;
public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(EventToCommandBehavior), null);
public string EventName
{
get { return (string)GetValue(EventNameProperty); }
set { SetValue(EventNameProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
protected override void OnAttachedTo(VisualElement bindable)
{
base.OnAttachedTo(bindable);
RegisterEvent(EventName);
}
protected override void OnDetachingFrom(VisualElement bindable)
{
DeregisterEvent(EventName);
base.OnDetachingFrom(bindable);
}
static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
{
var behavior = (EventToCommandBehavior)bindable;
if (behavior.AssociatedObject == null) return;
string oldEventName = (string)oldValue;
string newEventName = (string)newValue;
behavior.DeregisterEvent(oldEventName);
behavior.RegisterEvent(newEventName);
}
void RegisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name)) return;
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));
MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");
eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
eventInfo.AddEventHandler(AssociatedObject, eventHandler);
}
void DeregisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name) || eventHandler == null)
return;
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));
eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);
eventHandler = null;
}
void OnEvent(object sender, object eventArgs)
{
if (Command == null) return;
object resolvedParameter;
resolvedParameter = eventArgs;
if (Command.CanExecute(resolvedParameter))
Command.Execute(resolvedParameter);
}
}
public class BehaviorBase<T> : Behavior<T> where T : BindableObject
{
public T AssociatedObject { get; private set; }
protected override void OnAttachedTo(T bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;
if (bindable.BindingContext != null)
BindingContext = bindable.BindingContext;
bindable.BindingContextChanged += OnBindingContextChanged;
}
protected override void OnDetachingFrom(T bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= OnBindingContextChanged;
AssociatedObject = null;
}
void OnBindingContextChanged(object sender, EventArgs e)
{
OnBindingContextChanged();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
BindingContext = AssociatedObject.BindingContext;
}
}
}
ViewModel Code for Command
public ICommand RefreshCommand
{
get
{
return new Command(async (args) =>
{
Refresh();
});
}
}
public void Refresh()
{
var textbox = Subtotal;
}
From here I have take the code csharpcorer - behavior. How can I make it work ?

The reason you are not able to find it is because of the context.
Give your current ContentPage a name :
<ContentPage
...
x:Name="currentPage"/>
Then your Entry would look something like:
<Entry MaxLength="5"
Keyboard="Numeric"
HeightRequest="42">
<Entry.Behaviors>
<helpers:EventToCommandBehavior Command="{Binding BindingContext.RefreshCommand, Source={x:Reference currentPage}}"
EventName="TextChanged"></helpers:EventToCommandBehavior>
</Entry.Behaviors>
</Entry>

Related

display picker when clicking in imagebutton xamarin

i want to display a picker (or list) when i click in my imageButton but it didn't work, but when i create only a picker , it worked.
Xaml
<StackLayout Orientation="Horizontal" x:Name="stacklayout">
<Entry Placeholder="préciser l'entité correspondante"
ClearButtonVisibility="WhileEditing" x:Name="entit"/>
<ImageButton Source="list.png" WidthRequest="30" HeightRequest="30" x:Name="listEntité" Clicked="listEntité_Clicked"/>
Xaml.cs
private void listEntité_Clicked(object sender, EventArgs e)
{
Picker p = new Picker();
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://192.168.1.3:3000/api/adepApi/GetCurrencyLists");
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await httpClient.GetAsync("http://192.168.1.3:3000/api/adepApi/GetCurrencyLists");
var content = await response.Content.ReadAsStringAsync();
ResponseDataD EL = JsonConvert.DeserializeObject<ResponseDataD>(content);
p.ItemsSource= EL.Data.DeviseList;
stacklayout.Children.Add(p.ItemsSource);
}
class DeviseModel
public class DeviseModel
{
public class DeviseL
{
// public string devis;
[JsonProperty("Label")]
public string Devis { get; set; }
[JsonProperty("Value")]
public int id { get; set; }
}
public class ResponseDataD
{
public RootModelDevise Data;
}
public class RootModelDevise : INotifyPropertyChanged
{
List<DeviseL> deviseList;
[JsonProperty("list")]
public List<DeviseL> DeviseList
{
get { return deviseList; }
set
{
if (deviseList != value)
{
deviseList = value;
OnPropertyChanged();
}
}
}
DeviseL itemDevise;
public DeviseL ItemDevise
{
get { return itemDevise; }
set
{
if (itemDevise != value)
{
itemDevise = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}}
i have an error in the line
stacklayout.Children.Add(p.ItemsSource);
under p.itemsSource :unable to convert from 'System.Collections.Ilist' to 'Xamarin.Forms.View'
i have tried to add picker by imageButton click event , but nothing appears.
Firstly, please confirm that the EL.Data.DeviseList have list value, you have list binding to Picker, then adding picker to current contentPage.
public partial class Page16 : ContentPage
{
private List<string> list;
public Page16()
{
InitializeComponent();
list = new List<string>();
list.Add("test 1");
list.Add("test 2");
list.Add("test 3");
list.Add("test 4");
}
private void imagebutton1_Clicked(object sender, EventArgs e)
{
Picker p = new Picker();
p.ItemsSource = list;
stacklayout.Children.Add(p);
}
}
<StackLayout x:Name="stacklayout">
<ImageButton
x:Name="imagebutton1"
Clicked="imagebutton1_Clicked"
HeightRequest="30"
Source="plu3.png"
WidthRequest="30" />
</StackLayout>
Update:
private void imagebutton1_Clicked(object sender, EventArgs e)
{
Picker p = new Picker();
p.SelectedIndexChanged += P_SelectedIndexChanged;
p.ItemsSource = devs;
p.ItemDisplayBinding = new Binding("Devis");
stacklayout.Children.Add(p);
}
private void P_SelectedIndexChanged(object sender, EventArgs e)
{
Picker p = sender as Picker;
DeviseL selectitem = p.SelectedItem as DeviseL;
entit.Text =selectitem.Devis;
}

How to change when the Data Binding (back to Source) happens in Xamarin Forms?

I have multiple Entry views in my page connecting to a View Model using Data Binding. Everything works as intended except I need to calculate a values as the user enters it.
public decimal DownPayment
{
get => this.loanValues.DownPayment;
set
{
this.loanValues.DownPayment = value;
this.CalculateValues();
}
}
public decimal TradeInPrice
{
get => this.loanValues.TradeInPrice;
set
{
this.loanValues.TradeInPrice = value;
this.CalculateValues();
}
}
// Other properties
Now the problem is the property is set whenever user is entering the value. Is there anyway to change the event on which the Data Binding happens (i.e on Completed instead of TextChanged)?
I see there is UpdateSourceEventName property in XAML binding but I don't know why it doesn't work:
<Entry Placeholder="Down Payment ($)" Text="{Binding DownPayment, UpdateSourceEventName=Completed}" />
UPDATE: The documentation of UpdateSourceEventName says it's For internal use by the Xamarin.Forms platform
You could use EventToCommandBehavior to convert the Event Completed of Entry to Command and handle the logic in ViewModel .
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace App18
{
public class BehaviorBase<T> : Behavior<T> where T : BindableObject
{
public T AssociatedObject { get; private set; }
protected override void OnAttachedTo(T bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;
if (bindable.BindingContext != null)
{
BindingContext = bindable.BindingContext;
}
bindable.BindingContextChanged += OnBindingContextChanged;
}
protected override void OnDetachingFrom(T bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= OnBindingContextChanged;
AssociatedObject = null;
}
void OnBindingContextChanged(object sender, EventArgs e)
{
OnBindingContextChanged();
}
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
BindingContext = AssociatedObject.BindingContext;
}
}
}
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Windows.Input;
using Xamarin.Forms;
namespace App18
{
public class EventToCommandBehavior : BehaviorBase<View>
{
Delegate eventHandler;
public static readonly BindableProperty EventNameProperty = BindableProperty.Create("EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
public static readonly BindableProperty CommandProperty = BindableProperty.Create("Command", typeof(ICommand), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create("CommandParameter", typeof(object), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty InputConverterProperty = BindableProperty.Create("Converter", typeof(IValueConverter), typeof(EventToCommandBehavior), null);
public string EventName
{
get { return (string)GetValue(EventNameProperty); }
set { SetValue(EventNameProperty, value); }
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public IValueConverter Converter
{
get { return (IValueConverter)GetValue(InputConverterProperty); }
set { SetValue(InputConverterProperty, value); }
}
protected override void OnAttachedTo(View bindable)
{
base.OnAttachedTo(bindable);
RegisterEvent(EventName);
}
protected override void OnDetachingFrom(View bindable)
{
DeregisterEvent(EventName);
base.OnDetachingFrom(bindable);
}
void RegisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));
}
MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");
eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
eventInfo.AddEventHandler(AssociatedObject, eventHandler);
}
void DeregisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}
if (eventHandler == null)
{
return;
}
EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));
}
eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);
eventHandler = null;
}
void OnEvent(object sender, object eventArgs)
{
if (Command == null)
{
return;
}
object resolvedParameter;
if (CommandParameter != null)
{
resolvedParameter = CommandParameter;
}
else if (Converter != null)
{
resolvedParameter = Converter.Convert(eventArgs, typeof(object), null, null);
}
else
{
resolvedParameter = eventArgs;
}
if (Command.CanExecute(resolvedParameter))
{
Command.Execute(resolvedParameter);
}
}
static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
{
var behavior = (EventToCommandBehavior)bindable;
if (behavior.AssociatedObject == null)
{
return;
}
string oldEventName = (string)oldValue;
string newEventName = (string)newValue;
behavior.DeregisterEvent(oldEventName);
behavior.RegisterEvent(newEventName);
}
}
}
in xaml
</StackLayout>
<Entry WidthRequest="100" Text="{Binding Value1,Mode=OneWayToSource}" Keyboard="Numeric" >
<Entry.Behaviors>
<local:EventToCommandBehavior EventName="Unfocused" Command="{Binding CompletedCommand}" />
</Entry.Behaviors>
</Entry>
<Entry WidthRequest="100" Text="{Binding Value2,Mode=OneWayToSource}" Keyboard="Numeric">
<Entry.Behaviors>
<local:EventToCommandBehavior EventName="Unfocused" Command="{Binding CompletedCommand}" />
</Entry.Behaviors>
</Entry>
<Label WidthRequest="100" Text="{Binding Sum}" TextColor="Black" />
</StackLayout>
in ViewModel
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
double value1;
public double Value1
{
get
{
return value1;
}
set
{
if (value1 != value)
{
value1 = value;
NotifyPropertyChanged("Value1");
}
}
}
double value2;
public double Value2
{
get
{
return value2;
}
set
{
if (value2 != value)
{
value2 = value;
NotifyPropertyChanged("Value2");
}
}
}
private string sum;
public string Sum
{
get { return sum; }
set
{
if (sum != value)
{
sum = value;
NotifyPropertyChanged("Sum");
}
}
}
public ICommand CompletedCommand { get; set; }
public MyViewModel()
{
CompletedCommand = new Command(()=> {
Sum = (Value1 + Value2).ToString();
});
}
}

Change of color de my custom enter once is clicked my button

im working with custon entry rendered, i need to hear from xaml in my custom render when i clicked my button
i have this code in my xaml
<local:MyEntry eventRefresh="true">
when i clicked my button this function is actived
private async void Execute(object sender)
{
var entry = ((MyEntry)view);
entry.eventRefresh = "false";
but my EntryRendered donot hear the change
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var element = Element as MyEntry;
You should define the property eventRefresh as Bindable Property .
in your custom Entry
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace xxx
{
public class MyEntry:Entry,INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public static readonly BindableProperty eventRefreshProperty = BindableProperty.Create("eventRefresh", typeof(bool), typeof(MyEntry), true,propertyChanged:(obj,oldValue,newValue)=> {
//var entry = obj as MyEntry;
// entry.Text = newValue.ToString();
});
bool refresh;
public bool eventRefresh
{
get { return refresh; }
set {
if(refresh !=value)
{
refresh = value;
NotifyPropertyChanged("eventRefresh");
}
}
}
public MyEntry()
{
}
}
}
in xaml
<StackLayout VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand">
<local:MyEntry eventRefresh="{Binding Refresh}" BackgroundColor="{Binding BGcolor}" WidthRequest="200" HeightRequest="50" />
<Button Command="{Binding ClickCommand}" />
</StackLayout>
in View Model
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Color color;
public Color BGcolor
{
get { return color; }
set
{
if (color != value)
{
color = value;
NotifyPropertyChanged("BGcolor");
}
}
}
bool refresh;
public bool Refresh
{
get { return refresh; }
set
{
if (refresh != value)
{
refresh = value;
NotifyPropertyChanged("Refresh");
}
}
}
public ICommand ClickCommand { get; set; }
public MyViewModel()
{
BGcolor = Color.LightPink;
ClickCommand = new Command(()=> {
BGcolor = Color.Red;
});
}
}
in Custom Renderer
using System.ComponentModel;
using Android.Content;
using xxx;
using xxx.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly:ExportRenderer(typeof(MyEntry),typeof(NyEntryRenderer))]
namespace xxx.Droid
{
public class NyEntryRenderer : EntryRenderer
{
public NyEntryRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
Element.TextChanged += Element_TextChanged;
}
}
private void Element_TextChanged(object sender, TextChangedEventArgs e)
{
// var content = Element.Text;
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == MyEntry.BackgroundColorProperty.PropertyName)
{
// will been invoked when click button
}
}
}
}
Make your view model like this.
public class YourViewModel
{
public Command command
{
get
{
return new Command(() => {
//Change here button background colors
BackgroundColor = Color.Green;
});
}
}
private _backgroundColor = Color.White;
public Color BackgroundColor
{
get { return _backgroundColor;}
set
{
if (value == _backgroundColor)
return;
_backgroundColor = value;
NotifyOnPropertyChanged(nameof(BackgroundColor));
}
}
}
Your XAML
<local:MyEntry Text="{Binding Password}" Placeholder="Enter" />
<Button Text="send" Command="{Binding command}" BackgroundColor="{Binding BackgroundColor}"></Button>

How To send Command Parameter In Mvvm Silverlight 5

I Have Implemented Icommand in my mvvm architecture as SimpleCommand.cs
public class SimpleCommand<T1, T2> : ICommand
{
private Func<T1, bool> canExecuteMethod;
private Action<T2> executeMethod;
public SimpleCommand(Func<T1, bool> canExecuteMethod, Action<T2> executeMethod)
{
this.executeMethod = executeMethod;
this.canExecuteMethod = canExecuteMethod;
}
public SimpleCommand(Action<T2> executeMethod)
{
this.executeMethod = executeMethod;
this.canExecuteMethod = (x) => { return true; };
}
public bool CanExecute(T1 parameter)
{
if (canExecuteMethod == null) return true;
return canExecuteMethod(parameter);
}
public void Execute(T2 parameter)
{
if (executeMethod != null)
{
executeMethod(parameter);
}
}
public bool CanExecute(object parameter)
{
return CanExecute((T1)parameter);
}
public void Execute(object parameter)
{
Execute((T2)parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
And Implemented this ICommand in my viewModel As follows:
private ICommand printCommand;
public ICommand PrintCommand
{
get { return printCommand; }
set { printCommand = value; }
}
myviewmodel() // in Constructor of ViewModel
{
this.PrintCommand = new SimpleCommand<Object, EventToCommandArgs>(ExecutePrintCommand);
}
}
On XAML : I Have Called Command="{Binding PrintCommand}"
<Button Content="Print Button" Command="{Binding PrintCommand}" Width="120" Height="25" Margin="3"></Button>
It Works Perfectly...
But When I try To Send CommandParameter to Command As:
<Button Content="Print Button" Command="{Binding PrintCommand}" Width="120" Height="25" Margin="3" CommandParameter="{Binding ElementName=grdReceipt}"></Button>
Then Command is not executing.
and Giving Error as :
Unable to cast object of type 'System.Windows.Controls.Grid' to type
'PropMgmt.Shared.EventToCommandArgs'.
Please Help.Thanks In Advance.
The problem is this part in your SimpleCommand implementation
public void Execute(object parameter){
Execute((T2)parameter);
}
T2 in your case is of type EventToCommandArgs, but as parameter
CommandParameter="{Binding ElementName=grdReceipt}"
you are passing a System.Windows.Controls.Grid, which results in the exception you have mentioned.
Also is your implementation of the command incorrect. The parameter passed to CanExecute and Execute are the same object. The following implementation works using only one generic type.
public class SimpleCommand<T1> : ICommand
{
private Func<T1, bool> canExecuteMethod;
private Action<T1> executeMethod;
public SimpleCommand(Func<T1, bool> canExecuteMethod, Action<T1> executeMethod)
{
this.executeMethod = executeMethod;
this.canExecuteMethod = canExecuteMethod;
}
public SimpleCommand(Action<T1> executeMethod)
{
this.executeMethod = executeMethod;
this.canExecuteMethod = (x) => { return true; };
}
public bool CanExecute(T1 parameter)
{
if (canExecuteMethod == null) return true;
return canExecuteMethod(parameter);
}
public void Execute(T1 parameter)
{
if (executeMethod != null)
{
executeMethod(parameter);
}
}
public bool CanExecute(object parameter)
{
return CanExecute((T1)parameter);
}
public void Execute(object parameter)
{
Execute((T1)parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
Using this implementation you can define:
this.PrintCommand = new SimpleCommand<object>(ExecutePrintCommand);
Since your ExecutePrintCommand gets an object as parameter you need to do a type check or cast to the type you need. In your case you will get a System.Windows.Controls.Grid.
public void ExecutPrintCommand(object parameter) {
System.Windows.Controls.Grid grid = parameter as System.Windows.Controls.Grid;
if (grid != null) {
// do something with the Grid
}
else {
// throw exception or cast to another type you need if your command should support more types.
}
}

Access named item inside ItemTemplate

I have the following scenario:
<Button Click="ClickHandler">Click Me</Button>
<TextBox x:Name="MyInputTextBox" />
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox x:Name="MyRepeatTextBox" Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If MyRepeatTextBox.Text == MyInputTextBox.Text I want to change the color of MyRepeatTextBox.Background to Green. If MyRepeatTextBox.Text is blank I want to change the color to red. How would I implement the button click handler?
Not sure a button event would be the best for this.
As DataTriggers are again not brought outside the WPF world, those are out. There's no IMultiValueConverter either. Behaviours are currently out, but there is a nice codeplex project for them. I would use that
public class MatchTextForegroundBehaviour : Behavior<TextBox>
{
private TextBox _attachedElement;
public static readonly DependencyProperty MatchForegroundProperty =
DependencyProperty.Register("MatchForeground", typeof(Brush),
typeof(MatchTextForegroundBehaviour),
new PropertyMetadata(new SolidColorBrush(Colors.Green), OnMatchForegroundChanged));
public static readonly DependencyProperty FallbackForegroundProperty =
DependencyProperty.Register("FallbackForeground", typeof(Brush),
typeof(MatchTextForegroundBehaviour),
new PropertyMetadata(new SolidColorBrush(Colors.Black), OnFallbackForegroundChanged));
public static readonly DependencyProperty TextToMatchProperty =
DependencyProperty.Register("TextToMatch", typeof(string),
typeof(MatchTextForegroundBehaviour),
new PropertyMetadata(null, OnTextToMatchChanged));
public Brush MatchForeground
{
get { return (Brush)GetValue(MatchForegroundProperty); }
set { SetValue(MatchForegroundProperty, value); }
}
public Brush FallbackForeground
{
get { return (Brush)GetValue(FallbackForegroundProperty); }
set { SetValue(FallbackForegroundProperty, value); }
}
public string TextToMatch
{
get { return (string)GetValue(TextToMatchProperty); }
set { SetValue(TextToMatchProperty, value); }
}
/// <summary>
/// Event when the behavior is attached to a element.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
_attachedElement = AssociatedObject;
if(_attachedElement != null)
_attachedElement.TextChanged += (s,e) => ChangeForeground();
}
private static void OnMatchForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (MatchTextForegroundBehaviour)d;
behavior.ChangeForeground();
}
private static void OnTextToMatchChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (MatchTextForegroundBehaviour)d;
behavior.ChangeForeground();
}
private static void OnFallbackForegroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (MatchTextForegroundBehaviour)d;
behavior.ChangeForeground();
}
private void ChangeForeground()
{
if (_attachedElement == null) return;
if (string.IsNullOrEmpty(TextToMatch)) return; // change foreground to red?
if (_attachedElement.Text == TextToMatch)
{
_attachedElement.Foreground = MatchForeground;
}
else
{
_attachedElement.Foreground = FallbackForeground;
}
}
}
And the xaml
<TextBox x:Name="MyRepeatTextBox" Text="{Binding}"
TextToMatch="{Binding Text, ElementName=MyInputTextBox}"
FallbackForeground="Black" MatchForeground="Green" />
If a button click event is really how you want to do this, you can try the following. I have not compiled this against WinRT, but I think everything used is in WinRT.
Use the following ExtensionMethod
internal static class TreeExtensions
{
public static T GetChildElement<T>(this DependencyObject element) where T :FrameworkElement
{
if (element == null) return null;
if(element.GetType() == typeof(T)) return (T)element;
T childAsT = null;
int count = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(element, i);
childAsT = child.GetChildElement<T>();
if (childAsT != null) break;
}
return childAsT;
}
}
Inside the button click event you would do the following (assuming you gave the ItemsControl a name of itemsControl:
foreach (var item in itemsControl.Items)
{
var element = itemsControl.ItemContainerGenerator.ContainerFromItem(item);
var textblock = element.GetChildElement<TextBlock>();
if (textblock != null)
{
if (textblock.Text == MyInputTextBox.Text)
textblock.Foreground = new SolidColorBrush(Colors.Green);
else
textblock.Foreground = new SolidColorBrush(Colors.Black);
}
}