【问题标题】:Is there an event that fires when an element becomes inactive当元素变为非活动状态时是否会触发事件
【发布时间】:2019-05-03 12:17:27
【问题描述】:

我有一个与 plc 通信的 WPF-MVVM 应用程序。当按下按钮时,PLC 中的布尔变量应保持 true,如果其释放或失去焦点或变为非活动状态,则该变量应再次变为 false

我用事件实现了按钮。 (同时有一个PreviewTouchUpPreviewTouchDown 事件,因为有多点触控屏幕。)

<UserControl x:Class="..."
    ...
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    ... >
    ...
    <Button>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="PreviewMouseLeftButtonDown">
                <i:InvokeCommandAction Command="{Binding CmdSetFlag}" CommandParameter="PlcVariable" />
            </i:EventTrigger>
            <i:EventTrigger EventName="PreviewMouseLeftButtonUp">
                <i:InvokeCommandAction Command="{Binding CmdResetFlag}" CommandParameter="PlcVariable" />
            </i:EventTrigger>
            <!-- followed by touch events -->
        </i:Interaction.Triggers>
    </Button>
    ...
</UserControl>

在我的 ViewModel 中是命令CmdSetFlagCmdResetFlag

public MyViewModel : INotifyPropertyChanged
{

    public MyViewModel(string pageName)
    {
        CmdResetFlag = new ActionCommand((p) => MyPlc.WriteVar(p.ToString(), false));
        CmdSetFlag = new ActionCommand((p) => MyPlc.WriteVar(p.ToString(), true));
    }

    /// <summary>
    ///   Sets a bool on the plc to false.
    /// </summary>
    public ICommand CmdResetFlag { get; private set; }

    /// <summary>
    ///   Sets a bool on the plc to true.
    /// </summary>
    public ICommand CmdSetFlag { get; private set; }
}

因此,如果鼠标或触摸按下,事件将触发,并以 plc 变量名称作为参数调用命令。

然后有一个 plc 客户端类写入 plc 本身:

/// <summary>
///   MyPlc is a static wrapper class.
///   It sends write and read commands to the actual plc implementation.
/// </summary>
public class MyPlc
{
    static MyPlc()
    {
        MyActualPlc = new ...Plc();
    }

    /// <summary>
    ///   The actual plc interface in use. There may any type of plc.
    /// </summary>
    private IPlc MyActualPlc { get; set; }

    /// <summary>
    ///   Write a variable to the plc.
    /// </summary>
    /// <param name="varName">The name of the variable.</param>
    /// <param name="value">The value to write.</param>
    public void WriteVar(string varName, object value)
    {
        int myHandle = PlcClient.CreateVariableHandle(varName);
        PlcClient.WriteAny(myHandle, value);
        PlcClient.DeleteVariableHandle(myHandle);
    }
}

鼠标或触摸被释放,变量再次变为假。所以 plc 上的值在按钮被释放之前保持为真。

在出现诸如在事件中打开的窗口之类的中断之前,它可以正常工作。然后PreviewMouseUpPreviewTouchUp 事件都不会触发。

我也试过LostFocus,但没有成功。

有没有可以用来代替PreviewMouseUpPreviewTouchUp的事件?

【问题讨论】:

  • 不会点击吗?这只在您实际释放鼠标或按下回车时执行。触摸我虽然不知道:)
  • 问题是,一些变量应该保持真实,直到按钮被释放。例如,有一个泵应该运行直到变量变为 false。
  • 按钮有一个属性IsPressed,只要按钮被按下,它就保持为真。您可以尝试将布尔视图模型属性绑定到按钮的 IsPressed 属性。但请注意,如果您继续按住鼠标按钮,则 IsPressed 会转到false,但暂时将指针移出按钮。从您的描述中我不清楚是否需要这样做。这是一个快速的 XAML sn-p 来试验这种行为:&lt;Button x:Name="Button" Content="Press" /&gt; &lt;CheckBox Content="It's pressed" IsChecked="{Binding IsPressed, ElementName=Button, Mode=OneWay}" /&gt;
  • @EdPlunkett: IsPressed 听起来不错,但我无法添加事件监听器。
  • @AndyU。给我看代码和错误信息。解释你想要做什么。你把我弄丢了。

标签: c# wpf mvvm


【解决方案1】:

也许this 是解决方案。

通过使用通过行为添加的事件IsEnabledChanged(和IsVisibleChanged),为我解决了问题。

也许这只是因为按钮在打开导致我在问题中描述的中断的窗口时被禁用。

<UserControl x:Class="..."
    ...
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:ext="clr-namespace:NamespaceOfMyBehavior"
    ... >
    ...
        <Button...>
            <!-- The triggers looks like above -->
            <i:Interaction.Triggers>...</i:Interaction.Triggers>  
            <i:Interaction.Behaviors>
                <ext:PlcButtonBehavior PlcVar="PlcVar"/>
            </i:Interaction.Behaviors>
        </Button>
        ...
</UserControl>

Behavior 类看起来像

public class PlcButtonBehavior : Behavior<Button>
{
    public static readonly DependencyProperty InvalidValueProperty =
        DependencyProperty.Register(
            "InvalidValue", typeof(string), typeof(PlcButtonBehavior),
            new PropertyMetadata(string.Empty, (d, e) =>
                (PlcButtonBehavior)d).Update()));

    public string PlcVar
    {
        get { return (string)GetValue(InvalidValueProperty); }
        set { SetValue(InvalidValueProperty, value); }
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.IsEnabledChanged += AssociatedObject_IsEnabledChanged;
        AssociatedObject.IsVisibleChanged += IsVisibleChanged_IsVisibleChanged;
        Update();
    }

    protected override void OnDetaching()
    {
        AssociatedObject.IsEnabledChanged -= AssociatedObject_IsEnabledChanged;
        AssociatedObject.IsVisibleChanged -= IsVisibleChanged_IsVisibleChanged;
        base.OnDetaching();
    }

    private void AssociatedObject_IsEnabledChanged(
        object sender, DependencyPropertyChangedEventArgs e)
    {
        Update();
    }

    private void IsVisibleChanged_IsVisibleChanged(
        object sender, DependencyPropertyChangedEventArgs e)
    {
        Update();
    }

    private void Update()
    {
        if (AssociatedObject is Button button
            && button.IsVisible
            && button.IsEnabled
            && AssociatedObject.IsPressed)
        {
            Plc.SetFlag(PlcVar);           
        }
        else
        {
            Plc.ResetFlag(PlcVar);             
        }
    }
}

要将行为中的事件设置为触发器,它不起作用。

【讨论】:

    【解决方案2】:

    “在出现诸如在事件上打开的窗口之类的中断之前工作正常”,我不确定我是否理解正确,当您的控件(或应用程序)失去焦点时,您的事件无法正常工作? 也许您看起来像 Application.Deactivated Event - 当应用程序不再是前台应用程序时发生。

    【讨论】:

    • 当边框变得可见时,当鼠标获得另一个焦点时,我还必须获得事件。有一个消息边框而不是一个窗口。如果有消息,则设置一个变量,并且边框通过绑定变为可见。边框获得焦点,但按钮的LostFocus 事件未触发。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-04
    • 2014-10-18
    • 2012-07-28
    • 2020-09-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多