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();
}
}
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 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
Now the cursor is focusing in the TextBox. If i click on the the Button (RemoveLostFocus),The TextBox's Lost focus event get fired. But What i need is , Lost Focus event of TextBox should not fire. is there any way to do so ?.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
txtUserName.Focus();
}
private void UserName_LostFocus(object sender, RoutedEventArgs e)
{
if (txtUserName.Text.Length < 1)
{
MessageBox.Show("UserName should not be empty");
}
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
this.Close();
anotherWindow.Show();
}
You will want to use the FocusManager attached properties to apply the focus to the TextBox when the Button focus changes
Example:
<StackPanel>
<TextBox Name="txtbx" />
<Button Content="Click Me!" FocusManager.FocusedElement="{Binding ElementName=txtbx}"/>
</StackPanel>
With this solution the The TextBox will always be focused even when the Button is pressed and the TextBox LostFocus event will not be fired
you could Set Focusable="False" on the Button. here is a link of the answer enter link description here
Set Focusable Property
<Button Focusable="False" />
In the Click-Event of you Button you can do something like
this.textBox.Focus();
If your lostfocus-method looks like this:
private void UserName_LostFocus(object sender, RoutedEventArgs e){ ... }
you can prevent the lostfocus with the following code:
this.textBox.LostFocus -= UserName_LostFocus;
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 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");
}
}