How to unselect other AppBarToggleButton UWP - c#

I m doing an UWP project, and I'm using the CommandBar.
In my command Bar I have multiple AppBarToggleButton, when I click to one of them I wan't that the other AppBarToggleButton are unselect.
Is there an options to do that ?
Here is a symple code :
<CommandBar>
<AppBarToggleButton Icon="Shuffle" Label="Shuffle" Click="AppBarButton_Click" />
<AppBarToggleButton Icon="RepeatAll" Label="Repeat" Click="AppBarButton_Click"/>
<CommandBar.Content>
<TextBlock Text="Now playing..." Margin="12,14"/>
</CommandBar.Content>

You just need to x:Name your buttons and manually set IsChecked to False whenever the other one is clicked.
private void AppBarButton1_Click(object sender, RoutedEventArgs e)
{
AppBarButton2.IsChecked = false;
}
Update
A pure XAML solution would be using bindings. Note you will need an InvertedBooleanConverter here.
IsChecked="{x:Bind Toggle1.IsChecked, Converter={StaticResource InvertedBooleanConverter}, Mode=TwoWay}"
Bonus
I really dislike the binding approach so here's a pure XAML solution built with a behavior. Note you will need to first install the XAML Behaviors from the Nuget Package Manager -
Install-Package Microsoft.Xaml.Behaviors.Uwp.Managed -Version 2.0.0
This AutoDeselectToggleButtonBehavior behavior below basically gets all the AppBarToggleButtons once the CommandBar is loaded, subscribe to all their Click events and in the handlers, simply de-select all the other toggles except the clicked one.
public class AutoDeselectToggleButtonBehavior : Behavior<CommandBar>
{
private IEnumerable<AppBarToggleButton> _toggleButtons;
protected override void OnAttached()
{
AssociatedObject.Loaded += OnLoaded;
base.OnAttached();
}
protected override void OnDetaching()
{
foreach (var toggle in _toggleButtons)
{
toggle.Click -= OnToggleClick;
}
AssociatedObject.Loaded -= OnLoaded;
base.OnDetaching();
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
// Children extension method:
// https://github.com/JustinXinLiu/Continuity/blob/master/Continuity/Extensions/UtilExtensions.cs#L25
_toggleButtons = AssociatedObject.Children().OfType<AppBarToggleButton>();
foreach (var toggle in _toggleButtons)
{
toggle.Click += OnToggleClick;
}
}
private void OnToggleClick(object sender, RoutedEventArgs e)
{
var clickedToggle = (AppBarToggleButton)sender;
foreach (var toggle in _toggleButtons)
{
if (toggle != clickedToggle)
{
toggle.IsChecked = false;
}
}
}
}
Once it's in place, just attach it to your CommandBar like the following -
<CommandBar>
<Interactivity:Interaction.Behaviors>
<local:AutoDeselectToggleButtonBehavior />
</Interactivity:Interaction.Behaviors>
Clean and reusable. :)

Related

Keyboard accelerator firing when writing to Textbox

Click event of Button is fired when pressing D while writing to Textbox.
Is there an elegant way how to suppress Keyboard Accelerator while Textbox is focused?
XAML:
<StackPanel Orientation="Vertical">
<TextBox></TextBox>
<Button Click="Button_Click" Content="Button with "D" as keyboard accelerator" Margin="0,10">
<Button.KeyboardAccelerators>
<KeyboardAccelerator Key="D"></KeyboardAccelerator>
</Button.KeyboardAccelerators>
</Button>
<TextBlock x:Name="ButtonClickCounter"></TextBlock>
</StackPanel>
C#:
int buttonClickCounter;
private void Button_Click(object sender, RoutedEventArgs e)
{
ButtonClickCounter.Text = $"Button clicked {++buttonClickCounter} times";
}
EDIT:
Why Accelerator with Modifier (Alt+D or Ctrl+D) is not solution?
I am creating video player and I found that one-key shortcuts are neat solution for fast operations with video player (same as in VLC).
Best Solution so far:
Creating custom KeyboardAccelerator, that checks if focus is set to text box. Only edit in code that need to be done is changing KeyboardAccelerator to AcceleratorWithHandledActionIfTextboxIsFocused.
public class AcceleratorWithHandleDActionIfTextboxIsFocused:KeyboardAccelerator
{
public AcceleratorWithHandleActionIfTextboxIsFocused()
{
Invoked += AcceleratorWithHandleActionIfTextboxIsFocused_Invoked;
}
private void AcceleratorWithHandleActionIfTextboxIsFocused_Invoked(KeyboardAccelerator sender, KeyboardAcceleratorInvokedEventArgs args)
{
var focusedElement = FocusManager.GetFocusedElement();
if (focusedElement.GetType() == typeof(TextBox))
args.Handled = true;
}
}
part of XAML:
<Button.KeyboardAccelerators>
<custom:AcceleratorWithHandleDActionIfTextboxIsFocused Key="D"></KeyboardAccelerator>
</Button.KeyboardAccelerators>
I agree with #Thomas Weller in his comments as for not using a single letter as a keyboard accelerator...
But everybody has their own requirements, anyway, you could try to e.handle = true your event when textbox is focused.
Something like this:
public void Button_Click(object sender, ExecutedRoutedEventArgs e)
{
if (textBox.isFocused)
{
e.handled = true;
}
}

WPF: Figuring out whether a MouseBinding is a single click or double click

Im using Mousebindings in my view to listen to user clicks like this:
<Path.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding DoubleLeftClickProj}" />
<MouseBinding Gesture="LeftClick" Command="{Binding SingleLeftClick}"/>
</Path.InputBindings>
I only need one of the mouse gestures at a time. So if I double click on my application I want to ignore the single left click mousebinding. Possibly like waiting 1-2sec after the initial mouseclick then decided which should be called. Is there a simple way of doing this?
I got it working the following way (I used a Button to test it, you'll have to adapt it).
Use Event Handlers
<Button MouseDoubleClick="Button_MouseDoubleClick" Click="Button_Click"></Button>
store the DataContext in a static variable
private static object context;
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
context = DataContext;
}
Adapt this code (I mainly got it from https://stackoverflow.com/a/971676/4792869)
private static DispatcherTimer myClickWaitTimer =
new DispatcherTimer(
new TimeSpan(0, 0, 0, 1),
DispatcherPriority.Background,
mouseWaitTimer_Tick,
Dispatcher.CurrentDispatcher);
private void Button_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
// Stop the timer from ticking.
myClickWaitTimer.Stop();
((ICommand)DataContext).Execute("DoubleLeftClickProj");
e.Handled = true;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
myClickWaitTimer.Start();
}
private static void mouseWaitTimer_Tick(object sender, EventArgs e)
{
myClickWaitTimer.Stop();
// Handle Single Click Actions
((ICommand)context).Execute("SingleLeftClick");
}

How to detect a full mouse click (both down and up) on a control?

In WPF with MVVM (and no code-behind), say I have a Rectangle. If the user clicks on the rectangle, I want to perform an action. I effectively want the action to occur on mouse up. However, I only want the action triggered if they performed a full click (i.e. both mouse down and mouse up) on the rectangle. If they clicked down over some other control, held the mouse down, moved it over the rectangle, and released the mouse, I do not want to trigger the action.
There is the MouseDown event and the MouseUp event, but what I effectively want is a "MouseClick" event (like in WinForms). Is there any built-in mouse click event/trigger/something functionality? If not, what would be the best way to approach this? Custom trigger? Attached behavior?
Well, it isn't perfect, but I created a custom trigger to solve the problem. I'm still open to any better solutions if anyone has one.
public class MouseClickTrigger : TriggerBase<UIElement>
{
private bool _isMouseDown;
protected override void OnAttached()
{
this.AssociatedObject.MouseDown += this.AssociatedObject_MouseDown;
this.AssociatedObject.MouseUp += this.AssociatedObject_MouseUp;
this.AssociatedObject.MouseLeave += this.AssociatedObject_MouseLeave;
}
protected override void OnDetaching()
{
this.AssociatedObject.MouseDown -= this.AssociatedObject_MouseDown;
this.AssociatedObject.MouseUp -= this.AssociatedObject_MouseUp;
this.AssociatedObject.MouseLeave -= this.AssociatedObject_MouseLeave;
}
private void AssociatedObject_MouseDown(object sender, MouseButtonEventArgs e)
{
this._isMouseDown = true;
}
private void AssociatedObject_MouseUp(object sender, MouseButtonEventArgs e)
{
bool fullClick = this._isMouseDown;
this._isMouseDown = false;
if (fullClick)
{
this.InvokeActions(e);
}
}
private void AssociatedObject_MouseLeave(object sender, MouseEventArgs e)
{
this._isMouseDown = false;
}
}
Whaaaaat??? I dont your problem... Why cant you do something like this:
private bool isMouseDownInRect = false;
<Rectangle Width="100" Height="100" MouseUp="UIElement_OnMouseUp" MouseDown="UIElement_OnMouseDown" Fill="Black"></Rectangle>
private void UIElement_OnMouseUp(object sender, MouseButtonEventArgs e)
{
if (isMouseDownInRect)
{
// My code
}
isMouseDownInRect = false;
}
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
isMouseDownInRect = true;
}
I hope this is what you asking for.
EDIT:
Dont forget to handle the mouse_enter/leave event...

How to detect DoubleClick in WPF ToggleButton

In my WPF application I have Toggle Button, I want to detect when user double click on it (in both cases if it checked or unchecked).
How can I do that?
Thanks in advance
You can use the OnPreviewMouseDoubleClick event
xaml:
<ToggleButton Height="75" Width="100" PreviewMouseDoubleClick="Control_OnPreviewMouseDoubleClick"/>
code-behind:
private void Control_OnPreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var toggleButtton = sender as ToggleButton;
if (toggleButtton != null)
{
if (toggleButtton.IsChecked.HasValue)
{
if (toggleButtton.IsChecked.Value)
{
// Checked
}
else
{
// Unchecked
// this will re-check the button if double-click unchecks it
//
toggleButtton.IsChecked = true;
toggleButtton.Focus();
}
}
}
}
Use PreviewMouseDoubleClick event (msdn):
XAML:
<ToggleButton x:Name="tButton" Height="30" Content="MyButton"
PreviewMouseDoubleClick="tButton_PreviewMouseDoubleClick"
/>
Code-behind:
private void tButton_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
tButton.IsChecked = !tButton.IsChecked.Value;
e.Handled = true;
//...
}
This event is pretty easy with togglebuttons
Xaml you write the following to get an EventHandler:
<ToggleButton Name="button1" MouseDoubleClick="button1_DoubleClick" />
In c# you write the following to get an EventHandler:
button1.MouseDoubleClick += new MouseButtonEventHandler(button1_DoubleClick);
And in both cases you need:
void button1_DoubleClick(object sender, MouseButtonEventArgs e)
{
}

WPF TabControl How to change Tab on mouse up rather than mouse down?

In the WPF TabControl the default behavior is to change the selected tab on mouse down.
In my application changing the tab sometimes resizes things, and at times the mouse up event will get called on a another user control because the tabcontrol moved.
If i can set the tab pages to switch only on mouse up rather than mouse down it would solve the issue.
You can use a custom TabItem like so:
public class MyTabItem : TabItem {
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) {
if (e.Source == this || !this.IsSelected)
return;
base.OnMouseLeftButtonDown(e);
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) {
if (e.Source == this || !this.IsSelected)
base.OnMouseLeftButtonDown(e); // OR just this.Focus(); OR this.IsSeleded = true;
base.OnMouseLeftButtonUp(e);
}
}
You can subscribe to the PreviewMouseDown event and set the Handled flag of the routed event to disable the mousedown, and then on MouseUp you can set IsSelected on the TabItem. This way you don't have to create custom control.
XAML
<TabItem Header="My Tab" MouseUp="TabItem_MouseUp" PreviewMouseDown="TabItem_PreviewMouseDown">
<!--Content-->
</TabItem>
C#
private void TabItem_MouseUp(object sender, MouseButtonEventArgs e)
{
if (sender is TabItem tab && !tab.IsSelected)
tab.IsSelected = true;
}//end private void TabItem_MouseUp(object sender, MouseButtonEventArgs e)
private void TabItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (sender is TabItem tab && !tab.IsSelected)
e.Handled = true;
}//end private void TabItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)

Categories