Allowing click-through with two hit testable controls - wpf

I have two controls stacked on top of each other as such:
Button1 is ZIndex 2, Button2 is ZIndex 1.
Both need to be hit testable, as I need to listen to Button1's MouseEnter event for other system functions. But I also need to be able to have Button2 be clickable. I'm not quite sure how I can get this functionality to pan out (since I can't just set the Button1 to IsHitTestableFalse). Is there some sort of way I can say, in Button1's Click event
if (hasElementBeneath):
click that element instead

If I understand you correctly the trick is rather to prevent the "click" from the inner button to be handled by the outer button too.
To accomplish this: In the click handler for the inner button set the Handled property of the supplied RoutedEventArgs to true.
Like this:
<Button Click="Button1_Click" HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel>
<TextBlock>Button 1</TextBlock>
<Button Click="Button2_Click" Margin="10">Button 2</Button>
</StackPanel>
</Button>
and:
private void Button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Button1 clicked!");
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Button2 clicked!");
e.Handled = true;
}

Related

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 avoid lost focus of TextBox when click a button in wpf?

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;

WPF: Toggle multiple Buttons by dragging the mouse over them

Is it possible to toggle the state of multiple ToggleButtons by dragging the mouse over them (with the left mouse button hold down)?
There isn't anything in the box that would support this, but you should be able to subscribe to MouseMove and toggle the state on if the button is pressed.
<ToggleButton Content="ToggleButton" MouseEnter="ToggleButtonMouseEnter" Width="80" HorizontalAlignment="Left"/>
private void ToggleButtonMouseEnter(object sender, MouseEventArgs e)
{
System.Windows.Controls.Primitives.ToggleButton tb = (System.Windows.Controls.Primitives.ToggleButton)sender;
if(e.LeftButton == MouseButtonState.Pressed) tb.IsChecked = !tb.IsChecked;
}

Button on ListBox Item

I have a ListBox filled with items. Some of the items may have buttons or links within RichTextBlock inside. I want to fire different action either when the item is pressed or when the button is pressed. The problem is when I hit the button, also the action connected with the item itself is fired. How can I prevent fireing the event of list item when the button inside is pressed?
ListBox:
<ListBox x:Name="StripesList" SelectionChanged="StripesList_SelectionChanged">
</ListBox>
ListBox Items:
<Border x:Name="OuterBorder" Width="400">
<Image x:Name="ThumbnailBox" Width="100" Source="{Binding Thumbnail}" Tap="ThumbnailClick" />
</Border>
Code:
private void StripesList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (StripesList != null)
{
StripesList.SelectedIndex = -1;
}
}
private void ThumbnailClick(object sender, System.Windows.Input.GestureEventArgs e)
{
Image image = sender as Image;
// handle image click
}
private void ItemClick(object sender, EventArgs e)
{
// handle item click
}
I register item click event observers from code behind:
item.Tap += new EventHandler(ItemClick);
set e.Handled=true in the handler of all events . It will stop the Bubbleing of the event.

How do I stop binding properties from updating?

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

Resources