MVVM / WPF:解除绑定和重新绑定属性

时间:2017-06-26 22:49:14

标签: c# wpf mvvm

注意:我不确定该怎么称呼此问题,但它与取消绑定/重新绑定属性有关。

我有一个用编码器控制电机的arduino。我想创建一个接口来控制它,同时读取它的位置(使用编码器)。

观点: 我有一个文本框来显示电机位置的数字读数(DRO)。它的Text属性绑定到ViewModel的公共属性。

我希望能够在看到DRO的同时向Arduino发送所需的位置。换句话说,TextBox应该不断输出电机的位置,但是一旦我开始输入一个值,就把这个值发送给Arduino(有一些格式,所以Arduino知道如何处理它)。

现在正在发生的事情是,我有一个串行数据接收事件处理程序,它获取电机位置并将其设置为绑定属性,然后将其显示在文本框中。我需要知道一种方法,当它具有Focus时暂时禁用文本框的绑定。然后它需要在失去焦点后重新绑定。

有没有办法禁用控件的绑定,然后在事件后重新启用绑定?这是解决此问题的最佳方法吗?

感谢您的帮助!

4 个答案:

答案 0 :(得分:1)

不是禁用绑定(不确定如何),而是在将值从Arduino发送到UI之前添加条件。我建议您尝试使用此SO answer中的IsFocused属性。

FocusExtension:

public static class FocusExtension
{
    public static bool GetIsFocused(DependencyObject obj)
    {
        return (bool) obj.GetValue(IsFocusedProperty);
    }

    public static void SetIsFocused(DependencyObject obj, bool value)
    {
        obj.SetValue(IsFocusedProperty, value);
    }

    public static readonly DependencyProperty IsFocusedProperty =
        DependencyProperty.RegisterAttached(
            "IsFocused", typeof (bool), typeof (FocusExtension),
            new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));

    private static void OnIsFocusedPropertyChanged(
        DependencyObject d, 
        DependencyPropertyChangedEventArgs e)
    {
        var uie = (UIElement) d;
        if ((bool) e.NewValue)
        {
            uie.Focus(); // Don't care about false values.
        }
    }
}

用法:

<TextBox local:FocusExtension.IsFocused="{Binding IsDigitalReadOutFocused}" />

为视图模型创建一种方法,以了解控件是否已被聚焦。

public class ViewModel : ObservableBase // Made this up. It should implement INotifyPropertyChanged
{
    private bool _isDROFocused { get; set; }

    public bool IsDigitalReadOutFocused
    {  
        get { return this._isDROFocused; }
        set
        {
            this._isDROFocused = value;
            OnPropertyChanged("IsDigitalReadOutFocused");
        }
    }

    // On your Serial Data Received event handler

    //if(!IsDigitalReadOutFocused)
    //{
    //  DigitalReadOut = somevalue; // Set the textbox value
    //}
}

答案 1 :(得分:0)

如果我理解你可能需要触发文本框并使用bool值绑定触发器。 这是按钮触发器的示例

          <Style>
            <Setter Property="Content" Value="Scream" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding btnEnabled}" Value="True">
                    <Setter Property="IsEnabled" Value="True" />
                </DataTrigger>
            </Style.Triggers>
        </Style>

答案 2 :(得分:0)

  

有没有办法禁用控件的绑定,然后在事件后重新启用绑定?

你不能&#34;禁用&#34;绑定,但您可以使用BindingOperations.ClearBinding方法以编程方式删除绑定,并且可以使用BindingOperations.SetBinding方法以编程方式创建绑定。

<强> XAML:

<TextBox x:Name="textBox" GotKeyboardFocus="textBox_GotKeyboardFocus" LostKeyboardFocus="textBox_LostKeyboardFocus" />

示例代码:

private void textBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    BindingOperations.ClearBinding(textBox, TextBox.TextProperty);
}

private void textBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    BindingOperations.SetBinding(textBox, TextBox.TextProperty, new Binding("ThePropertyToBindTo"));
}

答案 3 :(得分:0)

我将使用我在StackOverflow周围找到的详细信息来回答我的问题。

首先,我需要处理GotFocus和LostFocus事件。我不想使用Code Behind,所以我发现我可以使用System.Windows.Interactivity.dll参考中的Interactivity。 (来自THIS文章)

视图模型:

using System.Windows.Interactivity;

private bool _xFocus;

public ICommand XGotFocus { get; set; }
public ICommand XLostFocus { get; set; }
public ICommand XSend { get; set; }

// In the constructor:
XGotFocus = new RelayCommand(() => _xFocus = true);
XLostFocus = new RelayCommand(() => _xFocus = false);
XSend = new RelayCommand(() => ExecuteXSend());
// Done with constructor

private void ExecuteXSend()
{
    RaisePropertyChanged("Xdro");
    string sendToPort = "X" + Xdro;

    try
    {
        port.WriteLine(sendToPort);
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error: \r\r" + ex.Message);
    }

    Console.WriteLine("Sending X position: " + sendToPort);
    MotorsMoving = true;
    RaisePropertyChanged("MotorsMoving");
}

查看:

<TextBox x:Name="tbXDro" HorizontalAlignment="Center" VerticalAlignment="Center" Width="75" IsReadOnly="False" FontSize="11" MaxLines="1" Text="{Binding Xdro, UpdateSourceTrigger=PropertyChanged}" local:InputBindingsManager.UpdatePropertySourceWhenEnterPressed="TextBox.Text">
<i:Interaction.Triggers>
    <i:EventTrigger EventName="GotFocus">
        <i:InvokeCommandAction Command="{Binding XGotFocus, Mode=OneWay}"/>
    </i:EventTrigger>
        <i:EventTrigger EventName="LostFocus">
            <i:InvokeCommandAction Command="{Binding XLostFocus, Mode=OneWay}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <TextBox.InputBindings>
        <KeyBinding Command="{Binding XSend}" Key="Return"/>
    </TextBox.InputBindings>    
</TextBox>

正如您所看到的,我对命令的返回键有一个键绑定&#34; XSend&#34;。为了更新Xdro属性,THIS文章介绍了在按下回车键时更新属性的方法。

InputBindingsManager.cs

public static class InputBindingsManager
{

    public static readonly DependencyProperty UpdatePropertySourceWhenEnterPressedProperty = DependencyProperty.RegisterAttached(
        "UpdatePropertySourceWhenEnterPressed", typeof(DependencyProperty), typeof(InputBindingsManager), new PropertyMetadata(null, OnUpdatePropertySourceWhenEnterPressedPropertyChanged));

    static InputBindingsManager()
    {

    }

    public static void SetUpdatePropertySourceWhenEnterPressed(DependencyObject dp, DependencyProperty value)
    {
        dp.SetValue(UpdatePropertySourceWhenEnterPressedProperty, value);
    }

    public static DependencyProperty GetUpdatePropertySourceWhenEnterPressed(DependencyObject dp)
    {
        return (DependencyProperty)dp.GetValue(UpdatePropertySourceWhenEnterPressedProperty);
    }

    private static void OnUpdatePropertySourceWhenEnterPressedPropertyChanged(DependencyObject dp, DependencyPropertyChangedEventArgs e)
    {
        UIElement element = dp as UIElement;

        if (element == null)
        {
            return;
        }

        if (e.OldValue != null)
        {
            element.PreviewKeyDown -= HandlePreviewKeyDown;
        }

        if (e.NewValue != null)
        {
            element.PreviewKeyDown += new KeyEventHandler(HandlePreviewKeyDown);
        }
    }

    static void HandlePreviewKeyDown(object sender, KeyEventArgs e)
    {
        if (e.Key == Key.Enter)
        {
            DoUpdateSource(e.Source);
        }
    }

    static void DoUpdateSource(object source)
    {
        DependencyProperty property =
            GetUpdatePropertySourceWhenEnterPressed(source as DependencyObject);

        if (property == null)
        {
            return;
        }

        UIElement elt = source as UIElement;

        if (elt == null)
        {
            return;
        }

        BindingExpression binding = BindingOperations.GetBindingExpression(elt, property);

        if (binding != null)
        {
            binding.UpdateSource();
        }
    }
}

所有这一切都实现了目标:

  1. 通过文本框绑定显示电机的位置
  2. 当文本框被聚焦时,它是可编辑的,当按下Enter / Return键时,它会将输入的值发送到Arduino
  3. 在失去焦点后,它会回到DRO视图。
  4. 感谢所有人的帮助!