I am new to WPF,
In my mainWindow I have multiple TextBox, so whenever a user enters different inputs in these textbox I want to implement those changes in the code behind, as soon as user leaves the focus of the textbox.
For example, my textBox looks like this:
<TextBox Name="SpiralAngleTextBox"
Grid.Column="1" Grid.Row="4"
Margin="5,5,5,5" SelectedText="0"/>
I am not looking to do any kind of input validation. What I want is to trigger some calculations or call a function whenever the TextBox leaves focus after contents of TextBox is updated.
You can write an EventHandler
<TextBox Name="SpiralAngleTextBox"
Grid.Column="1" Grid.Row="4"
Margin="5,5,5,5" SelectedText="0" LostFocus="SpiralAngleTextBox_LostFocus"/>
and in the xaml.cs
private void SpiralAngleTextBox_LostFocus(object sender, RoutedEventArgs e)
{
foo();
}
If you just want it to do stuff when the textbox content changes you can try something like this:
<TextBox Name="SpiralAngleTextBox"
Grid.Column="1" Grid.Row="4"
Margin="5,5,5,5" SelectedText="0" LostFocus="SpiralAngleTextBox_LostFocus"
TextChanged="SpiralAngleTextBox_TextChanged"/>
and in the xaml.cs
bool hasChanged;
private void SpiralAngleTextBox_LostFocus(object sender, RoutedEventArgs e)
{
if(hasChanged)
foo();
hasChanged = false;
}
private void SpiralAngleTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
hasChanged = true;
}
All you need to do is to bind to TextBox.Text
<TextBox Text="{Binding MyProperty}" />
Where MyProperty is some property in your code-behind. This is because TextBox.Text updates on lost focus (UpdateSourceTrigger=LostFocus by default.) You can learn more here: https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-control-when-the-textbox-text-updates-the-source
Related
So i have the TextBox:
<TextBox Controls:TextBoxHelper.ClearTextButton="True"
LostFocus="textboxNewValueCell_LostFocus"
TextChanged="textboxNewValueCell_TextChanged"/>
And when press on Clear button i want to catch the event.
Is it possible ?
I did not find any event
The ClearTextButton simply calls Clear() on the TextBox. There is no specific event raised. The best you can do is to handle the TextChanged event:
private void textboxNewValueCell_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox tb = sender as TextBox;
if (tb.Text.Length == 0)
{
//the TextBox was cleared and the Button was maybe clicked...
}
}
First, give your TextBox a name. Then, create a click event on the Button. when the click event fires, handle the clearing of the TextBox in the CodeBehind.
<Grid>
<TextBox x:Name="MyTextBox" Text="Some Text"/>
<Button x:Name="ClearButton" Click="ClearButton_Click"/>
</Grid>
private void ClearButton_Click(object sender, RoutedEventArgs e)
{
MyTextBox.Text = string.Empty;
}
I googled regarding this question but couldn't gather any information and I was wondering if it is possible for an attached behavior to handle an attached event??
I've an event declared in a class and a behavior that I am attaching to a TextBox control, the event will be raised when a button is clicked. I added the handler for this event in my behavior and wrote the logic in the event handler, but it is not executed. So, I was wondering if it is possible for an attached behavior to handle an attached event or not?
class ResetInputEventClass
{
public static readonly RoutedEvent ResetInputEvent = EventManager.RegisterRoutedEvent("ResetInput",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(ResetInputEventClass));
public static void AddResetInputEventHandler(DependencyObject d, RoutedEventHandler handler)
{
UIElement uie = d as UIElement;
if (uie == null)
{
return;
}
uie.AddHandler(ResetInputEventClass.ResetInputEvent, handler);
}
public static void RemoveResetInputEventHandler(DependencyObject d, RoutedEventHandler handler)
{
UIElement uie = d as UIElement;
if (uie == null)
{
return;
}
uie.RemoveHandler(ResetInputEventClass.ResetInputEvent, handler);
}
}
That is my Event class and this is how I am handling it in the behavior
public class MyBehavior : Behavior<TextBoxBase>
{
public MyBehavior()
{
// Insert code required on object creation below this point.
}
protected override void OnAttached()
{
base.OnAttached();
// Insert code that you would want run when the Behavior is attached to an object.
ResetInputEventClass.AddResetInputEventHandler(AssociatedObject, OnResetInputEvent);
}
protected override void OnDetaching()
{
base.OnDetaching();
// Insert code that you would want run when the Behavior is removed from an object.
ResetInputEventClass.RemoveResetInputEventHandler(AssociatedObject, OnResetInputEvent);
}
private void OnResetInputEvent(Object o, RoutedEventArgs e)
{
//Logic
}
}
Here is my XAML Code:
<Grid x:Name="LayoutRoot">
<StackPanel>
<TextBox Margin="5" Text="Bye" TextWrapping="Wrap" Width="150">
<i:Interaction.Behaviors>
<local:MyBehavior/>
</i:Interaction.Behaviors>
</TextBox>
<TextBox Margin="5" Text="Bye" TextWrapping="Wrap" Width="150">
<i:Interaction.Behaviors>
<local:MyBehavior/>
</i:Interaction.Behaviors>
</TextBox>
<Button Name="MyButton" Content="Save" Width="50" Height="25" Click="MyButton_Click"/>
</StackPanel>
</Grid>
and I am raising the event in the click event of my button
private void MyButton_Click(object sender, RoutedEventArgs e)
{
RoutedEventArgs eventArgs = new RoutedEventArgs(ResetInputEventClass.ResetInputEvent,e.OriginalSource);
RaiseEvent(eventArgs);
}
Your problem is simple. The textbox is registered for the event, but the parent of the textbox is raising it. Thus the handler is never called. You can change the event to make it a Tunneling event instead of Bubbling. Or you can get a handle on your textbox (give it a name and reference in code behind). And have it raise the event.
<Grid x:Name="LayoutRoot">
<StackPanel>
<TextBox Margin="5" x:Name="byeTextBox" Text="Bye" TextWrapping="Wrap" Width="150">
<i:Interaction.Behaviors>
<local:MyBehavior/>
</i:Interaction.Behaviors>
</TextBox>
<Button Name="MyButton" Content="Save" Width="50" Height="25" Click="MyButton_Click"/>
</StackPanel>
</Grid>
Your code-behind should then look like this
private void MyButton_Click(object sender, RoutedEventArgs e)
{
RoutedEventArgs eventArgs = new RoutedEventArgs(ResetInputEventClass.ResetInputEvent,e.OriginalSource);
byeTextBox.RaiseEvent(eventArgs);
}
and that should fix your problem.
Of course it is possible. Show me your XAML and I ll tel you how an attached event triggers an attached behavior.
Edited:
I dont see the need why you using attached behavior and attached events because you could do everything in code behind.
Here is how to do everything in code behind:
Here is XAML without attached properties:
<Grid>
<StackPanel>
<TextBox x:Name="txtBox" Margin="5" Text="Bye" TextWrapping="Wrap" Width="150"/>
<Button Name="MyButton" Content="Save" Width="50" Height="25" Click="MyButton_Click"/>
</StackPanel>
</Grid>
This is code behind.
public MainWindow()
{
InitializeComponent();
}
private void MyButton_Click(object sender, RoutedEventArgs e)
{
this.txtBox.Text = "hello";
}
Because you have set Name property on TextBox and Button you can access them from code behind in your Window.cs and you can write your handler easly.
Here is how you can do everything with attached properties:
This is the new XAML for the solution with attached properties. I had to create my custom Interaction because the one you are using is Expression Blend or silverlight and not pure WPF.
<Grid x:Name="LayoutRoot">
<StackPanel i:Interaction.Behaviour="True">
<TextBox x:Name="txtBox" Margin="5" Text="Bye" TextWrapping="Wrap" Width="150"/>
<Button Name="MyButton" Content="Save" Width="50" Height="25" Click="MyButton_Click"/>
</StackPanel>
</Grid>
I had to set Behavior on True because the default value is false and when value is not equal to the old then the propery changed event will be called with my custom logic like this:
private void MyButton_Click(object sender, RoutedEventArgs e)
{
RoutedEventArgs eventArgs = new RoutedEventArgs(ResetInputEventClass.ResetInputEvent,e.OriginalSource);
RaiseEvent(eventArgs);
}
public class Interaction : DependencyObject
{
// Using a DependencyProperty as the backing store for Behaviour. This enables animation, styling, binding, etc...
public static readonly DependencyProperty BehaviourProperty =
DependencyProperty.RegisterAttached("Behaviour", typeof(bool), typeof(Interaction), new PropertyMetadata(false, new PropertyChangedCallback(OnBehaviourChanged)));
private static void OnBehaviourChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
StackPanel sp = (StackPanel)d;
sp.Dispatcher.BeginInvoke(new Action(() =>
{
TextBox tb = VisualTreeHelper.GetChild(sp, 0) as TextBox;
ResetInputEventClass.AddResetInputHandler(sp, new RoutedEventHandler((o, a) =>
{
// Do here whatever you want, call your custom expressions.
tb.Text = "hello";
}));
}), System.Windows.Threading.DispatcherPriority.Background);
}
}
Inside property changed event which will be called as I already mentioned when I change false to true. I wait till everything is intialized by telling the dispatcher to execute my code when application is in background. Then I find the TextBox and inject the handler which will be called when you trigger ResetInput event.
This is very complicated solution but it will work with attached events and attached properties.
I highly recommend you to use the code behind for this scenario.
Also you made a mistake inside your ResetInputEventClass class. Add and Remove methods are not correctly spelled.
This is how you should have written them:
public static void AddResetInputHandler(DependencyObject d, RoutedEventHandler handler)
{
UIElement uie = d as UIElement;
if (uie == null)
{
return;
}
uie.AddHandler(ResetInputEventClass.ResetInputEvent, handler);
}
public static void RemoveResetInputHandler(DependencyObject d, RoutedEventHandler handler)
{
UIElement uie = d as UIElement;
if (uie == null)
{
return;
}
uie.RemoveHandler(ResetInputEventClass.ResetInputEvent, handler);
}
Have fun, I hope I helped you out.
You could also have achieved this with Commands
I ve a list from sharepoint and i collect from this list an hyperlink.
As i want my textbox to be like an hyperlink I ve added an event on mousedown to open this hyperlink, My concern is how to collect this hyperlink in the codebehind with the sender.
For the moment I've just hide this hyperlink in the tooltip maybe i can manage this differently any suggestion will be grantly appreciated.
My point so far, i don't know how to get this tooltip in the code behind.
Thanks
My XAML Code :
<ListBox Name="ListboxTips" ItemsSource="{Binding}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=Picture}" Height="20"></Image>
<TextBlock MouseDown="TextBlock_MouseDown_URL" TextDecorations="Underline"
Margin="10,10,20,10" Width="160" TextWrapping="Wrap"
Text="{Binding Path=TitleTip}"
ToolTip="{Binding Path=URL}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My code behind :
foreach (SPSClient.ListItem item in TipsList)
{
var tips = new Tips();
tips.TitleTip = item.FieldValues.Values.ElementAt(1).ToString();
tips.App = item.FieldValues.Values.ElementAt(4).ToString();
// get the Hyperlink field URL value
tips.URL = ((FieldUrlValue)(item["LinkDoc"])).Url.ToString();
//should collect the description of the url
//tips.URLdesc = ((FieldUrlValue)(item["LinkDoc"])).Description.ToString();
tips.Picture = item.FieldValues.Values.ElementAt(4).ToString();
colTips.Add(tips);
}
ListboxTips.DataContext = colTips;
....
private void TextBlock_MouseDown_URL(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//string test = (ToolTip)(sender as Control).ToString();
System.Diagnostics.Process.Start("http://www.link.com");
//System.Diagnostics.Process.Start(test);
}
Thanks a lot,
You can just access the property directly. It is not elegant, but will work!
private void TextBlock_MouseDown_URL(object sender, MouseButtonEventArgs e)
{
TextBlock txtBlock = sender as TexBlock;
// just access the property
string url = txtBlock.ToolTip as string;
}
A more elegant approach might be to use a Button, Hyperlink or something that exposes a Command, so that you can bind the 'click' action to a command on your view model that performs the action you wish to execute.
usually you stick any data you want to trespass somewhere to Tag attribute.
<TextBlock .. Tag="{Binding Path=URL}" />
This is easily retrievable as a public property:
private void TextBlock_MouseDown_URL(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var tb = sender as TextBlock;
if(tb != null)
{
var neededUrl = tb.Tag;
}
}
I am using a ToggleButton in a WPF window:
<ToggleButton Height="37"
HorizontalAlignment="Left"
Margin="485.738,254.419,0,0"
VerticalAlignment="Top"
Width="109"
IsEnabled="True"
Checked="toggleAPDTimeoutErr_Checked"
Unchecked="toggleAPDTimeoutErr_Unchecked">Timeout</ToggleButton>
I have two events that I am monitoring, but this is done in two different code behind handlers. How can this be done in only one?
I will have many ToggleButtons, and the code can get large.
You can attach a single click event of your ToggleButton and in its handler you can check the ToggleButton IsChecked property by type casting the sender object in your handler like this -
private void ToggleButton_Click(object sender, RoutedEventArgs e)
{
if((sender as ToggleButton).IsChecked)
{
// Code for Checked state
}
else
{
// Code for Un-Checked state
}
}
Xaml:
<ToggleButton Height="37" HorizontalAlignment="Left" Margin="485.738,254.419,0,0" VerticalAlignment="Top" Width="109" IsEnabled="True" Click="ToggleButton_Click">Timeout</ToggleButton>
You should not use Click event as some answers suggest, because it will not work when the property IsChecked is changed by code or any other event than mouse (keyboard, animation..). This is simply a bug.
Instead you can use the same handler for both Checked and Unchecked and do action depending on IsChecked property.
<ToggleButton
Checked="toggleButton_IsCheckedChanged"
Unchecked="toggleButton_IsCheckedChanged" />
Try this
private void tBtn_super_Click(object sender, RoutedEventArgs e)
{
if (tBtn_super.IsChecked == true)
{
MessageBox.Show("True");
}
else
{
MessageBox.Show("False");
}
}
I have a dialog that pops up over the main screen (it's actually a user control that appears on the page as per the application demo from Billy Hollis) in my application that has data from the main screen to be edited. The main screen is read only.
The problem I have is that when I change the data in the dialog, the data on the main screen updates as well. Clearly they are bound to the same object, but is there a way to stop the binding update until I click save in my dialog?
You could use a BindingGroup :
...
<StackPanel Name="panel">
<StackPanel.BindingGroup>
<BindingGroup Name="bindingGroup"/>
</StackPanel.BindingGroup>
<TextBox Text="{Binding Foo}"/>
<TextBox Text="{Binding Bar}"/>
<Button Name="btnSubmit" Content="Submit" OnClick="btnSubmit_Click"/>
<Button Name="btnCancel" Content="Cancel" OnClick="btnCancel_Click"/>
</StackPanel>
...
Code behind :
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
panel.BindingGroup.BeginEdit();
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
panel.BindingGroup.CommitEdit();
panel.BindingGroup.BeginEdit();
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
panel.BindingGroup.CancelEdit();
panel.BindingGroup.BeginEdit();
}
Have a look at the Binding.UpdateSourceTrigger property.
You can set the Binding in your dialog like so
<TextBox Name="myTextBox"
Text={Binding Path=MyProperty, UpdateSourceTrigger=Explicit} />
And then call the UpdateSource method in your button save event
myTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
Once you've called UpdateSource the source object will be updated with the value from the TextBox
I also choose to use BindingGroup. But instead of BeginEdit() / CommitEdit() / CancelEdit() pattern I call UpdateSource() explicitly on all the bindings associated with BindingGroup. This approach allows me to add only one event handler instead of 3.
private void OkButton_Click(object sender, RoutedEventArgs e)
{
CommitChanges();
DialogResult = true;
Close();
}
private void CommitChanges()
{
foreach (var bindingExpression in this.BindingGroup.BindingExpressions)
{
bindingExpression.UpdateSource();
}
}