I've seen enough examples with binding to property A with a datacontext where A exists in the viewModel class. Now what if in the viewModel I do not have any property A, instead I create some calss B that contains property A, then how to set up a binding here?
Let's say in xaml<TextBlock Text="{Binding Bid}"..> and In the viewModel's constructor I set up
Quote b = new Quote();
HttpClient.QuoteMap.Add(1,b);
HttpClient.Socket.reqMktdata(1,contract,..)
So b keeps updating its Bid and Ask... The thing is I don't see how to set a binding to b's Bid. For listview or DataGrid I can see how to do it as there's a property called itemsource to specify the data binding source and for each column different property is bind to any property if needed.
The class itself needs to be a property.
<TextBlock Text="{Binding Path=MyClassToBind.PublicProperty}"
private MyClass myClass = new MyClass();
public MyClass MyClassToBind
{ get { return myClass; } }
In your viewmodel create a property A which has the notify event on its change, but get the data from the B instance. If B has INotifyPropertyChanged, then subscribe to those changes and when B event fires that a change has occurred, post the notified change for your property A such as
OnPropertyChanged("A");
That way you can have related data which exists elsewhere but still updates the view accordingly.
This will update when the B property Data changes, to the property A on the MVVM.
class MyMVVM : INotifyPropertyChanged
{
private TheBClass B { get; set { OnPropertyChanged("A"); } }
public string A
{
get { return B.Data; }
set { OnPropertyChanged("A"); }
}
public MVVM(TheBClass bInstance;)
{
B = bInstance;
B.PropertyChanged += (sender, args) =>
{
if (args.PropertyName == "Data")
OnPropertyChanged("A");
};
}
}
Related
I use MVVM Light.
My Model inherits from MVVM Light's ObservableObject.
The Model has a property X
public float X
{
get
{
return x_;
}
set
{
Set<float>(() => this.X, ref x_, value);
}
}
In my ViewModel I have a property X
public float X
{
get
{
myModel.X;
}
}
My View has a label where the content is binding to the ViewModel property X.
(DataContext is set to ViewModel)
When the value of property X in Model is updated from code, the label in the view never gets updated.
Question what is the recommended way to make sure that the View correctly reflects the updated value?
(If I make a property in my ViewModel that returns the Model, I could bind direclty to Model.X in my View. But I want my View to bind to the ViewModel not directly down to the Model)
When myModel.X fires the PropertyChanged event, that does not automatically fire the ViewModel's PropertyChanged.
Moreover, if your Model class already implements INotifyPropertyChanged, there is no need to duplicate the X property in the ViewModel class.
Just turn the myModel member into a public property
public class ViewModel
{
public Model MyModel { get; set; }
}
and bind to it by {Binding MyModel.X}.
This example is a simplified version of my requirement. If i can solve this then the rest is simple (I hope). Assume I have a class hierarchy of
Class A:
Name
List <Class B>
Class B:
Name
List <Class C>
Class C:
Name
Each class has a separate view, view model and model. My main view places each view in a DockPanel. The view of each class is a DataGrid that simply places the names of each list. When i click on a Name of class A it shows me names of class B below. and when i click a Name of class B it shows names of class C. So basically 3 user controls that are all DataGrid controls being shown on a dockpanel in my MainView.
I am still finding my feet in MVVM and I am not sure if this is the best way to go about doing this. The way i'd go about it is that each view model will have a property called SelectedItem being bound to the SelectedItem in the DataGrid on its view. When the property is set, another property called ChildViewModel will be set. But i am not sure how to set this up in my MainView and also in each control. How do i bind the control to it's parent datacontext and how do i bind the item source to the selected item from the parent?
I would have one ViewModel that is the DataContext for a Window/UserControl/etc that these controls are all grouped in. Then set up the collection of ClassA plus 3 SelectedItem properties. In the SelectedItem properties, you will want to set the next one to null or a default value.
private ObservableCollection<ClassA> _CollectionOfA;
public ObservableCollection<ClassA> CollectionOfA
{
get { return _CollectionOfA; }
set
{
if (value == _CollectionOfA)
return;
_CollectionOfA = value;
RaisePropertyChanged(() => CollectionOfA);
}
}
private ClassA _SelectedClassA;
public ClassA SelectedClassA
{
get { return _SelectedClassA; }
set
{
if (value == _SelectedClassA)
return;
_SelectedClassA = value;
SelectedClassB = null; //Or default this to first item in the list, etc.
RaisePropertyChanged(() => SelectedClassA);
}
}
//Repeat SelectedClassA pattern for SelectedClassB/SelectedClassC
public ClassB SelectedClassB { get; set; }
public ClassC SelectedClassC { get; set; }
Then in your bindings, you'll use SelectedClassA.CollectionOfClassB property as the ItemsSource for the 2nd DataGrid, etc.
<DataGrid ItemsSource="{Binding CollectionOfClassA}" SelectedItem="{Binding SelectedClassA}">
//columns defined here
</DataGrid>
<DataGrid ItemsSource="{Binding SelectedClassA.CollectionOfClassB}" SelectedItem="{Binding SelectedClassB}">
//columns defined here
</DataGrid>
<DataGrid ItemsSource="{Binding SelectedClassB.CollectionOfClassC}" SelectedItem="{Binding SelectedClassC}">
//columns defined here
</DataGrid>
EDIT:
The first option is my KISS approach. Another option would be to use something like MVVM Light which implements a weak event pattern available through it's messenger class. In the constructor of your second ViewModel, you register to listen for changes to the SelectedItem in the first ViewModel. And in the setter of the first ViewModel you send a message that the SelectedItem changed. Here is an example of what ClassBViewModel would look like (ClassAViewModel would only need its SelectedClassA setter to send a message, no listener in the constructor. While ClassCViewModel would listen but not need to send).
public class ClassBViewModel : ViewModelBase
{
private ClassA _SelectedClassA;
public ClassA SelectedClassA
{
get { return _SelectedClassA; }
set
{
if (value == _SelectedClassA)
return;
_SelectedClassA = value;
RaisePropertyChanged(() => SelectedClassA);
}
}
private ClassB _SelectedClassB;
public ClassB SelectedClassB
{
get { return _SelectedClassB; }
set
{
if (value == _SelectedClassB)
return;
var oldValue = _SelectedClassB;
_SelectedClassB = value;
Messenger.Default.Send(new PropertyChangedMessage<ClassB>(oldValue, value, "SelectedClassB"));
RaisePropertyChanged(() => SelectedClassB);
}
}
public ClassBViewModel()
{
Messenger.Default.Register<PropertyChangedMessage<ClassA>>(this, (message) => SelectedClassA = message.NewValue);
}
}
Your ClassBView xaml for the DataGrid would still look the same:
<DataGrid ItemsSource="{Binding SelectedClassA.CollectionOfClassB}" SelectedItem="{Binding SelectedClassB}">
//columns defined here
</DataGrid>
I am looking for a way to create an UserControl in silverlight 4 and expose a dependency property, which can accept any type. What I mean by that is, for example, if you look at standard silverlight control like AutoCompleteBox, it is capable of handling any type of collections. So you can bind AutoCompleteBox with IEnumerable<Human> or IENumerable<Animal> etc. And when any item is selected AutoCompleteBox returns the selected value either Human instance or Animal instance via SelectedItem dependency property.
I want to achieve similar flexibility with my usercontrol. I wouild like to expose 2 dependency properties SuggestedItems and SelectedItem. Which ever collection is set to SuggestedItems via consumers of this usercontrol thru Binding, lets take as an example IEnumerable<Car>, the I want SelectedItem property to send instance of Car type back to consumer thru Binding. If I used IEnumerable<Boat>, then I need Boat to be returned with SelectedItem.
I was trying to achieve it by using below example using MVVM, but its not working. I am looking for some clues as to how it should be designed, Am I even on a correct path or I have to completely alter my design?
I created an UserControl called VehicleSelectorUserControl which has its own dedicated ViewModel called VehicleSelectorViewModel with two proerties SuggestedItems, SelectedItem.
And usercontrol has corresponding Dependency properties in its codebehind to expose them to consumers of usercontrol. UserControl XAML has a ListBox which is bound to SuggestedItems property of VehicleSelectorViewModel. When user makes a selection, VehicleSelectorViewModel SelectedItem is set, which them invokes a delegate called ItemSelected to notify VehicleSelectorUserControl codebehind, which then sets the SelectedItem Dependency property to make it available to consumer.
Below is code from the VehicleSelectorUserControl.xaml.cs code behind.
private VehicleSelectorViewModel _TheViewModel;
public UserNameControl()
{
InitializeComponent();
_TheViewModel = Resources["TheViewModel"] as VehicleSelectorViewModel;
_TheViewModel.ItemSelected = OnItemSelected;
}
public IEnumerable<object> SuggestedItems
{
get { return (IEnumerable<object>)GetValue(SuggestedItemsProperty); }
set { SetValue(SuggestedItemsProperty, value); }
}
public static readonly DependencyProperty SuggestedItemsProperty =
DependencyProperty.Register("SuggestedItems", typeof(IEnumerable<object>), typeof(VehicleSelectorControl), new PropertyMetadata(OnSuggestedItemsSet));
private static void OnSuggestedItemsSet(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
_TheViewModel.SuggestedItems = e.NewValue;
}
public object SelectedItem
{
get { return (String) GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(VehicleSelectorControl), null);
private void OnItemSelected()
{
SelectedItem = _TheViewModel.SelectedItem;
}
Its ViewModel VehicleSelectorViewModel code
public Action ItemSelected { get; set; }
private dynamic _SelectedItem;
public dynamic SelectedItem
{
get { return _SelectedItem; }
set
{
if (value != _SelectedItem)
{
_SelectedItem = value;
NotifyPropertyChanged("SelectedItem");
if(ItemSelected != null) ItemSelected.Invoke();
}
}
}
private dynamic _SuggestedItems;
public dynamic SuggestedItems
{
get { return _SuggestedItems; }
set
{
if (value != _SuggestedItems)
{
_SuggestedItems = value;
NotifyPropertyChanged("SuggestedItems");
}
}
}
The XAML of consumer will look like (Consumer has its own ViewModel, which responsible for supplying SuggestedCars [IEnumerable<Car>], SuggestedBoats [IEnumerable<Boat>].
<my:VehicleSelectorControl x:Name="MyCarSelectorControl"
SuggestedItems="{Binding SuggestedCars, Mode=TwoWay}"
SelectedItem="{Binding UserSelectedCar, Mode=TwoWay}" />
<my:VehicleSelectorControl x:Name="MyBoatSelectorControl"
SuggestedItems="{Binding SuggestedBoats, Mode=TwoWay}"
SelectedItem="{Binding UserSelectedBoat, Mode=TwoWay}" />
Short Version
If I update the Model object that my ViewModel wraps, what's a good way to fire property-change notifications for all the model's properties that my ViewModel exposes?
Detailed Version
I'm developing a WPF client following the MVVM pattern, and am attempting to handle incoming updates, from a service, to data being displayed in my Views. When the client receives an update, the update appears in the form of a DTO which I use as a Model.
If this model is an update to an existing model being shown in the View, I want the associated ViewModel to update its databound properties so that the View reflects the changes.
Let me illustrate with an example. Consider my Model:
class FooModel
{
public int FooModelProperty { get; set; }
}
Wrapped in a ViewModel:
class FooViewModel
{
private FooModel _model;
public FooModel Model
{
get { return _model; }
set
{
_model = value;
OnPropertyChanged("Model");
}
}
public int FooViewModelProperty
{
get { return Model.FooModelProperty; }
set
{
Model.FooModelProperty = value;
OnPropertyChanged("FooViewModelProperty");
}
}
The Problem:
When an updated model arrives, I set the ViewModel's Model property, like so:
instanceOfFooVM.Model = newModel;
This causes OnPropertyChanged("Model") to fire, but not OnPropertyChanged("FooViewModelProperty"), unless I call the latter explicitly from Model's setter. So a View bound to FooViewModelProperty won't update to display that property's new value when I change the Model.
Explicitly calling OnPropertyChanged for every exposed Model property is obviously not a desirable solution, and neither is taking the newModel and iterating through its properties to update the ViewModel's properties one-by-one.
What's a better approach to this problem of updating a whole model and needing to fire change notifications for all its exposed properties?
According to the docs:
The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs.
One option is to listen to your own events, and make a helper routine to raise the other notifications as required.
This can be as simple as adding, in your constructor:
public FooViewModel()
{
this.PropertyChanged += (o,e) =>
{
if (e.PropertyName == "Model")
{
OnPropertyChanged("FooViewModelProperty");
// Add other properties "dependent" on Model here...
}
};
}
Whenever your Model property is set, subscribe to its own PropertyChanged event. When your handler gets called, fire off your own PropertyChanged event. When the Model is set to something else, remove your handler from the old Model.
Example:
class FooViewModel
{
private FooModel _model;
public FooModel Model
{
get { return _model; }
set
{
if (_model != null)
{
_model.PropertyChanged -= ModelPropertyChanged;
}
if (value != null)
{
value.PropertyChanged += ModelPropertyChanged;
}
_model = value;
OnPropertyChanged("Model");
}
}
public int FooViewModelProperty
{
get { return Model.FooModelProperty; }
set
{
Model.FooModelProperty = value;
OnPropertyChanged("FooViewModelProperty");
}
}
private void ModelPropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Here you will need to translate the property names from those
// present on your Model to those present on your ViewModel.
// For example:
OnPropertyChanged(e.PropertyName.Replace("FooModel", "FooViewModel"));
}
}
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(String.Empty))
For VB.net if anybody else needs it. If you have already implemented "INotifyPropertyChanged" then the last line is all you need.
I have an INotifyProperty Screen item that I have bound to a wpf control.
Ok... I Simplified everything and am posting more code. I have a MainViewModel with the selected screen property.
public Screen SelectedScreen
{
get { return this.selectedScreen; }
set
{
this.selectedScreen = value;
this.OnPropertyChanged("SelectedScreen");
}
}
I have a textbox that is bound to this property:
<TextBlock Text="{Binding Path=SelectedScreen.ScreenNumber}" />
This all works initially. I have created another control that is changing the selected screen with the following code.
public Screen SelectedScreen
{
get { return (Screen)GetValue(SelectedScreenProperty); }
set
{
this.SetValue(SelectedScreenProperty, value);
for (int x = 0; x < this.Screens.Count; ++x)
this.Screens[x].IsSelected = false;
value.IsSelected = true;
}
}
public ObservableCollection<Screen> Screens
{
get { return (ObservableCollection<Screen>)GetValue(ScreensProperty); }
set { this.SetValue(ScreensProperty, value); }
}
public static readonly DependencyProperty SelectedScreenProperty =
DependencyProperty.Register("SelectedScreen",
typeof(Screen),
typeof(ScreenSelection));
public static readonly DependencyProperty ScreensProperty =
DependencyProperty.Register("Screens",
typeof(ObservableCollection<Screen>),
typeof(ScreenSelection),
new UIPropertyMetadata(new ObservableCollection<Screen>()));
This screen selection control is working. When I change screens and put a breakpoint on the set property of SelectedScreen it is called which then calls the SelectedScreen property of the MainViewModel. So the event is firing, but the textbox isn't updated even though it binds correctly the first time.
Does the class which contains the SelectedScreen property implement INotifyPropertyChanged? When the SelectedScreen property changes, the containing class should raise the PropertyChanged event, and typically, WPF should update the Binding.
Thank you gehho for looking at this. I figured it out and there is no way you had enough information to be able too. I was inheriting from ViewModelBase in the MainViewModel that was inheriting from ObservableObject where I implemented INotifyPropertyChanged. The problem is that I implemented the methods for INotifyPropertyChanged in both classes and WPF was listening to the wrong one. Very obscure. Very annoying. Very lasjkdf;ashdoh