Getting Value from ViewModel through DataContext WITHOUT Binding? - wpf

New to WPF. I am creating UserControls that need read access to the ViewModel state to do their thing. I currently use the following technique:
public partial class ControlBar : UserControl
{
private static readonly DependencyProperty URLProperty =
DependencyProperty.Register("URL", typeof(string), typeof(ControlBar),
new UIPropertyMetadata(null));
public ControlBar()
{
InitializeComponent();
SetBinding(URLProperty, "CurrentPage.URL");
Pin.Click += Pin_Click;
}
private void Pin_Click(object sender, RoutedEventArgs e)
{
var URL = (string)GetValue(URLProperty);
}
}
Is this the correct way and is it not overkill to set up a long-term binding for each variable I need access to? Or can you do something like:
GetValue(new Path("CurrentPage.URL.....
I made up the above obviously.
Thanks!

In general data-binding is the way to go. However sometimes when you are creating controls that have view-specific concerns for which data-binding will not be appropriate.
In those cases you will want to be able to interact with the DependencyProperty to set it and know when it changes. I have been following a pattern that I picked up from a Charles Petzold article in MSDN magazine.
My answer to another question shows the pattern for creating a DependencyProperty for a UserControl Stack Overflow: Dependency Property In WPF/SilverLight
Again, data-binding to a view model will likely solve your problem, but a DependencyProperty may come in useful depending on the situation.
Update in response to comment:
In many situations you can data bind your in a UserControl without using a DependencyProperty. For example if you have a TextBlock that displays a name you would put a TextBlock in the XAML of the UserControl
<TextBlock Text="{Binding Path=NameString}" />
In the view model which is present in the DataContext you would have a property NameString and if the TextBlock is to update the display when the NameString property changes the view model should implement INotifyPropertyChanged and the property should fire the PropertyChanged event with the name of the property sent along with the event.
protected string _NameString;
public string NameString
{
get { return _NameString; }
set { _NameString = value: Notify("NameString"); }
}
Where Notify is a method that checks the PropertyChanged event for null and sends the event if not null.
This works well if everywhere that you want to use the UserControl has a view model with a Name property. The great thing is that the UserControl can pick up on the DataContext of wherever it is hosted and bind to an external view model.
When you want to start binding the same UserControl to different properties is one place that you may want to use a DependencyProperty. In that case you could make a UserControl with a DependencyProperty and bind it to different properties
<my:SampleControl NameString="{Binding Path=GivenName}" />
<my:SampleControl NameString="{Binding Path=FamilyName}" />
And then have an internal view model that the DependencyProperty change handler updates when the bound property changes.
Update: No DependencyProperty or binding
You can always add an ordinary C# property to the UserControl and pass the data in that way.
public MyClass Data { get; set; }
Then in the code-behind of the UserControl you can simply use the property:
if (this.Data != null)
{
this.textBox1.Text = Data.NameString;
}
Update in response to comment:
Another way to access the view model in code is to cast the DataContext to your view model type:
MyClass data = this.DataContext as MyClass;
if (data != null)
{
// do something
this.textBox1.Text = data.NameString;
}

Related

Bind window model value to User Control Dependency Property

I have a simple user control that has One Dependency Property (the control is the model of itself)
The property is not directly bound to anything inside the user control, but I need to Bind its value to the Model of the window (or user control or whatever) where I put my user control.
If I set manually the User control Property Value, the property is modified correctly so I can assume the dependency property in the user control is working.
If I set the value to the Property binding it to my window model like this
<lctrl:InfoIconControl Grid.Row="0" Name="InfoIconTest" IconType="{Binding Path=IconTypeValue}"/>
Where IconTypeValue is a property of the window model, when I set the value of the window model property it does not change inside my user control. I presume I did something wrong but at the moment I have no clue.
Two possibilties come to mind as likely:
Your "model" (you mean viewmodel?) does not implement INotifyPropertyChanged and/or you're not firing the PropertyChanged when IconTypeValue changes its value.
You've done something like this.DataContext = this inside your UserControl and now the Binding is not working because it is looking for the IconTypeValue property inside your control, instead of looking for it in the "model".
Solution to option 1 is easy: implement the interface and make sure you fire the event when the property changes.
Solution to option 2 is simply removing any setting of DataContext inside your UserControl, and instead rely on relative Bindings (RelativeSource, ElementName, etc.) in your control's XAML. Or if you gotta set the DataContext of something, do NOT set the UserControl's one. Instead, set the DataContext of a container INSIDE the UserControl.
In your case, since you're using a viewmodel for your UserControl, using it as DataContext makes sense. But if you wanna support binding to the DependencyProperties of your UserControl, you're then gonna have to set your viewmodel as DataContext of something else... For instance, the first Grid in your XAML.
Just name the Grid:
<Grid x:Name="LayoutRoot">
And set your viewmodel as its DataContext:
InfoIconControlModel mModel;
public InfoIconControl()
{
InitializeComponent();
mModel = new InfoIconControlModel();
LayoutRoot.DataContext = mModel; // this.DataContext = mModel; <-- DON'T DO THIS
}
After that, the Bindings will begin to work. But you've made another typical mistake: you're only calling SetIcon from the CLR setter of your propertty.
public InfoIconType IconType
{
get
{
return (InfoIconType)this.GetValue(IconTypeProperty);
}
set
{
this.SetValue(IconTypeProperty, value);
this.SetIcon(); // <-- This won't work with Binding
}
}
Instead, you must also call it from the DependencyPropertyChanged callback (that you had already defined, on the other hand):
/// <summary>
/// Icon Type dependency Property
/// </summary>
public static readonly DependencyProperty IconTypeProperty = DependencyProperty.Register(
FLD_IconType, typeof(InfoIconType), typeof(InfoIconControl), new PropertyMetadata(InfoIconType.ICPlus, IconTypePropertyChanged));
///<summary>
///
///</summary>
private static void IconTypePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
InfoIconControl ic = sender as InfoIconControl;
ic.SetIcon(); // <-- This will work with Binding
}

MVVM + UserControl + Dependency Property

Alright, this is somewhat related to this question: WPF Printing multiple pages from a single View Model
I tried to follow the advice given there but now I am stuck.
My application uses a MainView.xaml and the appropriate MainViewViewModel.cs, I am using MVVM Light in the background.
Now - according to the post - it seems I have to do the following:
Create a user control
Expose some properties from the user control
Make sure the view model shows these properties
The idea is clear but I am stuck when trying to notify each other.
My user control (UcTest.xaml) exposes a Dependency Property:
public string SpecialText
{
get { return (string)GetValue(SpecialTextProperty); }
set
{
SetValue(SpecialTextProperty, value);
}
}
// Using a DependencyProperty as the backing store for SpecialText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty SpecialTextProperty =
DependencyProperty.Register("SpecialText", typeof(string), typeof(UcTest), new PropertyMetadata(new PropertyChangedCallback(SpecialTextChangedPropertyCallback)));
private static void SpecialTextChangedPropertyCallback(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
// Do something
Debug.WriteLine("Ffgdgf");
}
Alright, so I do now have a user control which has some dependency properties. Yet, these properties are completely separated from my ViewModel properties (those are the ones which shall be displayed).
So basically I have two possibilities:
How can I now tell my ViewModel for the UserControl that some properties have changed?
Is there a possibility to forget about the dependency properties and access the view model directly?
Additional info #1:
I have uploaded a (simple) example of what I am trying to do here: Example Project. I would like to change the value of the label in UserControl1 (via the binding property in the ViewModel for UserControl1) from my MainViewViewModel.
You would usually bind the UserControl's property to the ViewModel property. A two-way binding would work in both directions, from ViewModel to View and vice versa.
<Window x:Class="TestApplication.MainWindow" ...>
<Window.DataContext>
<local:MyViewModel/>
</Window.DataContext>
<Grid>
<local:UcTest SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
</Grid>
</Window>
To directly access the ViewModel object in the above example, you could simply cast the UserControl's DataContext property to the ViewModel type. The DataContext is inherited from the MainWindow.
var viewModel = DataContext as MyViewModel;
var property = viewModel.MyViewModelProperty;
You could of course also directly assign a specialized ViewModel instance to the UserControl's DataContext:
<local:UcTest SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
<local:UcTest.DataContext>
<local:UserControlViewModel/>
</local:UcTest.DataContext>
</local:UcTest>
or you may create the ViewModel instance as a resource in a resource dictionary and assign the DataContext like this
<local:UcTest DataContext="{StaticResource MyUserControlViewModel}"
SpecialText="{Binding MyViewModelProperty, Mode=TwoWay}"/>
Alright, after hours of googling it appears that the "correct" approach to this is not to do it at all. The general approach is to keep the data in your MainViewModel and not use an additional ViewModel for the UserControl (which I find a little ... well .. not so good). The main problem is that there is no easy mechanism to get the Data from the Dependency Property to the ViewModel.
For printing, I have now gone back to doing it purely in code.

Binding in Code doesn't react to source object changing

I have a dependency property on a class inheriting from adorner, like so:
public class LoadingAdorner : Adorner
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof (string), typeof (LoadingAdorner), new PropertyMetadata(default(string)));
public string Text
{
get { return (string) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty IsShowingProperty = DependencyProperty.Register("IsShowing", typeof (bool), typeof (LoadingAdorner), new PropertyMetadata(default(bool)));
...
}
Adorner's don't really have any XAML, but I wanted the text of this adorner to be bindable to the viewmodel. So I create the binding in code, in the view's constructor, like so:
private readonly LoadingAdorner _loading;
public MainWindow()
{
InitializeComponent();
_loading = new LoadingAdorner(MainPage);
var bind = new Binding("LoadingText"){Source = DataContext};
_loading.SetBinding(LoadingAdorner.TextProperty, bind);
}
The DataContext is my view model, my view model implements INotifyPropertyChanged, LoadingText is a string property that calls OnPropertyChanged, etc. All bindings in XAML work fine, however, the code binding does not.
I believe it is because at the time of creating the binding, the view model has not yet been set to the DataContext (it is null), I do this on the line after creating the view. If I set this binding to a property on my view using Source = this, it works.
My question is, why are the XAML bindings are capable of reacting to the source object changing, while the code binding doesn't appear to be? Is there a proper way for me to create a binding that will react to this similiar to the XAML bindings?
Binding do not and cannot react to source changes, it is a logical impossibility, objects do not change properties and references to objects change. Bindings can react to DataContext property changes but only if you do not do something horrible like Source = DataContext which kills the mechanism by getting the current data context once only. Just drop that so the DataContext is the default source again and the binding should react to the changes.
If the DataContext is on another object than the one that is bound it needs to be moved into the Path, i.e. new Binding("DataContext.LoadingText"){ Source = this }.

Binding a ContentControl to a deep path in WPF

The application I'm currently writing is using MVVM with the ViewModel-first pattern. I have XAML similar to the following:
<ContentControl Content="{Binding FooViewModel.BarViewModel.View, Mode=OneWay}"/>
Every VM is a DependencyObject. Every property is a DependencyProperty. Depending upon the state of the application, the value of the BarViewModel property of the FooViewModel can change, thus changing the value of the View property. Unfortunately when this happens, the new view is not displayed, and the old one remains.
This is extremely frustrating. I thought that if any part of a path expression changed, the binding would update, but that doesn't appear to be the case. When I've used shallower path expressions, such as FooViewModel.View and I've changed the value of the FooViewModel property, that has updated the ContentControl to which it's bound, but not in this case.
If your solution is that I abandon ViewModel-first, that is not an option, though I appreciate your advice. I must get this working as is.
CLARIFICATION
This is a question about data binding, and not about MVVM or how to implement it. You can safely ignore the MVVM aspects of this if it helps you to think about the problem, or if you have a different idea about how MVVM should be implemented. This is a large, existing project in which the MVVM design pattern cannot be changed. (It is far too late for that.)
So, with that said, the correct question to be answering is the following:
Given a binding path expression in which every element is a DependencyProperty and the final property is a view bound to a ContentControl, why does a change in a property in the middle of the path not cause the binding to update?
Although I would expect this to work, there are several problems with your approach.
Firstly, your view models should not use DependencyObject or DependencyProperty, this ties them in to WPF. They should instead implement INotifyPropertyChanged. This makes your view models reusable in other presentation technologies such as Silverlight.
Secondly, your view models shouldn't have references to your views, so you shouldn't require a View property on your view models.
I would seriously consider using an MVVM framework for view composition - Caliburn.Micro, for example, makes view model first development extremely straightforward, and already provides a view model base class which implements INotifyPropertyChanged, and a mechanism for building view compositions with conventions.
I.e. you can have a conductor view model which has an ActiveItem property, and you simply place a ContentControl on your view with the same name as the property:
<ContentControl x:Name="ActiveItem" />
You can use the ActivateItem() method to change the current active item.
Caliburn.Micro also has a host of other features, such as being able to place a Button control with x:Name="Save" on your view, and your Save method on your view model will automatically be invoked when the button is clicked.
Every VM is a DependencyObject. Every property is a
DependencyProperty.
why? a viewmodel should be a simple class with INotifyPropertyChanged and the Properties should be simple properties.
and if you want your different viewmodel be rendered in a different way - you should use DataTemplate.
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyViewModelA}>
<MyViewA/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyViewModelB}>
<MyViewB/>
</DataTemplate>
</Windows.Resources>
<Grid>
<ContentControl Content="{Binding MyActualVM}"/>
</Grid>
</Window>
EDIT: btw you always bind to the last Property: FooViewModel.BarViewModel.View --> so the INotifyPropertyChanged (if raised) just work for the .View
EDIT2: another approach could be to get the BindingExpression of your content control and call.
System.Windows.Data.BindingExpression expr = //get it from your contentcontrol
expr.UpdateTarget();
EDIT3: and a simple mvvm way - just use INotifyPropertyChanged
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MyFooVM = new FooVM();
this.MyFooVM.MyBarVM = new BarVM(){View = "erster"};
this.DataContext = this;
}
public FooVM MyFooVM { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
this.MyFooVM.MyBarVM = new BarVM(){View = "zweiter"};
}
}
public class INPC : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChanged(string property)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(property));
}
#endregion
}
public class FooVM:INPC
{
private BarVM _myBarVm;
public BarVM MyBarVM
{
get { return _myBarVm; }
set { _myBarVm = value;OnPropChanged("MyBarVM"); }
}
}
public class BarVM : INPC
{
private string _view;
public string View
{
get { return _view; }
set { _view = value;OnPropChanged("View"); }
}
}

How to bind back to the dependency property

I have a custom user control that exposes a DepenencyProperty (ImageData).
I've placed this user control on a page and I bind it's ImageData property to a property of my page's ViewModel (photo).
<localControls:PhotoPicker ImageData="{Binding Path=Photo, Mode=TwoWay}"/>
When the user interact with the control setting the controls ImageData property the Photo property of my viewmodel is updated == Perfect. However if the ViewModel changes the value of Photo, the ImageData property of PhotoPicker is not changed. What could I be missing in getting data from the ViewModel back down to the user control?
UPDATE:
Upon further investigation it seems that the setting from the ViewModel back to the control via the binding, through the dependencyproperty does not fire the setter of the dependency property wrapper property. What a mess. I need to know when that happens, if i can't do that in the setter of the wrapper property where should I do it?
UPDATE 2:
Seems that the only way to find about changes to the DependencyProperty is to add a PropertyChangedCallback in the PropertyMetadata when Registering the property.
Sounds like your view model doesn't send property changed notifications? Extremely simplified, your view model must look something like this:
public class MyViewModel : INotifyPropertyChanged
{
private object photo;
public object Photo
{
get { return this.photo; }
set { this.photo = value; this.OnPropertyChanged("Photo"); }
}
// ...
}

Resources