WPF中ButtonBase.IsPressed设置器不可访问,求实现按钮按压缩放功能
Hey there! Let's fix that button resizing behavior you're aiming for. The issue with your current code is that the Click event fires when you release the button, not when you first press it—so the resize happens too late. And as you noticed, ButtonBase.IsPressed is a read-only dependency property, which means you can't set it directly (that's the root cause of that "set accessor is inaccessible" error). Instead, we can react to its changes or use the right events to get the exact behavior you want: resizing only while the button is being pressed.
Solution 1: Use WPF Triggers (Cleanest, Declarative Approach)
WPF's built-in triggers are perfect for this kind of visual state change—no messy code-behind needed! You can define a style that automatically animates the button's size when IsPressed is true, and reverts it when the state changes back to false.
Step 1: Add a Multiplier Converter
First, create a simple converter to scale the button's size:
public class MultiplyConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is double baseValue && double.TryParse(parameter.ToString(), out double multiplier)) { return baseValue * multiplier; } return value; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
Step 2: Define the Button Style in XAML
Add the converter to your window resources, then style the button with triggers for the pressed state:
<Window.Resources> <local:MultiplyConverter x:Key="MultiplyConverter"/> </Window.Resources> <Button x:Name="SearchMedicationButton" Width="436" Height="593" Content="Search"> <Button.Style> <Style TargetType="Button"> <Style.Triggers> <Trigger Property="IsPressed" Value="True"> <Trigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" To="{Binding RelativeSource={RelativeSource Self}, Path=Width, Converter={StaticResource MultiplyConverter}, ConverterParameter=0.8}" Duration="0:0:0.2"/> <DoubleAnimation Storyboard.TargetProperty="Height" To="{Binding RelativeSource={RelativeSource Self}, Path=Height, Converter={StaticResource MultiplyConverter}, ConverterParameter=0.8}" Duration="0:0:0.2"/> </Storyboard> </BeginStoryboard> </Trigger.EnterActions> <Trigger.ExitActions> <BeginStoryboard> <Storyboard> <DoubleAnimation Storyboard.TargetProperty="Width" To="{Binding RelativeSource={RelativeSource Self}, Path=Width}" Duration="0:0:0.2"/> <DoubleAnimation Storyboard.TargetProperty="Height" To="{Binding RelativeSource={RelativeSource Self}, Path=Height}" Duration="0:0:0.2"/> </Storyboard> </BeginStoryboard> </Trigger.ExitActions> </Trigger> </Style.Triggers> </Style> </Button.Style> </Button>
This approach keeps your code clean and leverages WPF's native state management. The animation triggers instantly when the button is pressed, and reverts automatically when it's released (even if the mouse drags off the button mid-press!).
Solution 2: Code-Behind with Mouse Events
If you prefer handling logic in code-behind, replace your Click and MouseLeave events with MouseDown (triggers on press) and MouseUp/MouseLeave (triggers to revert size):
public partial class MainWindow : Window { private readonly double _originalWidth = 436; private readonly double _originalHeight = 593; private bool _isButtonPressed = false; public MainWindow() { InitializeComponent(); } private void SearchMedicationButton_OnMouseDown(object sender, MouseButtonEventArgs e) { _isButtonPressed = true; AnimateButton(_originalWidth, _originalWidth * 0.8); } private void SearchMedicationButton_OnMouseUp(object sender, MouseButtonEventArgs e) { if (_isButtonPressed) { _isButtonPressed = false; AnimateButton(_originalWidth * 0.8, _originalWidth); } } private void SearchMedicationButton_OnMouseLeave(object sender, MouseEventArgs e) { if (_isButtonPressed) { _isButtonPressed = false; AnimateButton(_originalWidth * 0.8, _originalWidth); } } private void AnimateButton(double fromSize, double toSize) { // Maintain aspect ratio double fromHeight = fromSize * (_originalHeight / _originalWidth); double toHeight = toSize * (_originalHeight / _originalWidth); DoubleAnimation widthAnim = new DoubleAnimation(fromSize, toSize, TimeSpan.FromSeconds(0.2)); DoubleAnimation heightAnim = new DoubleAnimation(fromHeight, toHeight, TimeSpan.FromSeconds(0.2)); SearchMedicationButton.BeginAnimation(Button.WidthProperty, widthAnim); SearchMedicationButton.BeginAnimation(Button.HeightProperty, heightAnim); } }
This ensures the button resizes the moment you press it, and reverts when you release or drag away the mouse. The _isButtonPressed flag prevents duplicate animations from firing accidentally.
Solution 3: Listen to IsPressed Property Changes
You can also subscribe to changes of the IsPressed property directly, which lets you react whenever the button's pressed state changes:
public partial class MainWindow : Window { private readonly double _originalWidth = 436; private readonly double _originalHeight = 593; public MainWindow() { InitializeComponent(); // Subscribe to IsPressed property changes var propertyDescriptor = DependencyPropertyDescriptor.FromProperty(Button.IsPressedProperty, typeof(Button)); propertyDescriptor.AddValueChanged(SearchMedicationButton, OnButtonPressedStateChanged); } private void OnButtonPressedStateChanged(object sender, EventArgs e) { if (SearchMedicationButton.IsPressed) { AnimateButton(_originalWidth, _originalWidth * 0.8); } else { AnimateButton(_originalWidth * 0.8, _originalWidth); } } private void AnimateButton(double fromSize, double toSize) { double fromHeight = fromSize * (_originalHeight / _originalWidth); double toHeight = toSize * (_originalHeight / _originalWidth); DoubleAnimation widthAnim = new DoubleAnimation(fromSize, toSize, TimeSpan.FromSeconds(0.2)); DoubleAnimation heightAnim = new DoubleAnimation(fromHeight, toHeight, TimeSpan.FromSeconds(0.2)); SearchMedicationButton.BeginAnimation(Button.WidthProperty, widthAnim); SearchMedicationButton.BeginAnimation(Button.HeightProperty, heightAnim); } }
This is a middle ground between XAML triggers and code-behind events—it uses WPF's built-in state tracking without needing to handle multiple mouse events.
Quick Note on IsPressed's Inaccessible Setter
To clarify why you got that error: ButtonBase.IsPressed is a read-only dependency property managed internally by WPF. It reflects the button's current state based on user input, so you can't set it manually—you can only observe its value to react to changes.
Hope one of these solutions works perfectly for you! Let me know if you need any tweaks.
内容的提问来源于stack exchange,提问作者P.Roger

