Tuesday 12 March 2019

[ReactiveUI] Bind enum to Radio button list using a ListBox

This is an incomplete blog. The code is broke. It's the code I wrote out to ask a technical question. As often I figured it out when I wrote it all out clearly! I've yet to put the corrected code in here! I found it in an editor tab. Rather than toss it away I'll put the working version here. Because the errors I got when it was broke are nested exceptions, which are themselves cryptic.

When your radio buttons depend on something like a enum or a class.

I have a ListBox of RadioButtons and want the Selected RadioButton to bubble up such that the ListBox SelectedItem is the one clicked. Can't see what I'm doing wrong.

View:

  <ListBox x:Name="lstRecordTypes">
    <ListBox.ItemContainerStyle>
      <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="Template">
          <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBoxItem}">
              <RadioButton 
                    Content="{Binding Display}"
                    ToolTip="{Binding Display}"
                    IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent},Path=IsSelected}"/>
            </ControlTemplate>
          </Setter.Value>
        </Setter>
        <Setter Property="Margin" Value="5"/>
      </Style>
    </ListBox.ItemContainerStyle>
  </ListBox>
        public MyView()
        {
            InitializeComponent();
            this.WhenActivated(disposables => this.BindControls(disposables));
        }

        private void BindControls(CompositeDisposable disposableRegistration)
        {
            this.WhenAnyValue(v => v.ViewModel).BindTo(this, v => v.DataContext).AddTo(disposableRegistration);

            this.OneWayBind(ViewModel, vm => vm.RecordTypes,  v => v.lstRecordTypes.ItemsSource)
                .DisposeWith(disposableRegistration);
            this.Bind(ViewModel, vm => vm.SelectedRecordType, v => v.lstRecordTypes.SelectedItem)
                .DisposeWith(disposableRegistration);
            this.BindCommand(ViewModel, vm => vm.SetRecordIndex,
                                              v => v.lstRecordTypes,
                                              nameof(lstRecordTypes.SelectionChanged));
 }

ViewModel:

        private IReactiveList<MyRecordEnumLookup> _recordTypes;
        public IReactiveList<MyRecordEnumLookup> RecordTypes
        {
            get { return _recordTypes; }
            set { this.RaiseAndSetIfChanged(ref _recordTypes, value); }
        }
        
        private MyRecordEnum _selectedRadioRecordType;
        public MyRecordEnum SelectedRadioRecordType
        {
            get { return _selectedRadioRecordType; }
            set { this.RaiseAndSetIfChanged(ref _selectedRadioRecordType, value); }
        }

        public void LoadRecordTypes()
        {
            // Convert a list of enums into a ReactiveList
            var list = ((IList<MyRecordEnum>)Enum.GetValues(typeof(MyRecordEnum)))
                          .Where( e => e != MyRecordEnum.Undefined)
                          .Select(e => new MyRecordEnumLookup(x));

            RecordTypes = new ReactiveList<MyRecordEnumLookup>();
            RecordTypes.AddRange(list);
        }

        public Int32 _selectedRecordTypeInt { get; set; }   // Valid range = 1..5; 0 = Undefined
        public MyRecordEnum SelectedRecordType              // Defaults to Undefined
        {
            get { return (MyRecordEnum)this._selectedRecordTypeInt; }
            set { this._selectedRecordTypeInt = (Int32)value; }
        }

[ReactiveUI] Test command

Testing ReactiveUI Commands is almost screaming Tell don't Ask at you.

I suppose may want to set a state on completion of the method invoked when executing the Command. e.g. DoExitViewModel(), in the example. But only do this when the view needs to know a result. In this example you would've Disposed() any ViewModel dependencies; so the View can now close itself.

Do not set a property in the ViewModel just so that you test whether production code successfully completed. Use Tell Don't Ask instead.

Commands can rarely send data from the View to ViewModel. When a ViewModel needs to know under what state a Command was invokde, one would normally set a property before invoking a Command. In contrast, when the view needs to know a result, I'll always try to return that result to the view from the method executing in the ViewModel.

 public partial class MyView : Window, IViewFor<MyViewModel>
 {

    public static readonly DependencyProperty ViewModelProperty =
        DependencyProperty.Register("ViewModel", typeof(MyViewModel)
                                               , typeof(MyView));

    public MyViewModel ViewModel
    {
        get { return (MyViewModel)this.GetValue(ViewModelProperty); }
        set { this.SetValue(ViewModelProperty, value); }
    }

    object IViewFor.ViewModel
    {
        get { return this.ViewModel; }
        set { this.ViewModel = (MyViewModel)value; }
    }

    public partial class MyView()
    {
        InitializeComponent();
        this.WhenActivated(disposables => this.BindControls(disposables));        
    }
    
    private void BindControls(CompositeDisposable disposableRegistration)
    {
        this.WhenAnyValue(v => v.ViewModel)
            .BindTo(this, v => v.DataContext)
            .AddTo(disposableRegistration);

        this.BindCommand(this.ViewModel, vm => vm.ExitView,
                                          v => v.btnExit)
            .AddTo(disposableRegistration);

        this.ViewModel.ExitView.Subscribe(success => { if (success) this.Close(); });

        this.WhenAnyObservable(v => v.ViewModel.BarcodeScanned)
            .Subscribe(authenticationType => this.ShowAuthentication(authenticationType));


    }
}

public class MyViewModel
{
    public ReactiveCommand<Unit, bool> ExitView { get; private set; }            // Output to view
    public ReactiveCommand<Unit, CRUDResult> AddTemplate { get; private set; }   // Output
    public ReactiveCommand<string, AuthenticationTypes?> BarcodeScanned { get; } // Input from view + Output
    public ReactiveCommand<Unit, Unit> Boring { get; }                           // No input/output from/to view

    public IObservable<bool> CanAddTemplate { get; private set; }

    public MyViewModel()
    {

        this.CanAddTemplate = (this).WhenAnyValue(vm => vm.PageState, (EditState status) => status == EditState.Browse);
        
        this.AddTemplate = ReactiveCommand.Create(() => this.DoAddTemplate(), this.CanAddTemplate);
        this.BarcodeScanned = ReactiveCommand.Create<string, AuthenticationTypes?>(barcode => this.DoBarcodeScanned(barcode));
        this.ExitView = ReactiveCommand.CreateFromTask(async () => await Task.Run(() => this.DoExitViewModel()));
    }
    
    public bool DoExitViewModel()
    {
        // Dispose the ViewModel
        return true;
    }

    private CRUDResult DoAddTemplate()
    {
        // Do whatever you do
        this.PageState = EditState.Add;
        return new CRUDResult(null);
    }
    
    private AuthenticationTypes? DoBarcodeScanned(string barcode)
    {
        return null;
    }

}

public class MyViewModelTests
{
    [Test]
    public void Add_Button_Test()
    {
        // Arrange
        var vm = new MyViewModel();
        vm.Setup(0);
        EditState expectedStatus = EditState.Add;
        
        CRUDResult resultPage = new CRUDResult(null);

        // Act
        vm.AddTemplate.Subscribe(tf => resultPage = tf);
        vm.AddTemplate.Execute().Subscribe();

        // Assert
        Assert.AreEqual(expectedStatus, vm.PageState);
        Assert.AreEqual(null, resultPage.Message);
    }
}

Wpf custom dictionary. Notes

Wpf uses #LID to indicate the language or dialect of the dictionary which it will pick up from the current localization.

e.g.
#LID 2057 = UK location

Must include

  1. the System namespace in xaml,
  2. SpellCheck.IsEnabled="True" in the control,
  3. location of the dictionary.

Best set build action of the dictionary to embedded resource.

<Window ...
  xmlns:sys="clr-namespace:System;assembly=System">
  <!-- Must include System namespace in xaml -->

  <TextBox SpellCheck.IsEnabled="True">
    <SpellCheck.CustomDictionaries>
      <!-- List of dictionaries to use -->
      <!-- hard-coded -->
      <sys:Uri>c:\pathToDictionary\uk.lex</sys:Uri>
      <!-- OR: Embedded resource -->
      <sys:Uri>pack://application:,,,/MyWpfProject;component/uk.lex</sys:Uri>
      <!-- Must set uk.lex  Build Action = Embedded Resource -->
    </SpellCheck.CustomDictionaries>
  </TextBox>

MyWpfProject - being the name of the application, in this example.

Tuesday 26 February 2019

[ReactiveUI] Bind a list of enums to WPF combo

Originally posted by: Mitkins, at: Oct 12 '17 at 0:22, at stackoverflow. I edited it to correct minor typos in the original. And to make one of two alterations.

I am trying to find a simple example where the enums are shown as is. All examples I have seen tries to add nice looking display strings but I don't want that complexity. Note: EffectStyle is just an enum.

public enum EffectStyle { Undefined = 0, Yes = 1, No = 2 }

Basically I have a class that holds all the properties that I bind, by first setting the DataContext to this class, and then specifying the binding like this in the xaml file:

<ComboBox ItemsSource="{Binding Path=EffectStyle}"/>

But this doesn't show the enum values in the ComboBox as items.


Using ReactiveUI, I've created the following alternate solution. It's not an elegant all-in-one solution, but I think at the very least it's readable.

In my case, binding a list of enum to a control is a rare case, so I don't need to scale the solution across the code base. However, the code can be made more generic by changing EffectStyleLookup.Item into an Object. I tested it with my code, no other modifications are necessary. Which means the one helper class could be applied to any enum list. Though that would reduce its readability - ReactiveList<EnumLookupHelper> doesn't have a great ring to it.

Using the following helper class:

public class EffectStyleLookup
{
    public EffectStyle Item { get; set; }
    public string Display { get { return Item.ToString();} }

    public EffectStyleLookup(){ }

    public EffectStyleLookup(EffectStyle item)
    {
        this.Item = item;
    }
}

Yes. I prefer using constructors. All things considered, you write less code.

In the ViewModel, convert the list of enums and expose it as a property:

public MyViewModel : ReactiveObject
{
  private ReactiveList<EffectStyleLookup> _effectStyles;
  public ReactiveList<EffectStyleLookup> EffectStyles
  {
    get { return _effectStyles; }
    set { this.RaiseAndSetIfChanged(ref _effectStyles, value); }
  }

  // See below for more on this
  private EffectStyle _selectedEffectStyle;
  public EffectStyle SelectedEffectStyle
  {
    get { return _selectedEffectStyle; }
    set { this.RaiseAndSetIfChanged(ref _selectedEffectStyle, value); }
  }

  public MyViewModel()
  {
    this.LoadEffectStyleCombo();
  }

  private void LoadEffectStyleCombo()
  {
    // Convert a list of enums into a ReactiveList
    var xs = ((IList<EffectStyle>)Enum.GetValues(typeof(EffectStyle)))
                  .Select( x => new EffectStyleLookup(x) );

    EffectStyles = new ReactiveList<EffectStyleLookup>();
    EffectStyles.AddRange(xs);
  }
}

In the ComboBox, utilise the SelectedValuePath property, to bind to the original enum value:

<ComboBox x:Name="cboEffectStyle" DisplayMemberPath="Display" SelectedValuePath="Item" />

In the View, this allows us to bind the original enum to the SelectedEffectStyle in the ViewModel, but display the ToString() value in the ComboBox:


public MyView()
{
    InitializeComponent();
    this.WhenActivated(disposables => this.BindControls(disposables));
}

private void BindControls(CompositeDisposable disposableRegistration)
{
    this.OneWayBind(ViewModel, vm => vm.EffectStyles, v => v.cboEffectStyle.ItemsSource).DisposeWith(disposableRegistration);
    this.Bind(ViewModel, vm => vm.SelectedEffectStyle, v => v.cboEffectStyle.SelectedValue).DisposeWith(disposableRegistration);
}