How to bind two properties from different models in WPF - c#

Let say I have classes like those:
public class ParentModel : INotifyPropertyChanged
{
// INotifyPropertyChanged pattern implemented ...
public IChildViewModel CurrentControlModel {
get { ... } set { /* Notify on changes */ }
}
}
public class ChildModelA : INotifyPropertyChanged, IChildViewModel
{
// INotifyPropertyChanged pattern implemented ...
public ICommand Command {
get { ... } set { /* Notify on changes */ }
}
}
public class ChildModelB : INotifyPropertyChanged, IChildViewModel
{
// INotifyPropertyChanged pattern implemented ...
public ICommand Command {
get { ... } set { /* Notify on changes */ }
}
}
public class ButtonViewModel : INotifyPropertyChanged
{
ICommand Command get { ... } set { /* Notify on changes */ }
}
I would like to have Command property to reflect the value of parentModelInstance.CurrentControlModel.Command event if
CurrentControlModel changes.
I cannot modify the ButtonViewModel.Command property to be a proxy of the property
because it's the view model for all buttons and I don't want to specialize it for every possible button.
If I do
ButtonViewModel viewModel;
viewModel.Command = parentModelInstance.CurrentControlModel.Command;
it doesn't work because CurrentControlModel can change (it's null at startup for instance).
I can listen to PropertyChanged event but it will cumbersome to do that for all properties of the model.
Any easier and cleaner alternative ?
Context
To give a bit of context, it's part of a dynamic toolbar code where you have buttons that can change icon, be disabled or change command, command target etc...
depending on what is the current focused control (which can be of different type).
CurrentControlModel is the view model of the current focused control.

The journey into the binding land
First solution: One helper to rule them all and with the View Model bind them
It was inspired by ReactiveUI and manual binding on DependencyProperty :
public static BindableProperty<TProperty> Watch<TInstance, TProperty>(
this TInstance instance,
Expression<Func<TInstance, TProperty>> expression,
BindingMode mode = BindingMode.TwoWay)
{
return new BindableProperty<TProperty>(instance,
GetPath((MemberExpression)expression.Body), mode);
}
public static void BindTo<TInstance, TProperty>(
this BindableProperty<TProperty> bindable,
TInstance instance,
Expression<Func<TInstance, TProperty>> expression) where TInstance
: DependencyObject
{
var getterBody = expression.Body;
var propertyInfo = (PropertyInfo)((MemberExpression)getterBody).Member;
var name = propertyInfo.Name;
var dependencyPropertyName = name + "Property";
var fieldInfo = typeof(TInstance).GetField(dependencyPropertyName,
BindingFlags.Public | BindingFlags.Static);
var dependencyProperty = (DependencyProperty)fieldInfo.GetValue(null);
Binding binding = new Binding();
binding.Source = bindable.Source;
binding.Path = new PropertyPath(bindable.Path);
binding.Mode = bindable.Mode;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(instance, dependencyProperty, binding);
}
public class BindableProperty<T>
{
public object Source { get; }
public string Path { get; }
public BindingMode Mode { get; }
public BindableProperty(object source, string path, BindingMode mode)
{
Source = source;
Path = path;
Mode = mode;
}
}
ButtonViewModel must derive from DependencyObject and implement the pattern
for the Command property
public class ButtonViewModel : DependencyObject
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand),
typeof(ButtonViewModel), new PropertyMetadata(default(ICommand)));
public ICommand Command
{
get { return (ICommand) GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
}
Then it can be used like this (for binding paste command to the paste button):
container.Watch(x => x.CurrentControlModel.Commands.Paste)
.BindTo(pasteButtonViewModel, x => x.Command);
Issues
Must setup DependencyProperty pattern for all of properties of view model.
Reflexion and expression analysis can raise runtime exceptions.
In case a conversion is needed, we must write a proxy doing the conversion and the value modification propagation.
Second solution: Reactive.UI and Fody
Reference the ReactiveUI.WPF and ReactiveUI.Fody, and modify the view model like this
public class ButtonViewModel : ReactiveObject
{
[Reactive]
public ICommand Command { get; set; }
}
Then we can bind the two properties like this:
container.WhenAnyValue(x => x.CurrentControlModel.Commands.Paste)
.BindTo(pasteButtonViewModel, x => x.Command);
Potential issue remaining
By not relying on DependencyProperty (apparently), there is a potential issue because we cannot tell the listener that the property is not set (with DependencyProperty.UnsetValue).
it's a one way binding.

Related

Bubbling NotifyOfPropertyChange with embedded classes

So here's a question that has me running in circles. I am working with an embedded class structure which needs to keep its child objects private but should be able to pass certain NotifyOfPropertyChange events up the ladder from data in those child objects. What is the best way to do this.
My current approach is the code below where my view for SystemViewModel (SystemView) has an element bound to the CommunicationStatus property, and I have a parent class SystemViewModel that has child class CommunicationManager which has child class Communicator as follows.
Things that make it difficult:
1) It MUST be assumed in this case that Communicator has no visibility of SystemViewModel so putting a NotifyOfPropertyChanged(() => CommunicationStatus) in the set method of Communicator's Connected property should not be an option... unless I'm missing something obvious.
2) SystemViewModel should not be able to access Communicator directly so binding from SystemView.xaml to Connected can't be done.
In my mind the NotifyOfPropertyChanged event in Connected should bubble up to the parents due to the implementation of PropertyChangedBase in all classes but that's not happening. Would love any help!
public class SystemViewModel : PropertyChangedBase
{
private CommunicationManager CommunicationManager;
public string CommunicationStatus
{
get
{
if (CommunicationManager.YepConnected)
{
return "Green";
}
else
{
return "Red";
}
}
}
}
public class CommunicationManager : PropertyChangedBase
{
private Communicator Communicator;
public bool YepConnected { get { return Communicator.Connected; } }
}
public class Communicator: PropertyChangedBase
{
private bool _connected;
public bool Connected
{
get { return _connected; }
set
{
_connected = value;
NotifyOfPropertyChange(() => Connected);
}
}
}
EDIT
So it appears that this works correctly and propagates the event as expected from the child class to the parent class. The real issue, which was a bit more insideous, has to do with how the WPF Binding relates to the property. Just for reference, the XAML I'm using looks like this:
<TextBlock Text="Status" Background="{Binding CommunicationStatus}"/>
Also, I used SolidColorBrush instead of string (although they both bind the same and work).
The issue is that when the notification event propagates up from Connected to CommunicationStatus, it stops there and does not propagate to the XAML binding (Nowhere in my code is CommunicationStatus used except in the XAML binding). I know the binding works because by debug I observe that when the program runs initially the color is set to red upon execution of the CommunicationStatus get method, presumably called from the XAML binding. Once the code is running, CommunicationStatus does update whenever Connected does, but the XAML binding no longer observes that change. If I manually implement NotifyOfPropertyChange(() => CommunicationStatus);, the binding element decides to update. However, because I'm not using any sort of set method in CommunicationStatus (and the notify event doesn't propagate up), there doesn't seem to be a straight-forward way of informing the XAML that my value has changed.
Sketchy Solution: Watch for changes to CommunicationStatus and raise the NotifyOfPropertyChange(() => CommunicationStatus); event as follows:
public class SystemViewModel : Conductor<object>
{
private CommunicationManager CommunicationManager;
private SolidColorBrush LastCommunicationStatusValue = new SolidColorBrush();
public SolidColorBrush CommunicationStatus
{
get
{
SolidColorBrush CurCommunicationStatusValue;
if (CommunicationManager.YepConnected)
{
CurCommunicationStatusValue = new SolidColorBrush(Colors.Green);
}
else
{
CurCommunicationStatusValue = new SolidColorBrush(Colors.Red);
}
if (CurCommunicationStatusValue.Color != LastCommunicationStatusValue.Color)
{
LastCommunicationStatusValue = CurCommunicationStatusValue;
NotifyOfPropertyChange(() => CommunicationStatus);
}
return CurCommunicationStatusValue;
}
}
}
And yes, if you don't do it perfectly it's an instant Stack Overflow (pun intended :)
Whenever the value of Connected changes, I observe that CommunicationStatus's get method executes. By doing this, that execution results in another execution of the get method, only this time the XAML updates.
Can anyone explain why this solution works and/or offer a more eloquent solution?
Here is an example how to do that with ReactiveUI
public class SystemViewModel : ReactiveObject
{
private readonly CommunicationManager communicationManager;
private readonly ObservableAsPropertyHelper<string> connectionStatus;
public SystemViewModel( CommunicationManager communicationManager )
{
this.communicationManager = communicationManager ?? throw new ArgumentNullException(nameof(communicationManager));
this.communicationManager
.WhenAnyValue( e => e.YepConnected, state => state ? "Green" : "Red" )
.ToProperty( this, e => e.ConnectionStatus, out connectionStatus );
}
public string ConnectionStatus => connectionStatus.Value;
}
public class CommunicationManager : ReactiveObject
{
private readonly Communicator communicator;
private readonly ObservableAsPropertyHelper<bool> yepConnected;
public CommunicationManager(Communicator communicator)
{
this.communicator = communicator ?? throw new ArgumentNullException(nameof(communicator));
this.communicator
.WhenAnyValue( e => e.Connected )
.ToProperty( this, e => e.YepConnected, out yepConnected );
}
public bool YepConnected => yepConnected.Value;
}
public class Communicator : ReactiveObject
{
private bool _connected;
public bool Connected
{
get { return _connected; }
set { this.RaiseAndSetIfChanged( ref _connected, value); }
}
}
Simple test
var communicator = new Communicator();
var manager = new CommunicationManager(communicator);
var vm = new SystemViewModel( manager );
vm.PropertyChanged += (s,e) => Console.WriteLine( "SystemViewModel.{0} changed", e.PropertyName );
communicator.Connected = true;
communicator.Connected = false;
generated output
SystemViewModel.ConnectionStatus changed
SystemViewModel.ConnectionStatus changed

wpf attached property do not working

I simply want to pass parameter to the control. But it threw error "Input string was not in a correct format." Why?* *
Xaml
<Views:SomeView SecurityId="abc"></Views:SomeView>
Model:
class Data
{
public string Case { get; set; }
public Data(int _input)
{
if (_input==1)
{
Case = "First";
}
else
{
Case = "Second";
}
}
}
ViewModel:
class DataViewModel
{
public string GetData
{
get
{
return D.Case;
}
set
{
D.Case = value;
}
}
public Data D;
public DataViewModel(string i)
{
D = new Data(Convert.ToInt16(i));
}
}
MainWindow
public partial class SomeView : UserControl
{
public string SecurityId
{
get
{
return (string)GetValue(SecurityIdProperty);
}
set { SetValue(SecurityIdProperty, value); }
}
public static readonly DependencyProperty
SecurityIdProperty =
DependencyProperty.Register("SecurityId",
typeof(string), typeof(SomeView),
new PropertyMetadata(""));
public SomeView()
{
DataContext = new DataViewModel(SecurityId);
InitializeComponent();
}
}
You never listened for changes.
You construct your DataViewModel with the value that SecurityId has at the time of the constructor call. Which is the default "". Then you change the value to "abc" through XAML. But that change is not transported anywhere. It happens and nobody cares. The construction of your DataViewModel is already done.
Do you want to listen to changes? I cannot tell. You will need to register a change handler for your dependency property.
In your PropertyMetaData you can pass a changed event handler as second parameter, for example a static method:
public static readonly DependencyProperty
SecurityIdProperty =
DependencyProperty.Register("SecurityId",
typeof(string), typeof(SomeView),
new PropertyMetadata("", new PropertyChangedCallback(MyValueChanged)));
You can then have a method to handle changes:
private static void MyValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
{
// react on changes here
}
It's not an attached property by the way. It's a normal dependency property.
This is because, you are trying to parse "abc" as integer, but you are not handling exceptions caused by ConvertTo.Int16() method.
Write DataViewModel constructor like,
public DataViewModel(string i)
{
int value = 0;
int.TryParse(i, out value); // TryParse handles the exception itself.
D = new Data(value);
}

Update the model from the view model

Update the model from the view model
I have read some post about the MVVM but I not sure if understand the
way that the view model is updating the model
Currently I have two text boxes in the UI which is bound to the XAML view and call to the view model when the event was raised .
when should be the place in the view model when I updating the model?
This is the view model
class ViewModel:INotifyPropertyChanged
{
private String _url;
private String _TemplateType;
public string URL
{
get { return _url; }
set
{
if (value != _url)
{
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return _TemplateType; }
set
{
if (value != _TemplateType)
{
_TemplateType= value;
OnPropertyChanged("URL");
}
}
}
The model
internal class DefineAddinModel
{
public string TemplateType { get; set; }
public String URL { get; set; }
}
The ViewModel usually acts as a wrapper around the Model and contains a reference to the Model which is can update either in response to commands or automatically in property setters.
UPDATE:
Here's an example of having the VM act as a wrapper around the Model. This may seem useless in your example but you will find in many cases the VM's getters/setters need to do some sort of transformation on the values rather than simply passing them through.
class ViewModel:INotifyPropertyChanged
{
private DefineAddinModel model;
public string URL
{
get { return model.URL; }
set
{
if (value != model.URL)
{
model.url = value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return model.TemplateType; }
set
{
if (value != model.TemplateType)
{
model.TemplateType = value;
OnPropertyChanged("TemplateType");
}
}
}
The better way to update your Model Is by using an event, its safer, so choose weather using a button click or lost focus, or whatever you want
void button_click(object sender,eventsarg e)
{
MyObj.URL = App.Locator.MyVM.MyDefineAddinModel.URL;// App.Locator because MVVMLight is tagged
MyObj.TemplateType = App.Locator.MyVM.MyDefineAddinModel.TemplateType ;
}
but personnaly i Use the following steps :
1- In your ViewModel create a CurrentItem object of type DefineAddinModel and without OnPropertyChanged then bind it to the View(UI) DataContext of the RootElement on the View )
2- for the model I use the INotifyPropertyChanged for each propery
3- after binding the datacontext of your root element to the CurrentItem of your ViewModel then bind just URL and TemplateType properties to your Controls, so any thing changes on the textbox will update CurrentItem properties
you can also chose the type of the binding (On LostFocus, or OnPropertyChanged)
You need to bind your TextBoxes to the two properties URL and TemplateType.
Try to use Commands (in the ViewModel)instead of events (in The CodeBehind) since you are in MVVM.
For updating the model : use a button with it's Command property bound to OnSave just like this example:
private String _url;
private String _TemplateType;
private DefineAddinModel _defineAddin;
public DefineAddinModel DefineAddin
{
get {return _defineAddin;}
set
{
_defineAddin = value;
OnPropertyChanged("DefineAddin");
}
}
public string URL
{
get { return _url; }
set
{
if (value != _url)
{
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType
{
get { return _TemplateType; }
set
{
if (value != _TemplateType)
{
_TemplateType= value;
OnPropertyChanged("URL");
}
}
}
public RelayCommand OnSaved
{
get;
set;
}
public ViewModel()
{
DefineAddin = new DefineAddinModel();
OnSaved = new RelayCommand(()=>
{
DefineAddin.URL = URL ;
DefineAddin.TemplateType = TemplateType;
});
Think about using third parties like MVVMLight it helps you a lot with MVVM and the helpers around it (Commands, Messenger, ViewModelLocator ...)
I think that the correct answer here is 'it depends'.
In most general cases, the advantage of actually using a ViewModel is also to track 'transient state', i.e. the state of an 'edit in progress' operation.
In this particular case, you would not push your changes directly to the Model every time a value is updated, instead you would do this via an 'Update' ICommand implementation that will collect all the data from the ViewModel and push it down to the Model.
This approach gives you many advantages:
The user of the view can change their mind as many times as they want, and only when they are happy will the Model actually get updated with their definitive choices
It greatly reduces the load on your persistence service, since only final changes are pushed through.
It allows you to do final validation on a complete set of values, rather than transient states, and hence reduces programming complexity and overhead.
It also makes your UI far more fluid since all the examples above are pushing updates on the UI Dispatcher, and avoids you having to cater for this via Tasks or other async approaches.
The backing model is never in an inconsistent state, since I would imagine that all values on one View/ViewModel are related, and only make sense when updated together using an ACID approach.
Here's an example of how I'd do it.
public class ViewModel:INotifyPropertyChanged {
private String _url;
private String _TemplateType;
public ViewModel(){
UpdateCommand = new DelegateCommand(OnExecuteUpdate, OnCanExecuteUpdate);
}
public bool OnCanExecuteUpdate(object param){
// insert logic here to return true when one can update
// or false when data is incomplete
}
public void OnExecuteUpdate(object param){
// insert logic here to update your model using data from the view model
}
public ICommand UpdateCommand { get; set;}
public string URL{
get { return _url; }
set {
if (value != _url) {
_url= value;
OnPropertyChanged("URL");
}
}
}
public string TemplateType {
get { return _TemplateType; }
set {
if (value != _TemplateType) {
_TemplateType= value;
OnPropertyChanged("TemplateType");
}
}
}
... etc.
}
public class DelegateCommand : ICommand {
Func<object, bool> canExecute;
Action<object> executeAction;
public DelegateCommand(Action<object> executeAction)
: this(executeAction, null) {}
public DelegateCommand(Action<object> executeAction, Func<object, bool> canExecute) {
if (executeAction == null) {
throw new ArgumentNullException("executeAction");
}
this.executeAction = executeAction;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter) {
bool result = true;
Func<object, bool> canExecuteHandler = this.canExecute;
if (canExecuteHandler != null) {
result = canExecuteHandler(parameter);
}
return result;
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged() {
EventHandler handler = this.CanExecuteChanged;
if (handler != null) {
handler(this, new EventArgs());
}
}
public void Execute(object parameter) {
this.executeAction(parameter);
}
}

How can i set a default value for a dependency property of type derived from dependencyobject

I am new to WPF and this is my first post. I have created a class called 'Fruit' that descends from 'DependencyObject' and adds and extra property called 'Apple'. I have created a new custom control that includes a Dependency Property called 'MyFruit' of type 'Fruit'. My question is, how can i set the default value for the properties within 'MyFruit' object (i.e. the 'Apple' property? I would like to set this in XAML using the object.
public class Gauge : Control
{
.
.
.
//---------------------------------------------------------------------
#region MyFruit Dependency Property
public Fruit MyFruit
{
get { return (Fruit)GetValue(MyFruitProperty); }
set { SetValue(MyFruitProperty, value); }
}
public static readonly DependencyProperty MyFruitProperty =
DependencyProperty.Register("MyFruit", typeof(Fruit), typeof(CircularGauge), null);
#endregion
}
//-------------------------------------------------------------------------
#region Fruit class
public class Fruit : DependencyObject
{
private int apple;
public int Apple
{
get { return apple; }
set { apple = value; }
}
}
#endregion
Instead of null in your dependency property metadata insert
new UIPropertyMetadata("YOUR DEFAULT VALUE GOES HERE")
So now it becomes
public static readonly DependencyProperty MyFruitProperty =
DependencyProperty.Register("MyFruit", typeof(Fruit), typeof(CircularGauge), new UIPropertyMetadata("YOUR DEFAULT VALUE GOES HERE"));
You need to use PropertyMetaData like this:
class MyValidation
{
public bool status
{
get { return (bool)GetValue(statusProperty); }
set { SetValue(statusProperty, value); }
}
public static readonly DependencyProperty statusProperty = DependencyProperty.Register("status", typeof(bool), typeof(MyValidation),new PropertyMetadata(false));
}

Is hierarchical databinding with properties possible?

Is it possible to bind to a property of a property?
Here is what I have:
[Bindable(true)]
public class DataClass
{
private string DescriptionValue = null;
private Content DataContent Value = new Content();
....
[Bindable(true)]
public Content DataContent
{
get { return DataContent; }
set { DataContent = value; }
}
[Bindable(true)]
public string Description
{
get { return DescriptionValue; }
set { DescriptionValue = value; }
}
...
}
[Bindable(true)]
public class Content
{
private object ContentValue = null;
private Color StateBackColorValue;
...
[Bindable(true)]
public object Content
{
get { return ContentValue; }
set { ContentValue = value; }
}
[Bindable(true)]
public Color StateBackColor
{
get { return StateBackColorValue; }
set { StateBackColorValue = value; }
}
...
}
Is it somehow possible to bind a control to DataContent.Content or any other property of the Content class? I know that I could introduce properties in DataContent class that map the Content class properties. I just wanted to know if hierarchical databinding with properties is possible.
What type of data-binding are you doing?
With simple binding (TextBox.Text to a single object, for example), yes, you can use "Foo.Bar.SomeProp" as the member. For PropertyGrid, you can mark the objects with [TypeConverter(typeof(ExpandableObjectConverter))] and it will work.
The tricky one is list binding (DataGridView etc); here, no: it doesn't flatten easily. You can do it if you go to great lengths (ITypedList etc), but it really isn't worth it - just add shim properties to the parent:
public string ChildName {
get {return child == null ? "" : child.Name;} // and setter if you want
}

Categories