从WPF中的子控件动态更改自定义控件的外观

问题描述

我正在使用InputSimulatorCore NuGet软件包创建WPF触摸屏键盘。我创建了一个Keyboard自定义控件,它基本上是一个包含几个KeyboardButton自定义控件的网格。

首先,我创建了一个自定义控件,该控件可以包含图像和一些文本作为KeyboardButton自定义控件的基础。

public class ImageTextButton : Button
{
    // Constructors
    static ImageTextButton() => DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageTextButton),new FrameworkPropertyMetadata(typeof(ImageTextButton)));

    // Dependency properties
    public string ItemName { ... }
    public static readonly DependencyProperty ItemNameProperty = ...;
    public virtual ImageSource Source { ... }
    public static readonly DependencyProperty SourceProperty = ...;
    public double ImageHeight { ... }
    public static readonly DependencyProperty ImageHeightProperty = ...;
    public double ImageWidth { ... }
    public static readonly DependencyProperty ImageWidthProperty = ...;
    public Brush EllipseFill { ... }
    public static readonly DependencyProperty EllipseFillProperty = ...;
    public Brush FontColor { ... }
    public static readonly DependencyProperty FontColorProperty = ...;

    public static P FindVisualParent<P>(DependencyObject dep) where P : DependencyObject
    {
        while (dep != null)
        {
            if ((dep is Visual) || (dep is System.Windows.Media.Media3D.Visual3D))
            {
                if (dep is P)
                {
                    return (dep as P);
                }
                dep = VisualTreeHelper.GetParent(dep);
            }
            else
            {
                dep = LogicalTreeHelper.GetParent(dep);
            }
        }
        return (null);
    }
}

接下来,我像这样从ImageTextButton继承来创建一个基本的KeyboardButton控件,该控件可以调用InputSimulator对象发送按键。

public class KeyboardButton : ImageTextButton
{
    protected Keyboard _keyboard;
    protected InputSimulator _sim = new InputSimulator();
    public ImageSource Sourcenormal { ... }
    public static readonly DependencyProperty SourcenormalProperty = ...;
    public ImageSource SourceShift { ... }
    public static readonly DependencyProperty SourceShiftProperty = ...;
    public virtual bool Shifted { ... }
    public static readonly DependencyProperty ShiftedProperty = ...;

    static KeyboardButton() { }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        this._keyboard = FindVisualParent<Keyboard>(this);
    }
}

然后,我定义了一个CharacterOutputButton,它可以发送实际的按键操作,就像用户键盘上键入的一样:

public class CharacterOutputButton : KeyboardButton
{
    public VirtualKeyCode OutputCode { ... } // From the InputSimulator NuGet
    public static readonly DependencyProperty OutputCodeProperty = ...;
    public string Output { ... }
    public static readonly DependencyProperty OutputProperty = ...;
    public string OutputShift { ... }
    public static readonly DependencyProperty OutputShiftProperty = ...;

    static CharacterOutputButton() { }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        // Sets the click event,is there a better way to do this?
        Click += (s,e) =>
        {
            _keyboard.Target.Focus();
            if (OutputCode > 0)
            {
                _sim.Keyboard.KeyPress(OutputCode);
            }
            else
            {
                _sim.Keyboard.TextEntry(Output);
            }
        };
    }
}

最后,我想实现CapsLockKey自定义控件,该控件也继承自KeyboardButton,以切换大写锁定:

public class CapsLockKey : KeyboardButton
{
    public override ImageSource Source // Update property when it's called?
    {
        get => NativeMethods.GetKeyState((ushort)VirtualKeyCode.CAPITAL) > 0 ? Sourcenormal : SourceShift;
        set => base.source = NativeMethods.GetKeyState((ushort)VirtualKeyCode.CAPITAL) > 0 ? Sourcenormal : SourceShift;
    }

    static CapsLockKey() { }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        Click += (s,e) =>
        {
            _sim.Keyboard.KeyPress(VirtualKeyCode.CAPITAL);
            if (NativeMethods.GetKeyState((ushort)VirtualKeyCode.CAPITAL) > 0)
            {
                Source = Sourcenormal;
            }
            else
            {
                Source = SourceShift;
            }
        };
    }
}

如您所见,在按下Caps Lock键时,我以前尝试过很多方法(例如ShiftShifted属性)来实现样式更改,但是没有有用。

我想我有两个具体问题:

  1. 按下Caps Lock时如何动态更新我的“字符输出”按钮的样式?

  2. 如果更改键盘样式(例如重新排列按键以匹配其他键盘布局(QWERTY与AZERTY)),如何保持此状态

解决方法

您可以使用VisualStateManager class在控件的不同视觉状态之间动态转换:

if (someElement.IsCapsLockPressed)
{
    VisualStateManager.GoToElementState(rect,"SomeState",true);
}
else
{
    VisualStateManager.GoToElementState(rect,"SomeOtherState",true);
}