1
votes

Caveat:
Brand new to ReactiveUI. Trying to go full on RxUI with the intention of never setting a view's DataContext. I believe this can be done!

Scenario:
I have a button and the button has a style and that button's style has a MultiDataTrigger with first condition being IsMouseOver and second condition being a property on my ViewModel of type bool. This then changes the background or foreground of the button. Traditionally, you would just bind that second condition to the ViewModel's property.

Is there a feasible way for this interaction to work without using a DataContext yet still have the same outcome I expect? I can't directly access the condition binding since it doesn't have a name. So there has to be some weird setup to get this to work.

Solution not really a fan of:
I could potentially add a control that isn't visible, give it a name, and use the this.OneWayBind() to bind that property to the "IsEnabled" property of this control. In doing so, my second condition of the button's MultiDataTrigger could then use binding based on ElementName and its path of IsEnabled. This would allow me to NOT use a DataContext but seems way too "hacky" for my taste. There's gotta be another way!

Thanks in advance!

Edit 1 - Attempt 1 based off Glenn Watson's comment to the post

        this.WhenActivated(disposables =>
        {
            this.WhenAnyValue(x => x.TheButton.IsMouseOver, x => x.ViewModel.SomeBoolValue).Subscribe(x =>
            {
                if (!x.Item1)
                    TheButton.Background = x.Item2 ? Brushes.Gray : Brushes.Blue;
                else
                    TheButton.Background = x.Item2 ? Brushes.Red : Brushes.Green;
            }).DisposeWith(disposables);
        });

Edit 2 - implemented/using Glenn Watson's answer.

2
You could use the ReactiveUI.Events.WPF nuget package, and get the MouseOver observable, and use Observable.CombineLatest() with the WhenAnyValue() from your ViewModel, and use the BindTo() method I am guessing.Glenn Watson
I am pretty suspicious all the people down voting this question are traditional WPF developers and not really familiar with RxUI style binding.Glenn Watson
@GlennWatson RE:First Response: Nearly nailed it but it got me in the right direction! There is no event for UIElement MouseOver and no subsequent Events.WPF Observable. Closest I got was the UIElement's IsMouseDirectlyOverChanged. The name is deceiving per the remarks from Microsoft. Instead I used the WhenAnyValue on the button for IsMouseOver. I edited the post. Any recommendations for a better solution utilizing more ReactiveUI?Dustin
@GlennWatson RE:Second Response: I too was taken aback by the down votes. I thought maybe it was because I didn't include actual code to work from, maybe my question was too vague, or the I was being a newbie and should know how to do it. I'm just glad I didn't tag it with MVVM as the purest would have had a witch hunt. As a once MVVM purest now recovering via safe types and ReactiveUI I knew not to get them involved! Thanks for all your help Glenn!Dustin

2 Answers

2
votes

I would recommend something close to the solution you have:

this.WhenAnyValue(x => x.TheButton.IsMouseOver, x => x.ViewModel.SomeBoolValue,
      (isMouseOver, boolValue) => 
      {
         if (isMouseOver)
           return boolValue ? Brushes.Gray : Brushes.Blue;
         else 
           return boolValue ? Brushes.Red : Brushes.Green;
      })
      .BindTo(this, view => view.TheButton.Background)
      .DisposeWith(disposables);

The modifications are using the third parameter which takes in a parameterized lambda to the previous two values, then just using BindTo(). It shouldn't be that dissimilar to what you've got.

0
votes

I have a couple of ideas. Dont' have time to try them first but they might help.

  1. You could try embedding the trigger in the button's resources directly and then still using the button name for the onewaybind.

  2. You could try defining a new DataTemplate for the button and target your viewmodel that has the trigger properties.