MVVM binding view to a changeable model - c#

Lets say i have a ListBox with multiple model objects, lets call it person with the normal values like name, age, familiyName and a list of objects professions with arbitrary values.
I want to create an Inspector control now, which shows the values of the selected person object but my questing is.
Should the Inspector viewmodel have its own properties (like SelectedPersonName, SelectedPersonAge etc..) to bind the view to, and update all of them in the event that the selected item in the listbox changes? Or should i implement it by simply having a reference in the inspector viewmodel referencing the selected item in the listbox, ending up with bindings like {Binding SelectedPerson.name} {Binding SelectedPerson.age} What is best practice here? is there a third way?

The second way. As long as you notify when the selected person changes, and when the properties on that person change, it will all bind correctly. And you won't need to make a bunch of new viewmodel properties.

you can use one viewModel for each model and initialize them in their constructors like this:
public class PersonVm : DependencyObject
{
public PersonVm(Model.Person model)
{
_model = model;
Name = model.Name;
Age = model.Age;
foreach (var professionModel in model.Professions)
{
Professions.Add(new ProfessionVm(professionModel));
}
}
Model.Person _model;
public int Id { get { return _model.Id; } }
//Name Dependency Property
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(PersonVm), new UIPropertyMetadata(null));
//Age Dependency Property
public int Age
{
get { return (int)GetValue(AgeProperty); }
set { SetValue(AgeProperty, value); }
}
public static readonly DependencyProperty AgeProperty =
DependencyProperty.Register("Age", typeof(int), typeof(PersonVm), new UIPropertyMetadata(0));
//Professions Observable Collection
private ObservableCollection<ProfessionVm> _professions = new ObservableCollection<ProfessionVm>();
public ObservableCollection<ProfessionVm> Professions { get { return _professions; } }
}
This is the viewModel for the whole page or window, you need to create one instance of it in the constructor of the page or window and set DataContext to it after InitializeComponent()
public class MainViewModel : DependencyObject
{
public MainViewModel(IEnumerable<Model.Person> models)
{
foreach (var personModel in models)
{
People.Add(new PersonVm(personModel));
}
}
//People Observable Collection
private ObservableCollection<PersonVm> _people = new ObservableCollection<PersonVm>();
public ObservableCollection<PersonVm> People { get { return _people; } }
//SelectedPerson Dependency Property
public PersonVm SelectedPerson
{
get { return (PersonVm)GetValue(SelectedPersonProperty); }
set { SetValue(SelectedPersonProperty, value); }
}
public static readonly DependencyProperty SelectedPersonProperty =
DependencyProperty.Register("SelectedPerson", typeof(PersonVm), typeof(MainViewModel), new UIPropertyMetadata(null));
}
This way you can bind very easily like this:
<DockPanel>
<ListBox
DockPanel.Dock="Left"
ItemsSource="{Binding People}"
SelectedItem="{Binding SelectedPerson}"
DisplayMemberPath="Name"/>
<StackPanel DataContext="{Binding SelectedPerson}">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Age}"/>
<ListBox ItemsSource="{Binding Professions}"
DisplayMemberPath="Whatever"/>
</StackPanel>
</DockPanel>
The benefit of this method is easy Binding. Also the updating of the model is done in the following way:
//Name Dependency Property
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(PersonVm),
new UIPropertyMetadata(null, (d, e) =>
{
var vm = (PersonVm)d;
var val = (string)e.NewValue;
vm._model.Name = val;
}));

Related

ListBox emtpy when bound to populated ICollectionView

I'm trying to implement filtering on a UserControl (which is essentially just a ListBox with a data template) using ICollectionView.
When I bind to the ICollectionView my LOAListBox is empty.
My xaml looks like this:
<TextBox Text="{Binding SearchString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<!-- LOA_List is a DependencyProperty which binds to ListBox.ItemsSource -->
<controls:LOAListBox LOA_List="{Binding FilteredView, Mode=OneWay}" />
And in my view model, I do this:
public class LOAViewModel : ViewModelBase
{
public ICollectionView FilteredView { get; private set; }
private string _searchString;
public string SearchString
{
get => _searchString;
set
{
_searchString = value;
RaisePropertyChanged("SearchString");
FilteredView.Refresh();
}
}
private List<LOA> _available_LOAs;
public List<LOA> Available_LOAs
{
get => _available_LOAs;
set
{
_available_LOAs = value;
RaisePropertyChanged("Available_LOAs");
}
}
public LOAViewModel()
{
Available_LOAs = data.GetLOAData();
FilteredView = CollectionViewSource.GetDefaultView(Available_LOAs);
FilteredView.Filter = new Predicate<object>(o => Filter(o as LOA));
}
private bool Filter(LOA loa)
{
return SearchString == null || loa.Display_Name.Contains(SearchString);
}
}
During debugging I can see that Available_LOAs is not empty and after GetDefaultView FilteredView also has that same collection. There aren't any binding errors. I also made by filter method always return true just to remove that possibility.
I feel like I must be missing a step but I've checked various other online examples and I can't find anything... My hunch is that it's related to the fact I'm binding to a ListBox nested in a UserControl, but I don't understand why that would matter when it works if change the binding from FilteredView to Available_LOAs directly.
Update; this is the simplified code for LOAListBox:
XAML:
<UserControl>
<ListBox ItemsSource="{Binding LOA_List, Mode=OneWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"/>
</UserControl>
Code-behind:
public partial class LOAListBox : UserControl
{
public static readonly DependencyProperty DataSource = DependencyProperty.Register(nameof(LOA_List), typeof(List<LOA>), typeof(LOAListBox), new PropertyMetadata());
public List<LOA> LOA_List
{
get => (List<LOA>)GetValue(DataSource);
set => SetValue(DataSource, value);
}
}
You cannot bind an ICollectionView to a List<T> property.
Change the type of your dependency property to IEnumerable:
public static readonly DependencyProperty DataSource = DependencyProperty.Register(nameof(LOA_List),
typeof(IEnumerable), typeof(LOAListBox), new PropertyMetadata());
public IEnumerable LOA_List
{
get => (IEnumerable)GetValue(DataSource);
set => SetValue(DataSource, value);
}
As a side note, you should also change the name of the dependency property from "DataSource" to "LOA_ListProperty" (and remove the underscore from both names) to follow the naming convention.
After going step-by-step to reproduce the issue, I eventually realised that I wasn't notifying of changes to FilteredView and, not helping matters, I was changing the ICollectionView source without reassigning the ICollectionView,
So I made my FilteredView a standard property that calls RaisePropertyChanged():
private ICollectionView _filteredView;
public ICollectionView FilteredView
{
get => _filteredView;
set
{
_filteredView = value;
RaisePropertyChanged("FilteredView");
}
}
And when I change the ICollectionView source variable I reassign based on the new source collection:
FilteredView = CollectionViewSource.GetDefaultView(Available_Destination_LOAs);
FilteredView.Filter = new Predicate<object>(o => Filter(o as LOA));

SelectedValue binding of a nested DataGrid doesn't work while ItemsSource does

I have a DataGrid inside of a UserControl which in turn lies inside of another UserControl. This is due to other needs of the project and I can't change this nested architecture. I'm binding a list of Person class to this DataGrid. This is a dumbed-down version without using a VM, but in my real project I am using a VM.
My UserControl with the DataGrid:
<Grid>
<DataGrid x:Name="MyDg"
ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=local:UCDataGrid}, UpdateSourceTrigger=PropertyChanged}"
MouseDoubleClick="MyDg_MouseDoubleClick"
SelectedValue="{Binding SelectedValue, RelativeSource={RelativeSource AncestorType=local:UCDataGrid}, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
Code Behind:
public partial class UCDataGrid : UserControl
{
public event RoutedEventHandler RoutedDataGridDoubleClick;
public UCDataGrid()
{
InitializeComponent();
}
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(object), typeof(UCDataGrid), new PropertyMetadata(null));
public object ItemsSource
{
get { return GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty SelectedValueProperty = DependencyProperty.Register("SelectedValue", typeof(object), typeof(UCDataGrid), new PropertyMetadata(null));
public object SelectedValue
{
get { return GetValue(SelectedValueProperty); }
set { SetValue(SelectedValueProperty, value); }
}
private void MyDg_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
RoutedDataGridDoubleClick?.Invoke(this, new RoutedEventArgs());
}
}
2nd UserControl that contains the above control:
<Grid>
<ContentControl Content="{Binding MyDataGrid, ElementName=ucDisplay}"/>
</Grid>
ucDisplay is simply the Name property value of this UserControl.
Code Behind:
Nothing fancy here.
public partial class UCDisplay : UserControl
{
public UCDisplay()
{
InitializeComponent();
}
public static readonly DependencyProperty MyDataGridProperty = DependencyProperty.Register("MyDataGrid", typeof(object), typeof(UCDisplay), new PropertyMetadata(null));
public object MyDataGrid
{
get { return GetValue(MyDataGridProperty); }
set { SetValue(MyDataGridProperty, value); }
}
}
Main Window
In my Main Window, I bind my People list as well as SelectedPerson instance, like so:
<Grid>
<local:UCDisplay>
<local:UCDisplay.MyDataGrid>
<local:UCDataGrid ItemsSource="{Binding People}"
SelectedValue="{Binding SelectedPerson, UpdateSourceTrigger=PropertyChanged}"
RoutedDataGridDoubleClick="UCDataGrid_RoutedDataGridDoubleClick"/>
</local:UCDisplay.MyDataGrid>
</local:UCDisplay>
</Grid>
Code Behind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
private List<Person> people;
public List<Person> People
{
get => people;
set => SetField(ref people, value);
}
private Person selectedPerson;
public Person SelectedPerson
{
get => selectedPerson;
set => SetField(ref selectedPerson, value);
}
public MainWindow()
{
InitializeComponent();
People = GetPeople();
DataContext = this;
}
private void UCDataGrid_RoutedDataGridDoubleClick(object sender, RoutedEventArgs e)
{
}
private List<Person> GetPeople()
{
return new List<Person>
{
new Person() { Name = "A" },
new Person() { Name = "B" },
new Person() { Name = "C" }
};
}
public class Person
{
public string Name { get; set; }
}
}
Again, in reality I'm using a VM, this is only to keep things simple.
Now when I run this I can display my list content just fine. But when I double-click an item in my DataGrid, in the corresponding in my Main Window code behind, the SelectedPerson remains null, although its binding is identical to the People list. I confirm this by using a break point in the main code behind:
But if I debug and see the value in the code behind of my innermost UserControl, you see that the SelectedValue there has the correct selected items value.
So what am I doing wrong here? Why can't I seem to bind the SelectedValue although I do it exactly the same as my ItemsSource binding, but the latter works?
SelectedValue is supposed to be used in conjunction with SelectedValuePath. You should use SelectedItem instead.
Besides that, you are missing a TwoWay Binding. Either explicitly declare the SelectedItem Binding TwoWay
<DataGrid x:Name="MyDg"
ItemsSource="{Binding ItemsSource,
RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedItem="{Binding SelectedItem,
RelativeSource={RelativeSource AncestorType=UserControl}, Mode=TwoWay}"/>
or register the property to bind TwoWay by default:
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
nameof(SelectedItem), typeof(object), typeof(UCDataGrid),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
Also note that setting UpdateSourceTrigger=PropertyChanged is pointless in all your Bindings.

Passing ModelData (context) between UserControls (views) MVVM Prism

I'm woking on a project and I have three ViewModels: ObjectDetailsViewMode has a Context (property linking to a model) of type ObjectBase; PropertyTextViewModel has a Context of type PropertyText and PropertyNumberViewModel has a Context of type PropertyNumber.
Below is the structure of the Models:
public class ObjectBase : ModelBase
{
private string _name;
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
public DataCollection<PropertyBase> Properties { get; } = new DataCollection<PropertyBase>();
}
public class PropertyText : PropertyBase
{
private string _default;
public string Default
{
get { return _default; }
set { SetProperty(ref _default, value); }
}
}
public class PropertyNumber : PropertyBase
{
private double _default = 0;
public double Default
{
get { return _default; }
set { SetProperty(ref _default, value); }
}
private double _minValue = 0;
public double MinValue
{
get { return _minValue; }
set { SetProperty(ref _minValue, value); }
}
private double _maxValue = 0;
public double MaxValue
{
get { return _maxValue; }
set { SetProperty(ref _maxValue, value); }
}
}
Regarding the views I have one for each ViewModel. The ObjectDetailsView is a use control that has a TextBox for editing the Object.Name, two buttons to add new PropertyText/PropertyNumber to the Object.Properties and an ItemsControl connected to that Object.Properties.
Each PropertyBase in the ItemsControl (ItemsSource) is resolved into a new view using the DataTemplate marker:
<ItemsControl ItemsSource="{Binding Object.Properties}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type models:PropertyText}">
<views:PropertyTextView />
</DataTemplate>
<DataTemplate DataType="{x:Type models:PropertyNumber}">
<views:PropertyNumberView />
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
As I'm using PRISM the correct ViewModel is automatically created for me and the view DataContext is then set to the new ViewModel. My problem is I need to pass the new Property from the Object.Properties list to the newly created View's ViewModel and store it in the Context property I have there.
I can't avoid creating a View/ViewModel for each property type because there is some under-the-hood logic on some Property types (not the ones I described here.. but I have other types like Boolean, Reference, Enum...)
So I really need to pass a value to the ViewModel I tried to use
<ItemsControl ItemsSource="{Binding Object.Properties}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type models:PropertyText}">
<views:PropertyTextView Context="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type models:PropertyNumber}">
<views:PropertyNumberView Context="{Binding}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
Be aware that Context is a custom property I created inside the ViewModel's to store the ModelContext. I even created a DependencyProperty in the View's behind code:
public PropertyBase Context
{
get { return (PropertyBase)GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
// Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContextProperty =
DependencyProperty.Register("Context", typeof(PropertyBase), typeof(PropertyTextView), new PropertyMetadata(null));
But it doesn't get linked to the ViewModels set event (I made a break point there and... nothing). I even tried a SetBinding in the PropertyTextView code-behind (constructor):
string propertyInViewModel = "Context";
var bindingViewMode = new Binding(propertyInViewModel) { Mode = BindingMode.TwoWay };
this.SetBinding(ContextProperty, bindingViewMode);
No luck with any of these... I' really stuck.
Something More Simple
If the PropertyTextView has this dependency property.
public string Context
{
get { return (PropertyBase)GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
// Using a DependencyProperty as the backing store for Context. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ContextProperty =
DependencyProperty.Register("Context", typeof(string), typeof(PropertyTextBuilderView), new PropertyMetadata(null));
I should be able to do:
right?! Why isn't the public property "Context" not being called (I placed a breakpoint there and I get nothing).
Instead of just setting the Context Property of your View to a new Binding you need to assign the Current DataContext like so:
<views:PropertyNumberView Context="{Binding .}"/>
This should assign the Current Views.DataContext Property to your new View.
If you're in an DataTemplate you probably need to specify the RelativeSource:
<views:PropertyNumberView Context="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType=UserControl}}
<ItemsControl ItemsSource="{Binding Object.Properties}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type models:PropertyText}">
<views:PropertyTextView Context="{Binding .}"/>
</DataTemplate>
<ItemsControl.Resources>
</ItemsControl>
As I'm using PRISM the correct ViewModel is automatically created for me
You don't have to use view-first with Prism. The ViewModelLocator is there to help, if you chose to, but view model-first is possible, too.
If I understand you correctly, you have a view model and want to populate a list with child view models. So do just that:
internal class ParentViewModel : BindableBase
{
public ParentViewModel( ParentModel parentModel, IChildViewModelFactory factory )
{
Children = new object[] { factory.CreateTextViewModel(parentModel.TextProperty), factory.CreateNumberViewModel(parentModel.NumberProperty) };
}
public IEnumerable Children { get; }
}
and map the different child view models to child views via DataTemplates.
parentModel.WhateverProperty will have a Name and Value properties as well as setter for the value, probably...

How to bind a ReactiveList of view models to a ListView containing Views of the view model

I have a ParentViewModel which contains a ReactiveList of ChildViewModels. I would like to bind it to a ListView which will display ChildViews. ChildView binds some text to a Label and a sets an Image resource based on a status enum:
ParentViewModel.cs Simple container for ChildViewModels
public class ParentViewModel : ReactiveObject
{
public ReactiveList<ChildViewModel> Children { get; }
= new ReactiveList<ChildViewModel>();
}
ChildViewModel.cs Has some text and a Status
public class ChildViewModel : ReactiveObject
{
public string SomeText { get; set; }
public enum Status
{
Unknown,
Known
}
public Status CurrentStatus { get; set; } = Status.Unknown;
}
ParentView.xaml UserControl which wraps a ListView, using ChildView for the data template. I made sure to add the ViewModel="{Binding}" on my DataTemplate.
<UserControl x:Class="MyProject.UI.ParentView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:MyProject.View_Models"
xmlns:ui="clr-namespace:MyProject.UI">
<ListView x:Name="ChildList" BorderThickness="0">
<ListView.ItemTemplate>
<DataTemplate>
<!--I have also tried the following line, didn't work -->
<!--<DataTemplate DataType="{x:Type vm:ChildViewModel}">-->
<ui:ChildView ViewModel="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
ParentView.xaml.cs Creates a dependency property for its ParentViewModel, and binds the view model's Children to the ChildList ListView
public partial class ParentView : UserControl, IViewFor<ParentViewModel>
{
public static readonly DependencyProperty ViewModelProperty
= DependencyProperty.Register(
"ViewModel",
typeof(ParentViewModel),
typeof(ParentView));
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = (ParentViewModel)value; }
}
public ParentViewModel ViewModel
{
get { return (ParentViewModel )GetValue(ViewModelProperty); }
set
{
SetValue(ViewModelProperty, value);
BindToViewModel(value);
}
}
private void BindToViewModel(ParentViewModel viewModel)
{
this.OneWayBind(viewModel, vm => vm.Children, v => v.ChildList.ItemsSource);
}
}
ChildView.xaml Simple UserControl with an Image and a Label
<UserControl x:Class="MyProject.UI.ChildView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Horizontal">
<Image Name="StatusIcon" Margin="2" />
<Label Name="DisplayName" />
</StackPanel>
</UserControl>
ChildView.xaml.cs Binds the ChildViewModel and sets image data
public partial class ChildView : UserControl, IViewFor<ChildViewModel>
{
public static readonly DependencyProperty ViewModelProperty
= DependencyProperty.Register(
"ViewModel",
typeof(ChildViewModel),
typeof(ChildView));
public ChildView()
{
InitializeComponent();
}
object IViewFor.ViewModel
{
get { return ViewModel; }
set { ViewModel = (ChildViewModel)value; }
}
public ChildViewModel ViewModel
{
get { return (ChildViewModel )GetValue(ViewModelProperty); }
set
{
SetValue(ViewModelProperty, value);
BindToViewModel(value);
}
}
private void BindToViewModel(ChildViewModel viewModel)
{
viewModel
.WhenAnyValue(vm => vm.CurrentStatus)
.Subscribe(status =>
{
// set StatusIcon's image based on status
});
this.Bind(viewModel, vm => vm.DisplayName, v => v.SomeText);
}
}
I have set breakpoints in ChildView to see if the ViewModel properties hit, but they never do. When I add ChildViewModels to ParentVieWModel.Children, a ChildView is created, but it is never bound properly. I could subscribe to Children's CollectionChanged event and manually set the binding, but I would like to do this the proper XAML/ReactiveUI way. What am I missing?
I think you need to fully set up your properties in your ChildViewModel so that they raise changes in the ViewModel that your ChildView can subscribe to. See ViewModels documentation.
string someText ;
public string SomeText {
get { return someText; }
set { this.RaiseAndSetIfChanged(ref someText , value); }
}
Status currentStatus;
public Status CurrentStatus {
get { return currentStatus; }
set { this.RaiseAndSetIfChanged(ref currentStatus, value); }
}
Or if one or both are readonly.
readonly ObservableAsPropertyHelper<string> someText;
public string SomeText{
get { return someText.Value; }
}
readonly ObservableAsPropertyHelper<Status> currentStatus;
public Status CurrentStatus{
get { return currentStatus.Value; }
}

WPF ComboBox validation error when binding to IEnumerable of Enum values

For the purpose of code reuse, I am attempting to bind a ComboBox ItemsSource to an enumerable of enum values defined in a viewmodel. (I am aware of the strategies for binding directly to the enum, but in order to achieve code reuse I need to bind to an enumerable.) On viewmodel construction, I set the selected item to the first value of the enumerable. When the UI first launches, however, the combobox loads with validation error:
Value '' could not be converted.
This error does not occur when I use the same XAML to bind to an enumerable of classes. After I select an enum value, I get no more validation errors and the UI works as intended. How do I avoid this error and get the combobox to display the selected item on startup?
The code details... I have a service implementing IAcquire<T> which returns an enumerable of enum values:
public interface IAcquire<T>
{
IReactiveList<T> Items { get; }
}
My viewmodel inheritance looks something like this:
class GranularitySelectionViewModel : ChartFilterSelectionBase<DataGranularity>
{
public GranularitySelectionViewModel([NotNull] IAcquire<DataGranularity> service)
: base(service, "Granularity")
{}
}
class ChartFilterSelectionBase<T> : SelectionViewModelBase
{
private readonly IAcquire<T> _service;
internal ChartFilterSelectionBase([NotNull] IAcquire<T> service, string label)
:base(label)
{
foreach (var value in service.Items)
{
Items.Add(value);
}
SelectedItem = Items.FirstOrDefault();
}
private readonly IReactiveList<T> _items = new ReactiveList<T>();
public new IReactiveList<T> Items
{
get { return _items; }
}
private T _selectedItem;
public new T SelectedItem
{
get { return _selectedItem; }
set { SetProperty(ref _selectedItem, value); }
}
}
public class SelectionBaseViewModel
{
protected SelectionBaseViewModel([NotNull] string label )
{
if (label == null) throw new ArgumentNullException("label");
_label = label;
}
private readonly string _label;
public string Label
{
get { return _label; }
}
//Placeholder to be overridden in derived class.
public object SelectedItem { get; set; }
//Placeholder to be overridden in derived class.
public IReactiveList<object> Items { get; private set; }
}
The XAML is as follows:
<DataTemplate DataType="{x:Type viewModels:SelectionBaseViewModel}">
<StackPanel Orientation="Vertical">
<Label Content="{Binding Label}" ContentStringFormat="{}{0}:" Margin="5,5,5,0"/>
<ComboBox Margin="5,0,5,5" ItemsSource="{Binding Items, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" BorderThickness="1" BorderBrush="White">
</ComboBox>
</StackPanel>
</DataTemplate>

Categories