2

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.

Dustin
  • 216
  • 1
  • 9
  • 1
    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 Nov 28 '18 at 10:17
  • 2
    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 Nov 28 '18 at 10:17
  • @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](https://docs.microsoft.com/en-us/dotnet/api/system.windows.uielement.ismousedirectlyover?view=netframework-4.7.2#System_Windows_UIElement_IsMouseDirectlyOver). 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 Nov 28 '18 at 21:29
  • @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 Nov 28 '18 at 21:33

2 Answers2

3

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.

Glenn Watson
  • 2,758
  • 1
  • 20
  • 30
  • Didn't even think to look at other parameter options. Still wrapping my head around the ReactiveUI concepts so leaning heavily on examples out there on the web. Unfortunately, a lot of examples use things that are now deprecated so have to piece meal certain scenarios together. Thanks again Glenn! – Dustin Nov 30 '18 at 22:23
  • 1
    btw, feel free to jump on the RxUI slack channel, we are here to help, and if there are any deprecated examples on the documentation website feel free to open issues at github.com/reactiveui/website – Glenn Watson Nov 30 '18 at 23:33
0

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.

john-g
  • 874
  • 6
  • 14
  • 1. I assume you mean create a TriggerBase object and add it to the button's triggers purely in code behind? 2. This approach would be to create a view and ViewModel for a DataTemplate specific for this interaction piece and bind through its ViewModel property? Sure.. This is all feasible, but a lot of work to pull off something that was once so simple. I'm leaning towards even using the button's Tag property to bind to the ViewModel's property. – Dustin Nov 27 '18 at 22:12
  • I have used Tag several times in this way to accomplish various things. Still use it for passing a value to my virtual keyboard. But actually I meant adding the style directly to the button's xaml not the code behind. Either way I would be curious to see what ends up working. – john-g Nov 28 '18 at 13:44