I am using an attached property to limit the input into textboxes and textblocks to either numeric or alphabetic. Now I would like to apply this attached property to a datagridtextcolumn.
I tried the following:
<DataGridTextColumn Header="Max" Width="50"
Binding="{Binding Path=Max, Mode=TwoWay"
Helper:InputService.NumericOnly="True">
and something like this:
<DataGridTextColumn.ElementStyle>
<Style>
<Setter Property="Helper:InputService.NumericOnly" Value="True"/>
</Style>
</DataGridTextColumn.ElementStyle>
But it does not work.
How do I do it right?
My InputService contains the NumericOnly property:
public static readonly DependencyProperty NumericOnlyProperty = DependencyProperty.RegisterAttached(
"NumericOnly",
typeof(bool),
typeof(InputService),
new UIPropertyMetadata(false, OnNumericOnlyChanged));
public static bool GetNumericOnly(DependencyObject d)
{
return (bool)d.GetValue(NumericOnlyProperty);
}
public static void SetNumericOnly(DependencyObject d, bool value)
{
d.SetValue(NumericOnlyProperty, value);
}
private static void OnNumericOnlyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
bool isNumericOnly = (bool)e.NewValue;
if (d is TextBox)
{
var textBox = (TextBox)d;
if (isNumericOnly)
{
textBox.PreviewTextInput += BlockNonDigitCharacters;
textBox.PreviewKeyDown += ReviewKeyDown;
}
else
{
textBox.PreviewTextInput -= BlockNonDigitCharacters;
textBox.PreviewKeyDown -= ReviewKeyDown;
}
}
else if (d is TextBlock)
{
var textBlock = (TextBlock)d;
if (isNumericOnly)
{
textBlock.PreviewTextInput += BlockNonDigitCharacters;
textBlock.PreviewKeyDown += ReviewKeyDown;
}
else
{
textBlock.PreviewTextInput -= BlockNonDigitCharacters;
textBlock.PreviewKeyDown -= ReviewKeyDown;
}
}
}
private static void BlockNonDigitCharacters(object sender, TextCompositionEventArgs e)
{
foreach (char ch in e.Text)
{
if (Char.IsDigit(ch))
{
e.Handled = true;
}
}
}
Ok, this it what works for me:
<DataGridTextColumn.EditingElementStyle>
<Style TargetType="TextBox">
<Setter Property="Helper:InputService.NumericOnly" Value="True"/>
</Style>
</DataGridTextColumn.EditingElementStyle>
Your property implementation expects only to be set on a TextBox or TextBlock. I would suggest that you put a breakpoint in your code and check what type of control it's actually being set on - I suspect you'll find it's the parent container of your cell, rather than the text control itself.
EDIT: based on your comment, you probably want to include the following in your binding:
Binding="{Binding Path=Max, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
This will cause the binding to refresh every time the property changes, where the default for most input controls is to fire when a control loses focus.
Related
I'm trying to get all the DataGrid Expanders to open and close using a open/close button like the example below. Problem is when I'm using a single boolean to bind to this will break when I'm opening/closing a single expander.
I'm using a ListCollectionView to set the GroupingDescription.
I guess I'm looking for a solution to somehow get the Expander to play nice?
View
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="{Binding Source={StaticResource proxy}, Path=Data.Expanded, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
<Expander.Header>
<StackPanel>
<!-- label -->
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
ViewModel
public bool Expanded
{
get { return _expanded; }
set { _expanded = value; OnPropertyChanged(); }
}
public ListCollectionView Items
{
get
{
return _items;
}
set
{
_items = value; OnPropertyChanged();
}
}
// logic
var items = new ListCollectionView(planninglist);
items.SortDescriptions.Add(new items.GroupDescriptions.Add(new PropertyGroupDescription("AanZet"));
items.IsLiveSorting = true;
Update
Fix based on SO answer suggested in the comments by #XAMlMAX.
public class ExpanderBehavior
{
public static readonly DependencyProperty IsExpandedProperty =
DependencyProperty.RegisterAttached("IsExpanded",
typeof(bool),
typeof(ExpanderBehavior),
new PropertyMetadata(OnChanged));
public static bool GetIsExpanded(DependencyObject obj)
{
return (bool)obj.GetValue(IsExpandedProperty);
}
public static void SetIsExpanded(DependencyObject obj, bool value)
{
obj.SetValue(IsExpandedProperty, value);
}
private static void OnChanged(DependencyObject o,
DependencyPropertyChangedEventArgs args)
{
Expander tb = o as Expander;
if (null != tb)
tb.IsExpanded = (bool)args.NewValue;
}
}
As per comment conversation.
Reason why you were experiencing issue with expander staying open even though it was bound is because when you use OneWay Binding and then click on the button it then becomes disconnected. To have a better idea on when it becomes disconnected use PresentationTraceSources.TraceLevel=High.
To overcome this, one can use an attached property to stop Binding from detaching.
Now this example is for ToggleButton but the logic should apply the same.
public class TBExtender
{
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.RegisterAttached("IsChecked",
typeof(bool),
typeof(TBExtender),
new PropertyMetadata(OnChanged));
public static bool GetIsChecked(DependencyObject obj)
{
return (bool)obj.GetValue(IsCheckedProperty);
}
public static void SetIsChecked(DependencyObject obj, bool value)
{
obj.SetValue(IsCheckedProperty, value);
}
private static void OnChanged(DependencyObject o,
DependencyPropertyChangedEventArgs args)
{
ToggleButton tb = o as ToggleButton;
if (null != tb)
tb.IsChecked = (bool)args.NewValue;
}
}
Credit goes to alex-p
and wallstreet-programmer
for responses on this SO question.
I would like to trigger the method SelectAllText() when the textbox background color is red. How can I bind to code behind.
xaml:
<TextBox Grid.Column="1" Grid.Row="0" Text="Text" MouseEnter="Test1MouseEnter" Background="{Binding TxtBoxcolor, Mode=OneWay}" Name="txbName">
<TextBox.Style>
<Style>
<Style.Triggers>
<Trigger Property="TextBox.Background" Value="Red">
<!--Trigger code behind-->
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
code behind:
public void SelectAllText()
{
txbName.SelectAll();
}
It's possible in your case to handle Changed event on the background in the code behind?
txbName.Background.Changed += Background_Changed;
and in the Background_Changed
private void Background_Changed(object sender, EventArgs e)
{
var brush = sender as Brush;
if(brush!=null)
{
if(brush == Brushes.Red)
{
txbName.SelectAll();
}
}
}
Here's a way to do this with an attached event. It can only handle raising change events for one property per control. To raise events on value changes for multiple properties, you'd need an attached property that's a collection of some object with a property name and an event, which would be more complicated to write. This really just demonstrates the concept, but it's sufficient for the specific problem you have in front of you right now.
public static class PropertyChange
{
public static readonly RoutedEvent PropertyChangeEvent =
EventManager.RegisterRoutedEvent("PropertyChangeEvent", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(PropertyChange));
public static void AddPropertyChangeEventHandler(DependencyObject d, RoutedEventHandler handler)
{
var uie = d as UIElement;
if (uie != null)
{
uie.AddHandler(PropertyChange.PropertyChangeEvent, handler);
}
}
public static void RemovePropertyChangeEventHandler(DependencyObject d, RoutedEventHandler handler)
{
var uie = d as UIElement;
if (uie != null)
{
uie.RemoveHandler(PropertyChange.PropertyChangeEvent, handler);
}
}
#region PropertyChange.PropertyName Attached Property
public static String GetPropertyName(UIElement obj)
{
return (String)obj.GetValue(PropertyNameProperty);
}
public static void SetPropertyName(UIElement obj, String value)
{
obj.SetValue(PropertyNameProperty, value);
}
public static readonly DependencyProperty PropertyNameProperty =
DependencyProperty.RegisterAttached("PropertyName", typeof(String), typeof(PropertyChange),
new PropertyMetadata(null, PropertyName_PropertyChanged));
private static void PropertyName_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = d as UIElement;
var oldProperty = e.OldValue as String;
var newProperty = e.NewValue as String;
if (oldProperty != null)
{
var dpd = DependencyPropertyDescriptor.FromName(oldProperty, d.GetType(), d.GetType());
dpd.RemoveValueChanged(d, PropertyChangedHandler);
}
if (newProperty != null)
{
var dpd = DependencyPropertyDescriptor.FromName(newProperty, d.GetType(), d.GetType());
dpd.AddValueChanged(d, PropertyChangedHandler);
}
}
private static void PropertyChangedHandler(object component, EventArgs args)
{
var uie = component as UIElement;
uie.RaiseEvent(new RoutedEventArgs(PropertyChange.PropertyChangeEvent, uie));
}
#endregion PropertyChange.PropertyName Attached Property
}
Demo
XAML
<TextBox
Width="100"
x:Name="TestTextBox"
Text="Blah blah blah"
local:PropertyChange.PropertyName="Background"
local:PropertyChange.PropertyChangeEvent="TestTextBox_PropertyChangeEvent"
>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Code behind
private void TestTextBox_PropertyChangeEvent(object sender, RoutedEventArgs e)
{
var tb = (TextBox)sender;
var x = tb.Background as SolidColorBrush;
// Instead of examining the background color, I would much prefer to look directly
// at the validation: What happens if you decide to change the error background color
// to a darker shade of red? Or a GradientBrush? A cosmetic decision like that should
// not affect program behavior.
//
// But you didn't give any clues about how your validation is implemented, so that's
// your problem not mine.
if (x != null && x.Color == Colors.Red)
{
tb.Focus();
tb.SelectAll();
}
}
I am trying to change the code below from being an event setter to an attached property (just so I can clean up the code behind). I get an error in the setter saying the value cannot be null, but I don't see why yet.
Forgetting for a second whether this is a good idea or not, can someone help me get the attached property right?
Cheers,
Berryl
EventSetter (works but with code behind as shown)
<!-- SINGLE CLICK EDITING -->
<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="OnPreviewMouseLeftButtonDown"/>
</Style>
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var cell = sender as DataGridCell;
cell.Activate();
}
Property setter (error)
<!-- SINGLE CLICK EDITING -->
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="begavior:DataGridCellProperties.SingleClickToEdit" Value="True"/>
</Style>
public class DataGridCellProperties
{
public static readonly DependencyProperty SingleClickToEditProperty =
DependencyProperty.RegisterAttached("SingleClickToEditProperty",
typeof(bool), typeof(DataGridCellProperties),
new PropertyMetadata(false, OnSingleClickToEditPropertyChanged));
[AttachedPropertyBrowsableForChildren(IncludeDescendants = false)]
[AttachedPropertyBrowsableForType(typeof(DataGridCell))]
public static bool GetSingleClickToEdit(DataGridCell obj) { return (bool)obj.GetValue(SingleClickToEditProperty); }
public static void SetSingleClickToEdit(DataGridCell obj, bool value) { obj.SetValue(SingleClickToEditProperty, value); }
private static void OnSingleClickToEditPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var sender = obj as UIElement;
if (sender == null) return;
if ((bool)e.NewValue)
{
sender.PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown_EditCell;
}
else
{
sender.PreviewMouseLeftButtonDown -= OnPreviewMouseLeftButtonDown_EditCell;
}
}
private static void OnPreviewMouseLeftButtonDown_EditCell(object sender, MouseButtonEventArgs e)
{
var cell = sender as DataGridCell;
cell.Activate();
}
}
"SingleClickToEditProperty" in your d-prop registration should be "SingleClickToEdit".
I currently am facing a problem. I am using WPF.Themes which i found on codeplex, it allows me to change my application's theme.
So I imported the project and got it all working fine, but for some control, say my treeViewItem, I had style already set to it which it overrides the global styles.
I have the following code after research but still won't work.
<TreeView Name="_tvTreeView" Grid.Row="1" >
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<EventSetter Event="MouseDoubleClick" Handler="tvTreeView_PreviewMouseDoubleClick"/>
<EventSetter Event="MouseDown" Handler="tvTreeView_MouseDown"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
The based on works if I manully add the resrouce file in the merge dictionary of app.xaml of my Main Project.
But WPF.Themes project allow me to change theme dynamically by doing this.
public static void ApplyTheme(this ContentControl control, string theme)
{
ResourceDictionary dictionary = ThemeManager.GetThemeResourceDictionary(theme);
control.Resources.MergedDictionaries.Clear();
if (dictionary != null)
control.Resources.MergedDictionaries.Add(dictionary);
}
Having the above code, does not merge the global styles and my event setters.
If I manually reference the theme in app.xaml then "BasedOn" would kick in and work, but "BasedOn" don't seem to work if I set the mergedDictionaries dynamically.
Is there a way I can get this to work without adding the theme to app.xaml.
Thanks and Regards,
The BaseOn property of style cannot be set with DynamicResource, with StaticResource, it will be sealed when applied to control.
You should merge the style when global style changed, try these codes:
public class Behavior
{
#region AutoMergeStyle
public static readonly DependencyProperty AutoMergeStyleProperty =
DependencyProperty.RegisterAttached("AutoMergeStyle", typeof(bool), typeof(Behavior),
new FrameworkPropertyMetadata((bool)false,
new PropertyChangedCallback(OnAutoMergeStyleChanged)));
public static bool GetAutoMergeStyle(DependencyObject d)
{
return (bool)d.GetValue(AutoMergeStyleProperty);
}
public static void SetAutoMergeStyle(DependencyObject d, bool value)
{
d.SetValue(AutoMergeStyleProperty, value);
}
private static void OnAutoMergeStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue == e.NewValue)
{
return;
}
Control control = d as Control;
if (control == null)
{
throw new NotSupportedException("AutoMergeStyle can only used in Control");
}
if ((bool)e.NewValue)
{
Type type = d.GetType();
control.SetResourceReference(Behavior.BaseOnStyleProperty, type);
}
else
{
control.ClearValue(Behavior.BaseOnStyleProperty);
}
}
#endregion
#region BaseOnStyle
public static readonly DependencyProperty BaseOnStyleProperty =
DependencyProperty.RegisterAttached("BaseOnStyle", typeof(Style), typeof(Behavior),
new FrameworkPropertyMetadata((Style)null,
new PropertyChangedCallback(OnBaseOnStyleChanged)));
public static Style GetBaseOnStyle(DependencyObject d)
{
return (Style)d.GetValue(BaseOnStyleProperty);
}
public static void SetBaseOnStyle(DependencyObject d, Style value)
{
d.SetValue(BaseOnStyleProperty, value);
}
private static void OnBaseOnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.OldValue == e.NewValue)
{
return;
}
Control control = d as Control;
if (control == null)
{
throw new NotSupportedException("BaseOnStyle can only used in Control");
}
Style baseOnStyle = e.NewValue as Style;
Style originalStyle = GetOriginalStyle(control);
if (originalStyle == null)
{
originalStyle = control.Style;
SetOriginalStyle(control, originalStyle);
}
Style newStyle = originalStyle;
if (originalStyle.IsSealed)
{
newStyle = new Style();
newStyle.TargetType = originalStyle.TargetType;
//1. Copy resources, setters, triggers
newStyle.Resources = originalStyle.Resources;
foreach (var st in originalStyle.Setters)
{
newStyle.Setters.Add(st);
}
foreach (var tg in originalStyle.Triggers)
{
newStyle.Triggers.Add(tg);
}
//2. Set BaseOn Style
newStyle.BasedOn = baseOnStyle;
}
else
{
originalStyle.BasedOn = baseOnStyle;
}
control.Style = newStyle;
}
#endregion
#region OriginalStyle
public static readonly DependencyProperty OriginalStyleProperty =
DependencyProperty.RegisterAttached("OriginalStyle", typeof(Style), typeof(Behavior),
new FrameworkPropertyMetadata((Style)null));
public static Style GetOriginalStyle(DependencyObject d)
{
return (Style)d.GetValue(OriginalStyleProperty);
}
public static void SetOriginalStyle(DependencyObject d, Style value)
{
d.SetValue(OriginalStyleProperty, value);
}
#endregion
}
Add attached property AutoMergeStyle to xaml:
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
<EventSetter Event="MouseDoubleClick" Handler="tvTreeView_PreviewMouseDoubleClick"/>
<EventSetter Event="MouseDown" Handler="tvTreeView_MouseDown"/>
<Setter Property="Behavior.AutoMergeStyle" Property="True"/>
</Style>
At a glance:
My app displays an ItemsControl containing a Canvas as its ItemsPanel. The ItemsControl is bound to a collection of objects, each having Left/Top/Width/Height properties. A DataTemplate is used to generate rectangles that are rendered in the Canvas and positioned correctly (binding on the Left and Top properties).
How can I implement drag/drop to move these rectangles around the Canvas?
Background for my question:
My WP7 app displays a "CanvasItemsControl" defined as follows:
public class CanvasItemsControl : ItemsControl
{
public string XBindingPath { get; set; }
public string YBindingPath { get; set; }
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
FrameworkElement contentitem = element as FrameworkElement;
if (XBindingPath != null && YBindingPath != null)
{
Binding xBinding = new Binding(XBindingPath);
Binding yBinding = new Binding(YBindingPath);
if (contentitem != null)
{
contentitem.SetBinding(Canvas.LeftProperty, xBinding);
contentitem.SetBinding(Canvas.TopProperty, yBinding);
}
}
base.PrepareContainerForItemOverride(element, item);
}
}
and used in XAML as follows:
<hh:CanvasItemsControl Grid.Row="1" x:Name="TheItemsControl"
Style="{StaticResource CanvasItemsControlStyle}"
ItemsSource="{Binding AllObjects}"
XBindingPath="Left" YBindingPath="Top" />
This is the style for the CanvasItemsControl:
<Style x:Key="CanvasItemsControlStyle" TargetType="local:CanvasItemsControl">
<Setter Property="ItemTemplate" Value="{StaticResource ObjectTemplate}"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
And this is the DataTemplate I use to render my class:
<DataTemplate x:Key="ObjectTemplate" >
<Border Background="{Binding Brush}"
Width="{Binding Width}"
Height="{Binding Height}">
<TextBlock Text="{Binding Description}"/>
</Border>
</DataTemplate>
The source of the CanvasItemsControl is a collection of objects that have the properties Left, Top, Width, Height, Brush, etc.
My question
As you can see, the end result is, as you add items to the AllObjects collection, each object gets rendered and positioned correctly in the canvas. Now I need to drag/drop/move these objects around the canvas. What approach would you advise me to use to implement drag/drop? Can you please guide me through the process?
Thank you
Here's the solution to my question (at least the best one in my opinion):
1) Use of a regular Canvas as opposed of a custom control inherited from Canvas.
2) Use of a user control taking the data context (the instance of my business entity) via constructor
3) The binding between the Left/Top properties of my business class and the Canvas.Left/Top is declared at the UserControl level.
4) Use of a custom behavior inheriting from System.Windows.Interactivity.Behavior. This behavior is attached to the User Control.
I would like to acknowlege Calvin Schrotenboer and Joe Gershgorin for their immense help.
<!--____ The UserControl ____-->
<UserControl... Canvas.Left={Binding Left}" Canvas.Top={Binding Top}">
<Grid.... layout of the UserControl instead of using a DataTemplate/>
<i:Interaction.Behaviors>
<MyExample:MyMouseDragElementBehavior/>
</i:Interaction.Behaviors>
</UserControl>
The custom behavior:
public class MyMouseDragElementBehavior : Behavior<FrameworkElement>
{
public event MouseEventHandler DragBegun;
public event MouseEventHandler DragFinished;
public event MouseEventHandler Dragging;
private Point relativePosition;
public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.Register("IsEnabled", typeof(bool), typeof(MyMouseDragElementBehavior), new PropertyMetadata(true));
public bool IsEnabled
{
get
{
return (bool)GetValue(IsEnabledProperty);
}
set
{
SetValue(IsEnabledProperty, value);
}
}
protected override void OnAttached()
{
AssociatedObject.AddHandler(
UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown), false);
base.OnAttached();
}
protected override void OnDetaching()
{
AssociatedObject.RemoveHandler(
UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(OnMouseLeftButtonDown));
base.OnDetaching();
}
private static int zIndex = 0;
private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!IsEnabled)
{
return;
}
zIndex++;
Canvas.SetZIndex(AssociatedObject, zIndex);
StartDrag(e.GetPosition(AssociatedObject));
if (DragBegun != null)
{
DragBegun(this, e);
}
}
private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
AssociatedObject.ReleaseMouseCapture();
}
private void OnMouseMove(object sender, MouseEventArgs e)
{
HandleDrag(e.GetPosition(AssociatedObject));
if (Dragging != null)
{
Dragging(this, e);
}
}
internal void HandleDrag(Point newPositionInElementCoordinates)
{
double x = newPositionInElementCoordinates.X - relativePosition.X;
double y = newPositionInElementCoordinates.Y - relativePosition.Y;
if (AssociatedObject != null)
{
var currentLeft = Canvas.GetLeft(AssociatedObject);
var currentTop = Canvas.GetTop(AssociatedObject);
Canvas.SetLeft(AssociatedObject, currentLeft + x);
Canvas.SetTop(AssociatedObject, currentTop + y);
}
}
internal void StartDrag(Point positionInElementCoordinates)
{
relativePosition = positionInElementCoordinates;
AssociatedObject.CaptureMouse();
AssociatedObject.MouseMove += OnMouseMove;
AssociatedObject.LostMouseCapture += OnLostMouseCapture;
AssociatedObject.AddHandler(UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp), false);
}
internal void EndDrag()
{
AssociatedObject.MouseMove -= OnMouseMove;
AssociatedObject.LostMouseCapture -= OnLostMouseCapture;
AssociatedObject.RemoveHandler(
UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(OnMouseLeftButtonUp));
}
private void OnLostMouseCapture(object sender, MouseEventArgs e)
{
EndDrag();
if (DragFinished != null)
{
DragFinished(this, e);
}
}
}