Question

I have 2 touch enabled Canvas' in a Silverlight App. What I need to do is when a person Holds (presses and keeps pressing) both canvases at the same time increment a value on the screen once. This should happen for every "double" hold. I can do that fine using normal events but tried writing the same thing using RX and I am getting stuck.

Currently my code looks identical to the separate events approach (using global variables and global method) but I think there must be a better way to compose this. Can anyone suggest a better approach?

        var leftHold = Observable.FromEvent<TCanvas.HoldHandler, GestureHoldEventArgs>(
                h => new TCanvas.HoldHandler(h),
                h => HoldLeft.Hold += h,
                h => HoldLeft.Hold += h
            );

        var rightHold = Observable.FromEvent<TCanvas.HoldHandler, GestureHoldEventArgs>(
                h => new TCanvas.HoldHandler(h),
                h => HoldRight.Hold += h,
                h => HoldRight.Hold += h
            );

        var rightRelease = Observable.FromEvent<TCanvas.ReleaseHandler, EventArgs>(
                h => new TCanvas.ReleaseHandler(e => { }),
                h => HoldRight.Release += h,
                h => HoldRight.Release += h
            );


        var leftRelease = Observable.FromEvent<TCanvas.ReleaseHandler, EventArgs>(
                h => new TCanvas.ReleaseHandler(e => { }),
                h => HoldLeft.Release += h,
                h => HoldLeft.Release += h
            );

        leftHold.Subscribe(e =>
        {
            _leftHeld = true;
            DoCheck();
        });

        rightHold.Subscribe(e =>
            {
                _rightHeld = true;
                DoCheck();
            });

        rightRelease.Subscribe(e =>
        {
            _rightHeld = false;
            DoCheck();
        });


        leftRelease.Subscribe(e =>
        {
            _leftHeld = false;
            DoCheck();
        });

And the very basic DoCheck function looks like this....

    private void DoCheck()
    {
        if (_rightHeld && _leftHeld)
        {
            MyTextBox.Text = (Convert.ToInt32(MyTextBox.Text) + 1).ToString() ;
        }
    }

Hopefully you can see what I am trying to do. Each canvas has a hold and release event so when HoldLeft and HoldRight are both held do something until either HoldRight or HoldLeft are is released.

Any help would be appreciated.

Was it helpful?

Solution

I believe I have solved my own problem.

Rather than the multiple subscribes I went for this.

        leftHold.Zip(rightHold, (a,b) => true)
            .TakeUntil(leftRelease.Amb(rightRelease))
            .Subscribe(_ => TextOutput.Text = (Convert.ToInt32(TextOutput.Text) + 1).ToString());

It appears to do what I want but I am open to further suggestions/improvements.

OTHER TIPS

I came across this answer and thought that it didn't make sense:

    leftHold.Zip(rightHold, (a,b) => true)
        .TakeUntil(leftRelease.Amb(rightRelease))
        .Subscribe(_ => TextOutput.Text =
            (Convert.ToInt32(TextOutput.Text) + 1).ToString());
  • Zip pairs up events so would require that I release the left and the right before holding them again. If I released right and the held it again this observable wouldn't fire again.
  • I also would think that the hold events only fire once, so there shouldn't be any need for a TakeUntil call.
  • Also I don't see how this answer will refire - it seems to get only one value.

Here's an alternative:

var left = leftHold.Select(x => true).Merge(leftRelease.Select(x => false));
var right = rightHold.Select(x => true).Merge(rightRelease.Select(x => false));

var bothHold =
    left.CombineLatest(right, (l, r) => l && r)
        .Where(lr => lr == true)
        .Select(lr => (Convert.ToInt32(MyTextBox.Text) + 1).ToString());

bothHold.Subscribe(t => MyTextBox.Text = t);

I know this question is a year old, but I would love to know if I've missed something or if this answer works.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top