"UpdateSourceTrigger=PropertyChanged" equivalent for a Windows Phone 7 TextBox - c#

Is there a way to get a TextBox in Windows Phone 7 to update the Binding as the user types each letter rather than after losing focus?
Like the following WPF TextBox would do:
<TextBox Text="{Binding Path=TextProperty, UpdateSourceTrigger=PropertyChanged}"/>

Silverlight for WP7 does not support the syntax you've listed. Do the following instead:
<TextBox TextChanged="OnTextBoxTextChanged"
Text="{Binding MyText, Mode=TwoWay,
UpdateSourceTrigger=Explicit}" />
UpdateSourceTrigger = Explicit is a smart bonus here. What is it? Explicit: Updates the binding source only when you call the UpdateSource method. It saves you one extra binding set when the user leaves the TextBox.
In C#:
private void OnTextBoxTextChanged( object sender, TextChangedEventArgs e )
{
TextBox textBox = sender as TextBox;
// Update the binding source
BindingExpression bindingExpr = textBox.GetBindingExpression( TextBox.TextProperty );
bindingExpr.UpdateSource();
}

I like using an attached property. Just in case you're into those little buggers.
<toolkit:DataField Label="Name">
<TextBox Text="{Binding Product.Name, Mode=TwoWay}" c:BindingUtility.UpdateSourceOnChange="True"/>
</toolkit:DataField>
And then the backing code.
public class BindingUtility
{
public static bool GetUpdateSourceOnChange(DependencyObject d)
{
return (bool)d.GetValue(UpdateSourceOnChangeProperty);
}
public static void SetUpdateSourceOnChange(DependencyObject d, bool value)
{
d.SetValue(UpdateSourceOnChangeProperty, value);
}
// Using a DependencyProperty as the backing store for …
public static readonly DependencyProperty
UpdateSourceOnChangeProperty =
DependencyProperty.RegisterAttached(
"UpdateSourceOnChange",
typeof(bool),
typeof(BindingUtility),
new PropertyMetadata(false, OnPropertyChanged));
private static void OnPropertyChanged (DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var textBox = d as TextBox;
if (textBox == null)
return;
if ((bool)e.NewValue)
{
textBox.TextChanged += OnTextChanged;
}
else
{
textBox.TextChanged -= OnTextChanged;
}
}
static void OnTextChanged(object s, TextChangedEventArgs e)
{
var textBox = s as TextBox;
if (textBox == null)
return;
var bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);
if (bindingExpression != null)
{
bindingExpression.UpdateSource();
}
}
}

Not through binding syntax, no, but it's easy enough without. You have to handle the TextChanged event and call UpdateSource on the binding.
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
((TextBox) sender).GetBindingExpression( TextBox.TextProperty ).UpdateSource();
}
This can be converted into an attached behavior as well pretty easily.

In TextChanged event call UpdateSource().
BindingExpression be = itemNameTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();

You can write your own TextBox Behavior to handle Update on TextChanged:
This is my sample to PasswordBox but you can simple change it to handle any property of the any object.
public class UpdateSourceOnPasswordChangedBehavior
: Behavior<PasswordBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.PasswordChanged += OnPasswordChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.PasswordChanged -= OnPasswordChanged;
}
private void OnPasswordChanged(object sender, RoutedEventArgs e)
{
AssociatedObject.GetBindingExpression(PasswordBox.PasswordProperty).UpdateSource();
}
}
Ussage:
<PasswordBox x:Name="Password" Password="{Binding Password, Mode=TwoWay}" >
<i:Interaction.Behaviors>
<common:UpdateSourceOnPasswordChangedBehavior/>
</i:Interaction.Behaviors>
</PasswordBox>

UpdateSourceTrigger=Explicit doesnt work for me, hence Im using custom class derivated from TextBox
public class TextBoxEx : TextBox
{
public TextBoxEx()
{
TextChanged += (sender, args) =>
{
var bindingExpression = GetBindingExpression(TextProperty);
if (bindingExpression != null)
{
bindingExpression.UpdateSource();
}
};
}
}

It's just one line of code!
(sender as TextBox).GetBindingExpression(TextBox.TextProperty).UpdateSource();
You can create a generic TextChanged event (for example "ImmediateTextBox_TextChanged") in the code behind of your page, and than link it to any TextBox in the page.

I took Praetorian's answer and made an extension class that inherits TextBox so you don't have to muddle up your view's code behind with this behavior.
C-Sharp:
public class TextBoxUpdate : TextBox
{
public TextBoxUpdate()
{
TextChanged += OnTextBoxTextChanged;
}
private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
TextBox senderText = (TextBox)sender;
BindingExpression bindingExp = senderText.GetBindingExpression(TextBox.TextProperty);
bindingExp.UpdateSource();
}
}
VisualBasic:
Public Class TextBoxUpdate : Inherits TextBox
Private Sub OnTextBoxTextChanged(sender As Object, e As TextChangedEventArgs) Handles Me.TextChanged
Dim senderText As TextBox = DirectCast(sender, TextBox)
Dim bindingExp As BindingExpression = senderText.GetBindingExpression(TextBox.TextProperty)
bindingExp.UpdateSource()
End Sub
End Class
Then call like this in XAML:
<local:TextBoxUpdate Text="{Binding PersonName, Mode=TwoWay}"/>

Related

WPF FlowDocumentScrollViewer automatically scroll to bottom of the document

I have a UserControl that the XAML looks like:
<Grid>
<FlowDocumentScrollViewer Name="ProvisionStatusMonitor" Document="{Binding Document}" SourceUpdated="OnSourceUpdated"/>
</Grid>
The code-behind looks like
private void OnSourceUpdated(object sender, DataTransferEventArgs e)
{
FlowDocumentScrollViewer docViewer = (FlowDocumentScrollViewer) sender;
var scrollViewer = (ScrollViewer)docViewer.Template
.FindName("ProvisionStatusMonitor", docViewer);
scrollViewer.ScrollToEnd();
}
But this doesn't seem to work. When the document is changed I was thinking that this event should fire and I could automatically scroll to the bottom of the document. What am I missing?
You may attach a PropertyChanged event handler to the view model in a DataContextChanged handler in the view.
Assuming that StatusView is a UserControl that contains the FlowDocumentScrollViewer shown in the question, it could look like this:
public StatusView()
{
InitializeComponent();
DataContextChanged += StatusViewDataContextChanged;
}
private void StatusViewDataContextChanged(
object sender, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue is INotifyPropertyChanged oldViewModel)
{
oldViewModel.PropertyChanged -= ViewModelPropertyChanged;
}
if (e.NewValue is INotifyPropertyChanged newViewModel)
{
newViewModel.PropertyChanged += ViewModelPropertyChanged;
}
}
private void ViewModelPropertyChanged(
object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Document")
{
var docViewer = ProvisionStatusMonitor;
var scrollViewer = (ScrollViewer)docViewer.Template
.FindName("PART_ContentHost", docViewer);
scrollViewer.ScrollToEnd();
}
}

c# Format all TextBox when lostfocus

I created public void that formating all the textbox in grid to currency
My code
public void FormatinTextBox()
{
foreach (Control ctrl in MainGrid.Children)
{
if (ctrl.GetType() == typeof(TextBox))
{
double amount = 0.0d;
if (Double.TryParse(((TextBox)ctrl).Text, NumberStyles.Currency, null, out amount))
((TextBox)ctrl).Text = amount.ToString("C");
else
((TextBox)ctrl).Text = String.Empty;
}
}
}
and it's working perfect if i put this code to event handler of MainGrid_Loaded.
but i want to run this code for each time i leave textbox (lostFocus).
i prefer to fire this code with xaml on each textbox, i dont know if it possible to do that. this is my code in xaml for one of my textbox
<TextBox x:Name="Nose" HorizontalAlignment="Left" Height="24" Margin="710,209,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Width="110" BorderThickness="0,0,0,1" Text="100" LostFocus="Nose_LostFocus"/>
if it's not possible, how can i run this code on back code
private void Nose_LostFocus(object sender, RoutedEventArgs e)
{
}
If you bind the Text property to a source property, you could apply a StringFormat to the binding:
public partial class MainWindow : Window
{
public MainWindow ()
{
InitializeComponent();
Nose.DataContext = this;
}
public decimal? Text { get; set; }
}
XAML:
<TextBox x:Name="Nose" Text="{Binding Text, StringFormat=C}"/>
But if you wan't some custom logic, you could use an event handler. You would implement it pretty much the same way as you did before:
private void Nose_LostFocus(object sender, RoutedEventArgs e)
{
TextBox textBox = (TextBox)sender;
double amount;
textBox.Text = (double.TryParse(textBox.Text, out amount)) ? amount.ToString("C") : string.Empty;
}

Dependency property not working on a interaction behavior

I am trying to add a dependency property IsActive to a RubberBandBehavior I found on CodeProject, so that I can activate and deactivate it from my ViewModel. The code below does not give any compile error and runs, but the value does not seem to be set correctly, when I check the line with the comment \\ this line is always 'false'.
The modified class RubberBandBehavior:
public class RubberBandBehavior : Behavior<ListBox>
{
public static readonly DependencyProperty IsActiveProperty =
DependencyProperty.Register("IsActive", typeof(bool), typeof(RubberBandBehavior),
new PropertyMetadata(IsActiveProperty_Changed));
private static void IsActiveProperty_Changed(DependencyObject sender,
DependencyPropertyChangedEventArgs args)
{
// This gets called _after_ OnAttached!
}
public bool IsActive
{
get { return (bool)GetValue(IsActiveProperty); }
set { SetValue(IsActiveProperty, value); }
}
protected override void OnAttached()
{
AssociatedObject.Loaded += new System.Windows.RoutedEventHandler(AssociatedObject_Loaded);
base.OnAttached();
}
void AssociatedObject_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
bool a = IsActive; // this line is always 'false'
RubberBandAdorner band = new RubberBandAdorner(AssociatedObject);
AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer(AssociatedObject);
adornerLayer.Add(band);
}
protected override void OnDetaching()
{
AssociatedObject.Loaded -= new System.Windows.RoutedEventHandler(AssociatedObject_Loaded);
base.OnDetaching();
}
}
In my XAML I have this:
<i:Interaction.Behaviors>
<behavior:RubberBandBehavior IsActive="True"/>
</i:Interaction.Behaviors>
My plan is then to bind to my ViewModel like this, but first I need to get the above sorted out:
<i:Interaction.Behaviors>
<behavior:RubberBandBehavior IsActive="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.IsEditable}"/>
</i:Interaction.Behaviors>
You wont get this value in the AssociatedObject.Loaded event. You need to use the IsActiveProperty_Changed handler in your code example to get the correct value of this property

Binding list to textbox won't update if ObservableCollection

I'm creating a custom control in WPF. I bind a List<IMyInterface> to a dependency property. This in turn binds again to a ListBox which shows all the items as expected.
I now want to bind 1 item from this list to a Textblock, so I bind the entire list to the textblock. I have a converter in this which extracts the single item I want.
It has worked fine but for a few reasons, I want to use ObservableCollection instead of List
Oddly, when I change a value in my ObservabaleCollection at run time, the value is shown in the ListBox (success) but not in my textblock. The converter is not even hit!
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.Errors = new ObservableCollection<IEventDetail>();
this.Errors.CollectionChanged += Errors_CollectionChanged;
var bw = new BackgroundWorker();
bw.DoWork += ((o, e) =>
{
System.Threading.Thread.Sleep(1500);
Dispatcher.Invoke(() =>
{
this.Errors.Add(new MyEvents("example of some detail", "Failed title"));
});
System.Threading.Thread.Sleep(2500);
Dispatcher.Invoke(() =>
{
this.Errors.Add(new MyEvents("Another example", "Failed title 2"));
});
});
bw.RunWorkerAsync();//background worker for testing/debugging only
}
private void Errors_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged("Errors");
}
private ObservableCollection<IEventDetail> _errors;
public ObservableCollection<IEventDetail> Errors
{
get
{
return this._errors;
}
set
{
this._errors = value;
OnPropertyChanged("Errors");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
And the xaml is simply
<local:Notify Events="{Binding Errors}" DockPanel.Dock="Right"/>
As you can see, I've tried to use the CollectionChanged event to then force fire the INotifyPropertyChanged, but it's firing in my Converter yet the ListBox is updating fine (so I know the binding is fine)
This is the UserControls xaml
<TextBlock Text="{Binding Path=Events, RelativeSource={RelativeSource AncestorLevel=1, AncestorType=UserControl}, Mode=Default, Converter={StaticResource MostRecentConverter}}" Grid.Row="0" />
<ListBox ItemsSource="{Binding Path=Events, RelativeSource={RelativeSource AncestorLevel=1,AncestorType=UserControl}, Mode=Default}" Grid.Row="1">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding EventTitle}" Style="{StaticResource txtBckRed}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Do I need to do something else?
As mentioned in the comments TextBlock it bound only to Events property change (not its items) so it won't trigger unless new instance of collection is created. Reacting to INotifyCollectionChanged is characteristic to ItemsSource property.
Solution 1
Leave everything as it is at the moment just give TextBlock some name
<TextBlock Text="{Binding ...}" x:Name="myTextBlock"/>
and subscribe to CollectionChanged event inside your UserControl where you manually force binding target to update
public partial class MyUserControl : UserControl
{
public static readonly DependencyProperty EventsProperty =
DependencyProperty.Register("Events",
typeof(IEnumerable),
typeof(MyUserControl),
new PropertyMetadata(EventsPropertyChanged));
private static void EventsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MyUserControl)d).EventsPropertyChanged(e);
}
private void EventsPropertyChanged(DependencyPropertyChangedEventArgs args)
{
var newCollection = args.NewValue as INotifyCollectionChanged;
if (newCollection != null)
newCollection.CollectionChanged += (s, e) => myTextBlock.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
}
public IEnumerable Events
{
get { return (IEnumerable)GetValue(EventsProperty); }
set { SetValue(EventsProperty, value); }
}
}
Solution 2
Create your own collection class inherited from ObservableCollection<T> with custom property that would do what your converter does
public class MyObservableCollection<T> : ObservableCollection<T>
{
private string _convertedText;
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
this.ConvertedText = ...; // <- do here what your IValueConverter does
}
public string ConvertedText
{
get { return _convertedText; }
private set
{
_convertedText = value;
OnPropertyChanged(new PropertyChangedEventArgs("ConvertedText"));
}
}
}
and bind TextBlock.Text to Events.ConvertedText property instead, without need for converter

Click event on calendar control

I have added a click event on the calendar control. But with my implementation, this event don't work.
My code in Cal.cs control:
#region click
public static RoutedEvent ClickEvent =
EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Cal));
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
}
protected virtual void OnClick()
{
RoutedEventArgs args = new RoutedEventArgs(ClickEvent, this);
RaiseEvent(args);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
OnClick();
}
#endregion
XAML code :
<Calen:Cal x:Name="Calendar" Margin="0,50,0,0" Click="Calendar_Click"/>
C# code :
private void Calendar_Click(object sender, RoutedEventArgs e)
{
string t = "";
}
I don't found any solution. I don't know why this code don't work correctly.
Can you help me with this problem please ?
You need to set the DataContext to point to the class that contains the code behind.
<UserControl>
DataContext="{Binding RelativeSource={RelativeSource Self}}">
</UserControl>
This is necessary because unfortunately, by default, the DataContext is not set up correctly in WPF.
For more info on DataContext, see ReSharper WPF error: "Cannot resolve symbol "MyVariable" due to unknown DataContext".

Categories

Resources