WPF StackPanel com Clique e DoubleClick
-
10-07-2019 - |
Pergunta
Eu preciso ser capaz de lidar com o clique duas vezes e clique único evento no WPF StackPanel. Mas não há tal coisa como DoubleClick Evento do StackPanel. Eu quero fazer 2 diferentes operações nestes 2 EventHandlers.
Qualquer idéia de como fazer isso?
Obrigado
Solução
A melhor maneira é para corrigir seu próprio manipulador botão do mouse com um tempo limite - se o evento é acionado novamente dentro do tempo limite, em seguida, disparar a sua mensagem DoubleClick, caso contrário, chamar o manipulador de clique único. Aqui está um código de exemplo ( Editar: originalmente encontrado aqui ):
/// <summary>
/// For double clicks
/// </summary>
public class MouseClickManager {
private event MouseButtonEventHandler _click;
private event MouseButtonEventHandler _doubleClick;
public event MouseButtonEventHandler Click {
add { _click += value; }
remove { _click -= value; }
}
public event MouseButtonEventHandler DoubleClick {
add { _doubleClick += value; }
remove { _doubleClick -= value; }
}
/// <summary>
/// Gets or sets a value indicating whether this <see cref="MouseClickManager"/> is clicked.
/// </summary>
/// <value><c>true</c> if clicked; otherwise, <c>false</c>.</value>
private bool Clicked { get; set; }
/// <summary>
/// Gets or sets the timeout.
/// </summary>
/// <value>The timeout.</value>
public int DoubleClickTimeout { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="MouseClickManager"/> class.
/// </summary>
/// <param name="control">The control.</param>
public MouseClickManager(int doubleClickTimeout) {
this.Clicked = false;
this.DoubleClickTimeout = doubleClickTimeout;
}
/// <summary>
/// Handles the click.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
public void HandleClick(object sender, MouseButtonEventArgs e) {
lock (this) {
if (this.Clicked) {
this.Clicked = false;
OnDoubleClick(sender, e);
}
else {
this.Clicked = true;
ParameterizedThreadStart threadStart = new ParameterizedThreadStart(ResetThread);
Thread thread = new Thread(threadStart);
thread.Start(e);
}
}
}
/// <summary>
/// Resets the thread.
/// </summary>
/// <param name="state">The state.</param>
private void ResetThread(object state) {
Thread.Sleep(this.DoubleClickTimeout);
lock (this) {
if (this.Clicked) {
this.Clicked = false;
OnClick(this, (MouseButtonEventArgs)state);
}
}
}
/// <summary>
/// Called when [click].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
private void OnClick(object sender, MouseButtonEventArgs e) {
if (_click != null) {
if (sender is Control) {
(sender as Control).Dispatcher.BeginInvoke(_click, sender, e);
}
}
}
/// <summary>
/// Called when [double click].
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
private void OnDoubleClick(object sender, MouseButtonEventArgs e) {
if (_doubleClick != null) {
_doubleClick(sender, e);
}
}
}
Em seguida, no controle que deseja receber os eventos:
MouseClickManager fMouseManager = new MouseClickManager(200);
fMouseManager.Click += new MouseButtonEventHandler(YourControl_Click);
fMouseManager.DoubleClick += new MouseButtonEventHandler(YourControl_DoubleClick);
Outras dicas
<StackPanel MouseDown="StackPanel_MouseDown">
<!--stackpanel content-->
<TextBlock>Hello</TextBlock>
</StackPanel>
Em seguida, no manipulador de eventos:
private void StackPanel_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount >= 2)
{
string hello; //only hit here on double click
}
}
O trabalho deve. Note-se que um único clique no StackPanel teria atingido o evento (mas não a se verificar)
... anos mais tarde. @ Solução de Moomintroll é perfeitamente aceitável. Outra opção é para embrulhar o painel de pilha em um controle de conteúdo que suporta o evento de clique duplo.
<ContentControl MouseDoubleClick="DoubleClickHandler" >
<StackPanel>
</StackPanel>
</ContentControl>
Outra opção é adicionar um MouseBinding aos InputBindings no StackElement e depois adicionar um CommandBinding que é ativado por MouseBinding. Em geral esta é uma prática melhor do que mecanismos baseados em eventos porque evita os problemas de vazamento de memória causados ??por referências fortes. Além disso, prevê a separação da lógica de comando a partir da representação.
Dito isto, não é tão simples e anexando as marcas de eventos para um grande atalho.
E escusado será dizer, fazer o seu fundo StackPanel pelo menos transparente ou não vai ser pego pelo teste de ocorrência clique do mouse quando você clicar sobre o "background". fundos nulos são ignorados pela detecção de ocorrências.
Eu tive um problema semelhante (respondem ao evento de clique único e fazer um trabalho adicional em caso de evento de clique duplo). Eu resolvi o problema desta forma:
1) definir e declarar algum inteiro para segurar último clique timestamp
int lastClickTimestamp;
2) no método de inicialização Window_Loaded previamente declarado variável com algum número maior do que 200
lastClickTimestamp = 1000;
3) manipulador do botão add mouse para o seu painel de pilha
stackPanel.MouseLeftButtonUp += new MouseButtonEventHandler(stackPanel_MouseLeftButtonUp);
4) adicione o seguinte método
void stackPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (e.Timestamp - lastClickTimeStamp < 200)
{
//double click
}
lastClickTimeStamp = e.Timestamp;
//single click
}
Este código é inútil se você precisa para detectar separadamente único clique e evento de clique duplo. , Que introduz situação um pouco mais complexidade, mas definitivamente pode ser resolvido.
resposta completa usando WPF DispatcherTimer
. Criamos o temporizador sob demanda e desligá-lo quando feito de forma a evitar o entupimento de recursos.
A C #:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
public partial class MainWindow : Window
{
DispatcherTimer dt;
bool clear_timer()
{
if (dt == null)
return false;
dt.Tick -= _single_click;
dt = null;
return true;
}
private void _click(Object sender, MouseButtonEventArgs e)
{
if (clear_timer())
Debug.Print("double click");
else
dt = new DispatcherTimer(
TimeSpan.FromMilliseconds(GetDoubleClickTime()),
DispatcherPriority.Normal,
_single_click,
Dispatcher);
}
void _single_click(Object sender, EventArgs e)
{
clear_timer();
Debug.Print("single click");
}
public MainWindow() { InitializeComponent(); }
[DllImport("user32.dll")]
static extern uint GetDoubleClickTime();
};
O XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Horizontal"
Background="AliceBlue"
Width="100"
Height="100"
MouseLeftButtonDown="_click" />
</Window>
Não há uma solução mais fácil para fazer isso.
O evento de No StackPanel PreviewMouseLeftDown (por exemplo), você pode verificar se MouseButtonEventArgs.ClickCount propriedade tem um valor de 2. 1 = clique Único 2 = Double Click