Могу ли я расширить Button, чтобы добавить событие RightClick таким образом, чтобы графические побочные эффекты также сохранялись?
Я пытаюсь расширить кнопку, чтобы добавить событие RightClick.
Мой клиент хочет, чтобы кнопка делала разные вещи в зависимости от того, щелкнешь ли ты левой или правой кнопкой мыши. Я ожидал, что будет легкое событие для щелчка правой кнопкой мыши, но оказывается, что нет.
Я бы предпочел, чтобы визуальное поведение Баттона было идентичным ранее существовавшему событию Click, но оно оказалось сложным. Кнопка имеет много графических вариантов поведения, которые возникают при нажатии и перетаскивании кнопки.
- Когда вы нажимаете вниз, кнопка опускается. Если вы тянете вниз опущенную кнопку, она поднимается (например, не опускается). Любые другие кнопки, которые вы перетаскиваете, игнорируют его.
- Когда вы перетаскиваете исходную кнопку, она снова опускается.
- Если вы перетаскиваете, а затем отпускаете, исходное состояние кнопки должно быть сброшено (то есть вы не можете снова щелкнуть и перетащить).
Эти маленькие графические причуды будут выглядеть грубыми, если визуальные элементы, нажимаемые левой кнопкой мыши, не соответствуют визуальным элементам, вызываемым правой кнопкой мыши.
В настоящее время я застрял на этом: если щелкнуть правой кнопкой мыши и удерживать кнопку, а затем перетащить кнопку, как я могу определить, отменяет ли щелчок пользователь? Мне нужно знать это, чтобы я мог не нажимать кнопку при повторном входе.
Более широкий вопрос: я даже на правильном пути? Я не мог найти никого, кто делал это раньше. Мой код ниже.
public class RightClickButton : Button
{
public event RoutedEventHandler RightClick;
public RightClickButton()
{
this.MouseRightButtonDown += new System.Windows.Input.MouseButtonEventHandler(RightClickButton_MouseRightButtonDown);
this.MouseRightButtonUp += new System.Windows.Input.MouseButtonEventHandler(RightClickButton_MouseRightButtonUp);
this.MouseEnter += new System.Windows.Input.MouseEventHandler(RightClickButton_MouseEnter);
this.MouseLeave += new System.Windows.Input.MouseEventHandler(RightClickButton_MouseLeave);
}
void RightClickButton_MouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.IsPressed = true;
}
void RightClickButton_MouseRightButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.IsPressed = false;
if (RightClick != null)
RightClick.Invoke(this, e);
}
void RightClickButton_MouseLeave(object sender, System.Windows.Input.MouseEventArgs e)
{
if (this.IsPressed)
this.IsPressed = false;
}
void RightClickButton_MouseEnter(object sender, System.Windows.Input.MouseEventArgs e)
{
if (this.IsFocused && Mouse.RightButton == MouseButtonState.Pressed)
this.IsPressed = true;
}
}
6 ответов
Благодаря ответу на мой другой вопрос, это то, что я придумал.
Кажется, могут быть некоторые различия между поведением XP и Win7. Мне нужно проверить дальше на Win7.
public class RightClickButton : Button
{
public event RoutedEventHandler RightClick;
private bool _clicked = false;
public RightClickButton()
{
this.MouseRightButtonDown += new System.Windows.Input.MouseButtonEventHandler(RightClickButton_MouseRightButtonDown);
this.MouseRightButtonUp += new System.Windows.Input.MouseButtonEventHandler(RightClickButton_MouseRightButtonUp);
}
// Subclasses can't invoke this event directly, so supply this method
protected void TriggerRightClickEvent(System.Windows.Input.MouseButtonEventArgs e)
{
if (RightClick != null)
RightClick(this, e);
}
void RightClickButton_MouseRightButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.IsPressed = true;
CaptureMouse();
_clicked = true;
}
void RightClickButton_MouseRightButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
ReleaseMouseCapture();
if(this.IsMouseOver && _clicked)
{
if (RightClick != null)
RightClick.Invoke(this, e);
}
_clicked = false;
this.IsPressed = false;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (this.IsMouseCaptured)
{
bool isInside = false;
VisualTreeHelper.HitTest(
this,
d =>
{
if (d == this)
{
isInside = true;
}
return HitTestFilterBehavior.Stop;
},
ht => HitTestResultBehavior.Stop,
new PointHitTestParameters(e.GetPosition(this)));
if (isInside)
this.IsPressed = true;
else
this.IsPressed = false;
}
}
}
Просто добавляю свой 2с. Это основано на ответе @Grant со следующими изменениями:
Это правильно использует
_rightButtonDown
флаг, а неCaptureMouse
свойство определять обработку, так какCaptureMouse
возможно, был установлен в другом месте (т.е. это придерживается надлежащей инкапсуляции.)Это имеет поддержку для
ClickMode
свойство, допускающее стоимостьPress
,Это правильно использует
OnRightClick
обработчик, имя которого не только теперь следует стандартному соглашению, но также обрабатывает все вызовы дляRightClick
событие, а не дублирование кода. Это было также отмечено какvirtual
чтобы правильно поддерживать возможности подкласса в том, что если вы переопределите эту функцию, вы можете обрабатывать / блокировать всеRightClick
События.Я назвал его EnhancedButton на случай, если захочу добавить дополнительную функциональность.
Я включил пример того, как вы определяете стиль, чтобы он правильно отображался на панели инструментов.
Примечание. Правильный способ реализации события RightClick для элемента управления - это RoutedEvent, а не стандартное событие CLR. Тем не менее, я оставлю это на усмотрение читателя, чтобы этот пример был простым.
Вот код для класса EnhancedButton:
public class EnhancedButton : Button
{
private bool _rightButtonDown = false;
public EnhancedButton()
{
MouseRightButtonDown += RightClickButton_MouseRightButtonDown;
MouseRightButtonUp += RightClickButton_MouseRightButtonUp;
}
#region RightClick Event
public event RoutedEventHandler RightClick;
protected virtual void OnRightClick(MouseButtonEventArgs e)
{
RightClick?.Invoke(this, e);
}
#endregion RightClick Event
#region Event Handlers
private void RightClickButton_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
IsPressed = true;
_rightButtonDown = true;
CaptureMouse();
if(ClickMode == ClickMode.Press)
OnRightClick(e);
}
private void RightClickButton_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
if(!_rightButtonDown)
return;
ReleaseMouseCapture();
IsPressed = false;
if(IsMouseOver && ClickMode == ClickMode.Release)
OnRightClick(e);
_rightButtonDown = false;
}
#endregion Event Handlers
#region Overrides
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if(!_rightButtonDown)
return;
bool isInside = false;
VisualTreeHelper.HitTest(this, d =>
{
if (d == this)
isInside = true;
return HitTestFilterBehavior.Stop;
},
ht => HitTestResultBehavior.Stop,
new PointHitTestParameters(e.GetPosition(this)));
IsPressed = isInside;
}
#endregion Overrides
}
И вот как вы определяете стиль, чтобы он выглядел, как и все другие кнопки панели инструментов. Добавьте это к ресурсам панели инструментов или, еще лучше, стилизуйте свои панели инструментов, чтобы установить это в стиле, чтобы вам не приходилось добавлять его вручную везде.
<Style TargetType="{x:Type myControls:EnhancedButton}" BasedOn="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" />
Теперь это более совместимая замена стандартного Button
контроль.
Если вы посмотрите на исходный код ButtonBase
вы увидите, что было бы трудно легко воспроизвести то, что происходит в OnMouseLeftButtonDown
а также OnMouseLeftButtonUp
переопределение.
Простой взлом, который, кажется, работает довольно хорошо, это получить от Button
следующее:
public class MyButton : Button
{
protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
{
base.OnMouseRightButtonDown(e); // this line probably optional/not required
base.OnMouseLeftButtonDown(e);
}
protected override void OnMouseRightButtonUp(MouseButtonEventArgs e)
{
base.OnMouseRightButtonUp(e); // this line probably optional/not required
base.OnMouseLeftButtonUp(e);
}
}
Тогда используйте <MyButton>
на месте <Button>
в твоем Xaml.
Я не могу гарантировать, что это будет работать правильно во всех сценариях, но, похоже, он работает должным образом, так как визуальное поведение кнопки при щелчке правой или левой кнопкой мыши одинаково.
Конечно, wpf позволяет связывать команды с элементами управления. Сначала подключите привязки команд окна.
<Window.CommandBindings>
<CommandBinding Command="{x:Static custom:Window1.RightClickCommand}"
Executed="YourRightClickMethodInCSharpFile" />
</Window.CommandBindings>
Затем зарегистрируйте привязку правой кнопкой мыши на кнопке
<Button Content="Ima Button!">
<Button.InputBindings>
<MouseBinding Command="{x:Static custom:Window1.RightClickCommand}"
MouseAction="RightClick" />
</Button.InputBindings>
</Button>
источник: WPF Command bindings - щелчки мышью
Вы можете использовать событие PreviewMouseDown. Было бы что-то вроде этого
private void buttonTest_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.RightButton == MouseButtonState.Pressed)
{
MessageBox.Show("Right");
}
else if (e.LeftButton == MouseButtonState.Pressed)
{
MessageBox.Show("Left");
}
e.Handled = true;
}
Конечно, вы можете сделать это, почему бы и нет?
Просто реализуйте события для события Mouse-Up и проверьте, была ли нажата правая кнопка мыши - если true, то вызовите свой делегат / событие, которое вы добавили ранее, например, OnRightMouseClick.
Посмотрите на странице MSDN Routed Events - и да, вы на правильном пути.
Привет,