Confused in DataContext in WPF - wpf

I am beginner to WPF and MVMM architecture. VI came across many links which explains about DataContext dependence property in WPF MVMM architecture,
i.e.
view.DataContext = new ViewModels.MainViewModel();
but they always made me confused. Although I have some basic idea about this DataContext like it is used to represent who's object we need in xaml file, but when blogs talks about tree structure inheritance of dataContext I gets confused. Can any one please help me with some very simple and clear example showing how this hierarchy of DataContext works?
Thanks in advanced.

The DataContext property specifies the default source for Data Binding. Consider the following example:
<TextBox Text="{Binding MyProperty}" />
What this Binding says: take the value of MyProperty from whatever object is inside the DataContext, convert it to a string and put it in the TextBox. So if we would set the DataContext of the TextBox to be an object of the following class:
public class Example {
int MyProperty { get { return 3; } }
}
Then, the Text of the TextBox would be set to 3.
What does it mean that the values Inherit? Consider a slightly more complex example:
<Window Name="MainWindow">
<StackPanel>
<TextBox Text="{Binding MyProperty}" />
...etc
If we would have 10 or more TextBox elements on our screen, it would be a lot of senseless work to assign the DataContext to each and every TextBox. To relieve this issue, the implementors of WPF decided that setting the DataContext on the MainWindow in our case would also apply it to everything inside that Window (all children, all nested elements) until the DataContext property is overwritten (i.e. we set the DataContext of the TextBox, then the TextBox and all its children would also receive this DataContext).
If you want to see this behavior in action, the same applies to the FontSize property, try setting the FontSize of your Window to 48 and see what happens to all the text in there!

The Datacontext property is the default source of all the binding of a View.
In MVVM, the Datacontext is used to link a ViewModel to a View.
As the Datacontext property is a dependence property, if you don't define it in a control, it will inherit from his father, etc.
Here is an exemple of MVVM implementation :
Parent of all ViewModel class (to implement INotifyPropertyChanged in all ViewModels) :
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Note : INotifyPropertyChanged allow your ViewModel to notify the View of a change (used for bindings).
Let's say I want a MainWindows (View) to be linked to a ViewModel :
public MainWindow()
{
InitializeComponent();
MainViewModel mainViewModel = new MainViewModel(this);
this.DataContext = mainViewModel;
}
With for ViewModel :
class MainViewModel : ViewModelBase
{
#region fields
private MainWindow mainWindow;
private string message = "Hello world !";
#endregion
#region properties
public MainWindow MainWindow
{
get
{
return this.mainWindow;
}
}
public string Message
{
get
{
return message;
}
set
{
this.message = value; OnPropertyChanged("Message");
}
}
// ...
#endregion
public MainViewModel(MainWindow mainWindow)
{
this.mainWindow = mainWindow;
}
}
So now if I want to bind a property of MainViewModel in my View (mainwindow), i just have to have a public property in my ViewModel and to create a binding in my XAML. I won't have to specify the source as the DataContext is the default source.
So MainWindow.xaml I can add :
<TextBox Text="{Binding Message}" />

Related

WPF C# INotifyPropertyChanged doesn't fire up

I am building a WPF application that as part of its flow, checks for network connectivity and display the IP address in a TextBlock.
Now I am trying to update the TextBlock Text property everytime the IP address changes for whatever reason.
I have the IP address change working fine, but i could not get INotifyPropertyChanged to work.
I read all the possible solutions and implementations but I couldn't come up with a working code.
The public property gets the value from a static string from the Network Helper class.
So, the code:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public string ipAddress
{
get { return NetworkStatus.localIP; }
set
{
if (value != NetworkStatus.localIP)
{
NetworkStatus.localIP = value;
NotifyIPChanged("IpAddress");
}
}
}
private void NotifyIPChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
XAML:
<TextBlock x:Name="ipTxt"
TextWrapping="Wrap"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{Binding DataContext.ipAddress}"
Height="30"
Width="110"
Margin="-30,10,0,-10"
/>
UPDATE
NetWorkStatus.cs -- static bool IsNetworkAvailable()
...
if (statistics.BytesReceived > 0 || statistics.BytesSent > 0)
{
IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
localIP = host.AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToString();
return true;
}
As you can see this method sets a static string "localIP". This is then evaluated by IpAddress property.
Why the TextBlock Text property doesn't get updated when the IP Address changes?
Rename the property to IpAddress so that it adheres to widely accepted naming conventions.
public string IpAddress
{
get { return NetworkStatus.localIP; }
set
{
if (value != NetworkStatus.localIP)
{
NetworkStatus.localIP = value;
NotifyPropertyChanged();
}
}
Use the CallerMemberName attribute on the propertyName parameter of your notification method, so that you do not have to write the name explicitly.
using System.Runtime.CompilerServices;
...
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Bind it correctly. The current DataContext is already used as source object of the Binding. You must not add it to the property path.
<TextBlock Text="{Binding IpAddress}" ... />
In a possible next step you might want to separate the view from the view model and put the property in a separate class:
public class ViewModel : INotifyPropertyChanged
{
public string IpAddress
{
get ...
set ...
}
...
}
and assign the Window's DataContext to an instance of the view model class:
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
I think you need to take a closer look to how WPF works.
As a remark, there is no need to implement INotifyPropertyChanged in code behind. If you are using events, then you can automatically refresh the properties of the targeted UI element.
However, using code behind is not a good practice in our days. You should take a look at MVVM pattern. You have there a Model, View and ViewModel. The ViewModel should implement INotifyPropertyChanged.
The fact is that your code is in my opinion absolutely wrong. The naming is not fine : when you implement INotifyPropertyChanged you should not implement it only for a property, and the name should not look like : NotifyIPChanged, instead you should use RaisePropertyChanged, NotifyPropertyChanged or OnPropertyChanged. In setters you should not refresh something else, but only the property that you are targeting, because otherwise the Single Responsability principle is violated, as in your case. Also a bad practice is to bind to Code Behind.
I hope this post would make you to read more about MVVM and WPF. Good luck!
is it possible that the Event doesnt react, because the first letter of IpAdress is an Upper?
NotifyIPChanged("IpAddress");
public string ipAddress
Text="{Binding DataContext.ipAddress}"

How to set datacontext for each combobox?

I'm having a problem about binding in combobox ( WPF, MVVM).
I have a combobox, which binds to AViewModel ( for example).
To do that, I did have:
- AModel
- AViewModel
- Xaml file :
<Window.DataContext>
<ViewModel:AViewModel/>
</Window.DataContext>
It works fine.
But, now, I add one more combobox to the same form with combobox above. This combobox binds to diffirent ViewModel (BViewMoel for example, note that, this BViewModel located in diffirent file with AViewModel above).
And this is combobox xaml:
<ComboBox
DataContext="BViewModel"
ItemsSource="{Binding Path=MyList}" DisplayMemberPath="BName"/>
My problem is: the second combobox is not populated because it does not have datacontext.
But I cannot set datacontext for it because it is set above for AViewModel.
I did a lots of searching but I still stuck in this.
Should I merge all ViewModels into a ViewModel and set this to Datacontext of Window or any ideal?
Thank you.
Really, I wouldn't use a ViewModel for each combobox. Combobox is a simple control, you should bind the ItemsSource property to a public property (of type ObservableCollection<T> for instance) of the ViewModel of the owner view.
Sometimes it's useful to use a ViewModel for a specific and complex usercontrol. In this case, you can expose the viewModel as a public property of the ViewModel of the owner view:
public class UCViewModel : ViewModelBase
{
}
public class MyViewViewModel : ViewModelBase
{
public MyViewViewModel()
{
this.UCViewModel = new UCViewModel();
}
public UCViewModel UCViewModel { get; set; }
}
<Window x:Class="MyView">
<MyComplexUsercontrol DataContext="{Binding UCViewModel}" />
</Window>
public partial class MyView : Window
{
public MyView()
{
InitializeComponent();
this.DataContext = new MyViewViewModel();
}
}
But again, for a simple combobox, just bind it to a property of the ViewModel associated with the owner view.
combobox1.DataContext = new AViewModel();
combobox2.DataContext = new BViewModel();
But I suggest using a ViewModel contains two properties.
public class ViewModel
{
public AViewModel AViewModel{get;set;}
public BViewModel BViewModel{get;set;}
}

TextBlock binding not working

I have a TextBlock that bind to property in my model. My model sitting in my view model that bind with the window.
<TextBlock Text="{Binding MyModel.TextVar,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBlock>
TextVar is a string property that call function of Notify...
So, I do not understand why it does not work. (There is no binding error in the OutPut).
EDIT:
string _textVar;
public string TextVar
{
get
{
return _textVar;
}
set
{
_textVar= value;
NotifyPropertyChanged("TextVar");
}
}
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)-- HERE THE PROPBLEM, IT ARRIVE NULL
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
Seems that everything is perfect, now just add this line under the constructor of your class.
this.DataContext = this;
The problem arries when you have declared another DataContext in some parent window. I was suffering from same problem and this solution worked for me.
A Quick Review of Binding a TextBox in WPF
Step 1: Declare a property in class of window
first of all, you should declare a property in class of you window, i.e in .xml.cs file of your new window.
string _someVariable;
public string NameOfProperty
{
get
{
return _someVariable;
}
set
{
_someVariable= value;
}
}
Step 2: Bind the property to TextBox
Now bind your TextBox to your newly created property using the following syntax.
{Binding Path=NameOfProperty}
The Path notes the property that you want to bind to, however, since Path is the default property of a binding, you may leave it out if you want to, like this:
{Binding NameOfProperty}
Step 3: Declare the DataContext of your Window
Your solution should work until now, but in case you are using some pattern you should define the DataContext of your window.
Binding TextBlock with Mode=TwoWay makes no sense because its a TextBlock and not a TextBox.
but nevertheless your binding work if the DataContext of your textblock has a Property "MyModel" and this "MyModel" - object has a Property "TextVar".
if you wanna look up your datacontext and binding at runtime use Snoop
EDIT: that what you need if you do your binding like you did
public class MyViewmodel
{
public MyOtherClass MyModel {get;set;}
}
public class MyOtherClass
{
public string TextVar {get;set;}
}
windows.xaml.cs ctor:
this.data = new MyViewmodel();
this.DataContext = this.data;

Triggering Commands from the ViewModel in WPF with MVVM

I have created a few Custom Controls (NOT UserControls) with bind-able "ClearCommand" ICommand dependency properties. This property will do exactly what it sounds: it will clear all the values from the control (textboxes, etc). I also bind (some) of those same properties to the VM I describe below.
Now I'm stuck trying to trigger the ClearCommand in those controls in the following MVVM scenario:
I've added a few such controls into my View. The View also includes a "Save" button that binds to my ViewModel's SaveCommand DelegateCommand property.
What I need to happen is that, upon a successful save, the VM should trigger the ClearCommand on those controls found in the View.
UPDATE
I've added code examples below. I have a few controls that resemble the ExampleCustomControl. Also, just to note, I am open to restructuring some of this if it's completely off.
Example Control snippet:
public class ExampleCustomControl : Control {
public string SearchTextBox { get; set; }
public IEnumerable<CustomObject> ResultList { get; set; }
public ExampleCustomControl() {
ClearCommand = new DelegateCommand(Clear);
}
/// <summary>
/// Dependency Property for Datagrid ItemSource.
/// </summary>
public static DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem",
typeof(CustomObject), typeof(ExampleCustomControl), new PropertyMetadata(default(CustomObject)));
public CustomObject SelectedItem {
get { return (CustomObject)GetValue(SelectedCustomObjectProperty); }
set { SetValue(SelectedCustomObjectProperty, value); }
}
public static DependencyProperty ClearCommandProperty = DependencyProperty.Register("ClearCommand", typeof(ICommand),
typeof(ExampleCustomControl), new PropertyMetadata(default(ICommand)));
/// <summary>
/// Dependency Property for resetting the control
/// </summary>
[Description("The command that clears the control"), Category("Common Properties")]
public ICommand ClearCommand {
get { return (ICommand)GetValue(ClearCommandProperty); }
set { SetValue(ClearCommandProperty, value); }
}
public void Clear(object o) {
SearchTextBox = string.Empty;
SelectedItem = null;
ResultList = null;
}
}
Example View snippet:
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<control:ExampleCustomControl Grid.Row="0"
SelectedItem="{Binding Selection, UpdateSourceTrigger=PropertyChanged}" />
<Button Grid.Row="1" x:Name="ResetButton" Command="{Binding SaveCommand}">
Save
</Button>
</Grid>
Example ViewModel:
public class TestViewModel : WorkspaceTask {
public TestViewModel() {
View = new TestView { Model = this };
SaveCommand = new DelegateCommand(Save);
}
private CustomObject _selection;
public CustomObject Selection {
get { return _selection; }
set {
_selection = value;
OnPropertyChanged("Selection");
}
}
public DelegateCommand SaveCommand { get; private set; }
private void Save(object o) {
// perform save
// clear controls
}
}
As others have said the VM shouldn't know about the view directly in MVVM so it doesn't make sense really that the VM triggers something on your custom control to clear everything.
I would have set the DataContext of the custom control to an object that has all the properties you want to clear, which are all each bound (two-way) to your textboxes etc. Then in the Save() method you can set a new object (which the custom control DataContext is bound to) and all the properties will be cleared for you (assuming you have implemented INotifyPropertyChanged on the object).
UPDATED:
As per my comment, see an example of the workaround for your current setup (untested btw):
public static DependencyProperty SelectedItemProperty = DependencyProperty.Register("SelectedItem",
typeof(CustomObject), typeof(ExampleCustomControl), new PropertyMetadata(default(CustomObject), OnSelectedItemChanged));
private static void OnSelectedItemChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
var cont = source as ExampleCustomControl;
//do all the clearing of txtboxes etc here....
cont.SearchTextBox = string.Empty;
}
But I would still try and move all this into the VM. i.e. have a clear command, like you do with the save command and bind the textbox text etc to a property in the VM and when the command is called it clears everything, which you can then easily call from the Save method in the VM too. But obviously I have no idea what you are trying to achieve in the long run or how selectedItem and the textboxes etc are related, so depends (as always) i guess.
It sounds like you are thinking about this the wrong way. In MVVM the ViewModel should never know anything about the custom controls (hence you are having a problem with this Clear functionality).
Your requirements are a bit vague, but have you considered:
1) If the properties are bound from the VM, can't the Control detect when these are changed?
2) If you really need to call Clear from the XAML layer and want to keep it pure MVVM, then consider something like the Expression Blend SDK's CallMethodAction.
As a followup to my comment. I suspect your command is targeting the View and clearing the TextBoxes directly. Instead, have your command target the ViewModel and clear the properties the View is bound to. Then you can have the command be a property on the ViewModel and call it whenever needed.

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"); }
}
}

Resources