WPF button OnPreviewMouseUp and OnPreviewMouseDown behave differently - wpf

I'm experimenting the OnPreviewMouseUp and OnPreviewMouseDown tunneling events. I'm particularly interested in e.OriginalSource as I don't understand why when I click on the text content of the button, e.OriginalSource is still the button itself, and not the text block - the visual child of the button. In fact I found the OnPreviewMouseDown event does do what I expected -- it shows e.OriginalSource as text block, however the OnPreviewMouseUp event always shows e.OriginalSource as the button itself. The following is the simple code I used for this experiment:
XAML:
<StackPanel Margin="5">
<Button Name="cmd" Margin="8">Click me.</Button>
</StackPanel>
Code behind:
public partial class ButtonMouseUpEvent : System.Windows.Window
{
public ButtonMouseUpEvent()
{
InitializeComponent();
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseDown(e);
Debug.WriteLine($"OnPreviewMouseDown(): e.Source = {e.Source}, e.OriginalSource = {e.OriginalSource}");
}
protected override void OnPreviewMouseUp(MouseButtonEventArgs e)
{
base.OnPreviewMouseUp(e);
Debug.WriteLine($"OnPreviewMouseUp(): e.Source = {e.Source}, e.OriginalSource = {e.OriginalSource}");
}
I suspect this has something to do with button's click event, which eats up the MouseUp event, but I'm trying to understand exactly why.

Related

Problem with same Blend behaviour applied to multiple elements

I created a simple Blend behaviour to be attached to TextBox elements. It's purpose is to scroll the textbox to its end when it gets the focus, and to scroll it back to the beginning when it loses the focus.
public class TextBoxScrollToEndBehaviour : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.GotFocus += AssociatedObject_GotFocus;
AssociatedObject.LostFocus += AssociatedObject_LostFocus;
}
private void AssociatedObject_LostFocus(object sender, System.Windows.RoutedEventArgs e)
{
var textBox = sender as TextBox;
textBox.ScrollToHorizontalOffset(0);
}
private void AssociatedObject_GotFocus(object sender, System.Windows.RoutedEventArgs e)
{
var textBox = sender as TextBox;
textBox.ScrollToHorizontalOffset(double.PositiveInfinity);
}
}
Xaml:
<TextBox Text="{Binding MyBinding, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Behaviors>
<behaviours:TextBoxScrollToEndBehaviour />
</i:Interaction.Behaviors>
</TextBox>
It works great when I focus the TextBox and then I click on some other control to loose the focus. Problem is that if I switch the focus between two TextBox that share the same behaviour, the scroll is not set back to 0 on the first TextBox, even the LostFocus event is correctly triggered on it.
What am I missing here? Thanks!
.NET Framework 4.7.2
I found out that by replacing this line in the LostFocus event:
textBox.ScrollToHorizontalOffset(0);
with
textBox.ScrollToLine(0);
the behaviour works perfectly in any condition.

WPF event trigger for clear TextBox

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;
}

Keep submenu open when a new image is selected for display

Using WPF: A Simple Color Picker With Preview, Sacha Barber, 18 Apr 2012 ,
I created a custom control from it:
public class ColorCustomControl : Control
{....}
It is then used as:
<Menu....>
<MenuItem.....>
<pn:ColorCustomControl/>
</MenuItem>
</Menu>
This yields the following picture when the Brushes MenuItem is selected:
Selection of any item in the opened Brushes submenu results in the appropriate action being taken with the Brushes submenu REMAINING OPEN. This is the effect I want.
However, as shown below, selection of any of the three swatches results in a quick flicker of the new swatch -- it replaces the color pattern to the left of "Preview"--followed immediately by closure of the Brushes submenu.
If the Brushes menuitem is again chosen, the most recently selected swatch correctly appears.
I have tried all preview events (i.e., keyboard lost focus, left mouse down, etc.), to try stopping closure of the submenu when a swatch is chosen. Nothing I have found will stop the popup from closing.
How can closure of the Brushes submenu be prevented when selecting a swatch from the visual?
(I strongly suspect that redrawing of the visual, as in InvalidateVisual() when a new swatch image is selected, is forcing closure of the submenu).
Any ideas anybody?
TIA
My suggestion is to stop events propagation from your user control. So in your ColorCustomControl class first of all add a property (it can be a dependency one too if you need):
private bool propagateEvents = true;
public bool PropagateEvents
{
get
{
return propagateEvents;
}
set
{
propagateEvents = value;
}
}
Then add e.Handled = !PropagateEvents; at the end of every mouse event handler; in the end add a Swatch_MouseLeftButtonUp method (it has to handle the event raised by ImgSqaure1, ImgSqaure2 and ImgCircle1).
The result will be:
private void Swatch_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Image img = (sender as Image);
ColorImage.Source = img.Source;
e.Handled = !PropagateEvents;
}
private void Swatch_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
e.Handled = !PropagateEvents;
}
private void CanvImage_MouseDown(object sender, MouseButtonEventArgs e)
{
IsMouseDown = true;
e.Handled = !PropagateEvents;
}
private void CanvImage_MouseUp(object sender, MouseButtonEventArgs e)
{
IsMouseDown = false;
e.Handled = !PropagateEvents;
}
and in the user control XAML:
<Image x:Name="ImgSqaure1"
Height="20" Width="20"
Source="Images/ColorSwatchSquare1.png"
Margin="45,0,0,0"
ToolTip="Square swatch1"
MouseLeftButtonDown="Swatch_MouseLeftButtonDown"
MouseLeftButtonUp="Swatch_MouseLeftButtonUp"/>
<Image x:Name="ImgSqaure2"
Height="20" Width="20"
Source="Images/ColorSwatchSquare2.png" Margin="5,0,0,0"
ToolTip="Square swatch2"
MouseLeftButtonDown="Swatch_MouseLeftButtonDown"
MouseLeftButtonUp="Swatch_MouseLeftButtonUp"/>
<Image x:Name="ImgCircle1" Height="20" Width="20"
Source="Images/ColorSwatchCircle.png" Margin="5,0,0,0"
ToolTip="Circle swatch1"
MouseLeftButtonDown="Swatch_MouseLeftButtonDown"
MouseLeftButtonUp="Swatch_MouseLeftButtonUp" />
Now all you have to do is set the PropagateEvents property in your menu:
<Menu....>
<MenuItem.....>
<pn:ColorCustomControl PropagateEvents="False" />
</MenuItem>
</Menu>
I hope it can help you.

How to 'format' text box values in WPF

Is there an easy way to have a text box display a format in a text box and remove it when editing ?
Example : I want to ask for a money value. When the user finished entering the value 5.5, it displays $ 5.00 . When the user reclick on the textbox, it should switch back to 5.5 .
Thanks you.
Edit : What I am trying to do would be the equivalent of a converter except the textbox does not have a binding set to it.
There's a couple of solutions for this. Perhaps the easiest would be to implement a custom Behavior (you'll need to reference System.Windows.Interactivity.dll) & hook onto some events TextBox exposes, like GotFocus and LostFocus. Here's a sample I've done for selecting all of a TextBox's text when it gets focused:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace TextBoxUtility
{
public class TextBoxSelectAllOnFocusBehavior : Behavior<TextBox>
{
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown;
this.AssociatedObject.GotKeyboardFocus += OnSelectAll;
this.AssociatedObject.MouseDoubleClick += OnSelectAll;
}
protected override void OnDetaching()
{
this.AssociatedObject.PreviewMouseLeftButtonDown -= OnPreviewMouseLeftButtonDown;
this.AssociatedObject.GotKeyboardFocus -= OnSelectAll;
this.AssociatedObject.MouseDoubleClick -= OnSelectAll;
base.OnDetaching();
}
private static void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var textbox = sender as TextBox;
if (textbox != null && !textbox.IsKeyboardFocusWithin)
{
textbox.Focus();
e.Handled = true;
}
}
private static void OnSelectAll(object sender, RoutedEventArgs e)
{
var textbox = sender as TextBox;
if (textbox != null)
{
textbox.SelectAll();
}
}
Edit: Forgot to show how to hook the behavior up to the TextBox.
To attach the Behavior, in your XAML you'll need to add a namespace reference to your Behavior's namespace, and then attach it as a child of the TextBox markup like so:
<TextBox Text="{Binding ....}">
<i:Interaction.Behaviors>
<utility:TextBoxSelectAllOnFocusBehavior />
</i:Interaction.Behaviors>
</TextBox>
You could do something similar, though with yours you'd want to hook up to the LostFocus-related events and adjust the contents of the TextBox's Text property.

WPF ListBox drag & drop interferes with ContextMenu?

I'm implementing drag & drop from a ListBox, but I'm seeing some strange behaviour with a ContextMenu elsewhere in the window. If you open the context menu and then start a drag from the ListBox, the context menu closes but won't open again until after you perform another drag.
Does this make sense? Anybody got any ideas what might be going on?
<ListBox Grid.Row="0" ItemsSource="{Binding SourceItems}" MultiSelectListboxDragDrop:ListBoxExtension.SelectedItemsSource="{Binding SelectedItems}" SelectionMode="Multiple" PreviewMouseLeftButtonDown="HandleLeftButtonDown" PreviewMouseLeftButtonUp="HandleLeftButtonUp" PreviewMouseMove="HandleMouseMove"/>
<ListBox Grid.Row="1" ItemsSource="{Binding DestinationItems}" AllowDrop="True" Drop="DropOnToDestination" />
<Button Grid.Row="2">
<Button.ContextMenu>
<ContextMenu x:Name="theContextMenu">
<MenuItem Header="context 1"/>
<MenuItem Header="context 2"/>
<MenuItem Header="context 3"/>
</ContextMenu>
</Button.ContextMenu>
Button with context menu
</Button>
...
public partial class Window1
{
private bool clickedOnSourceItem;
public Window1()
{
InitializeComponent();
DataContext = new WindowViewModel();
}
private void DropOnToDestination(object sender, DragEventArgs e)
{
var viewModel = (WindowViewModel)e.Data.GetData(typeof(WindowViewModel));
viewModel.CopySelectedItems();
}
private void HandleLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var sourceElement = (FrameworkElement)sender;
var hitItem = sourceElement.InputHitTest(e.GetPosition(sourceElement)) as FrameworkElement;
if(hitItem != null)
{
clickedOnSourceItem = true;
}
}
private void HandleLeftButtonUp(object sender, MouseButtonEventArgs e)
{
clickedOnSourceItem = false;
}
private void HandleMouseMove(object sender, MouseEventArgs e)
{
if(clickedOnSourceItem)
{
var sourceItems = (FrameworkElement)sender;
var viewModel = (WindowViewModel)DataContext;
DragDrop.DoDragDrop(sourceItems, viewModel, DragDropEffects.Move);
clickedOnSourceItem = false;
}
}
}
It seemed to be something to do with the mouse capture!?
The normal sequence of events during a drag goes something like this...
The PreviewMouseLeftButtonDown
handler gets called and
ListBox.IsMouseCaptureWithin is
false.
The PreviewMouseMove handler
gets called. By this time
ListBox.IsMouseCaptureWithin is true.
During the PreviewMouseMove handler
DragDrop.DoDragDrop gets called and
sometime during this the mouse
capture is released from the ListBox.
But, what seems to happening for a drag started when the context menu is open is...
The PreviewMouseLeftButtonDown
handler gets called and
ListBox.IsMouseCaptureWithin is
false.
The PreviewMouseMove handler gets
called. But this time
ListBox.IsMouseCaptureWithin is
still false.
Sometime after the end of the
PreviewMouseMove handler the
ListBox then gets the mouse capture
(ListBox.IsMouseCaptureWithin
becomes true)
The result of this is that after the drag, the ListBox still has the mouse capture so any clicks on the button to open the context menu are actually going to the listbox not the button.
Adding the following code to the start of the PreviewMouseLeftButtonDown handler seems to help by swallowing up the click that closes that context menu rather than trying to start a drag from it...
if (!contextMenuCloseComplete)
{
sourceElement.CaptureMouse();
return;
}
...with the contextMenuCloseComplete bool getting set in handlers for the context menu's Closed and Opened events.
Does that make sense? Does anyone understand where this mouse capture behaviour is coming from?

Resources