-1

Note: I'm not really sure what to call this problem, but it has to do with unbinding/rebinding a property.

I have an arduino that is controlling a motor with an encoder. I wanted to create an interface to control it while it reads its position (using the encoder).

The View: I have a textbox to display the digital readout (DRO) of the position of the motor. It's Text property is bound to a public property of the ViewModel.

I want to be able to send a desired position to the Arduino while also seeing the DRO. In other words, the TextBox should constantly be outputting the motor's position, but as soon as I start to enter a value, send THAT value to the Arduino (with some formatting so the Arduino knows what to do with it).

What's happening right now is, I have a Serial Data Received event handler and it gets the motor position and sets that to the bound property which, in turn, displays it in the textbox. I need to know a way to temporarily disable the binding of the textbox when it has Focus. It then needs to re-bind after it has lost focus.

Is there a way to disable the binding of a control then re-enable the binding after an event? Is this the best way to accomplish this problem?

Thanks for any help!

jojoguy10
  • 99
  • 4

4 Answers4

1

Instead of disabling the binding (Not really sure how), just add a condition before sending the value from your Arduino to your UI. I suggest you try using the IsFocused property from this SO answer.

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.
        }
    }
}

Usage:

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

Create a way for the view model to know if the control is focused or not.

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
    //}
}
jegtugado
  • 5,081
  • 1
  • 12
  • 35
  • I think this is the best answer for my question. The only thing that I have yet to accomplish is, how do I send what the USER inputs into the textbox to the Arduino? I'm still reading up on WPF (coming from WinForms), so I don't know if there's a good way to "read" the current value INSIDE of the textbox through the ViewModel. – jojoguy10 Jun 27 '17 at 15:20
  • Just create a different SO question since that has nothing to do with unbinding and rebinding a property. – jegtugado Jun 28 '17 at 01:44
0

If i understand you may need a trigger for the textbox and bind the trigger with a bool value. This is an example of a button trigger

          <Style>
            <Setter Property="Content" Value="Scream" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding btnEnabled}" Value="True">
                    <Setter Property="IsEnabled" Value="True" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
Mr ASD
  • 103
  • 1
  • 1
  • 8
0

Is there a way to disable the binding of a control then re-enable the binding after an event?

You can't "disable" the binding but you can programmatically remove a binding using the BindingOperations.ClearBinding method and you can programmatically create a binding using the BindingOperations.SetBinding method.

XAML:

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

Sample Code:

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"));
}
mm8
  • 163,881
  • 10
  • 57
  • 88
0

I'm going to answer my question with details I've found around StackOverflow.

First of all, I needed to handle the GotFocus and LostFocus events. I didn't want to use the Code Behind, so I found that I could use Interactivity from the System.Windows.Interactivity.dll reference. (From THIS article)

ViewModel:

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");
}

View:

<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>

As you can see, I have a key binding for the Return key for the command "XSend". In order to update the Xdro property, THIS article explained a way to update a property when the enter key is pressed.

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();
        }
    }
}

All of this accomplishes the goals:

  1. Show the position of the motor through the textbox binding
  2. When the textbox is focused, it is editable and when the Enter/Return key is pressed, it sends the entered value to the Arduino
  3. It goes back to the DRO view after it has lost focused.

Thanks everyone for all of the help!

jojoguy10
  • 99
  • 4