Binding a ContentControl to a deep path in WPF - 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"); }
}
}

Related

Confused in DataContext in 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}" />

WPF: right way to do a Tabcontrol with MVVM pattern

First of all, I'm newbie in WPF and specially in MVVM. I have a window with diferent tabs and a very large ViewModel with the business logic of the content of every tab. I know it is not right, so now I'm trying to do it more elegant:
As I see googling, an idea is to do a collection of a "base" viewmodel from wich inherit the sub-viewmodels of every tab, and a collection on this "base" viewmodel in the viewmodel of the window.
TabBaseViewModel
Tab1ViewModel inherits TabBaseViewModel
Tab2ViewModel inherits TabBaseViewModel
MainWindow ViewModel --> Collection of TabBaseViewModel
The contents the tabs do not have anything in common along each other.
How I have to proceed?
You should consider using an MVVM framework if you're using MVVM. With Caliburn.Micro for example, you can define your main view as:
<TabControl x:Name="Items">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
Where the data context is a Conductor type that has a collection. The Items property will expose a collection of your view models:
public class MainViewModel : Conductor<IScreen>.Collection.OneActive
{
private OneOfMyViewModels oneOfMyViewModels;
private AnotherViewModel anotherViewModel;
protected override void OnInitialise()
{
// Better to use constructor injection here
this.oneOfMyViewModels = new OneOfMyViewModels();
this.anotherViewModel = new AnotherViewModel();
this.Items.Add(this.oneOfMyViewModels);
this.Items.Add(this.anotherViewModel);
}
protected override void OnActivate()
{
base.OnActivate();
this.ActivateItem(this.oneOfMyViewModels);
}
}
public class OneOfMyViewModels : Screen
{
public OneOfMyViewModels()
{
this.DisplayName = "My First Screen";
}
}
I posted an answer to a different question which shows how to do exactly this: How to Get a Reference to a ViewModel
It's a very simple example, but hopefully should get you started along the right track.

MVVM pattern violation: MediaElement.Play()

I understand that ViewModel shouldn't have any knowledge of View, but how can I call MediaElement.Play() method from ViewModel, other than having a reference to View (or directly to MediaElement) in ViewModel?
Other (linked) question: how can I manage View's controls visibility from ViewModel without violating MVVM pattern?
1) Do not call Play() from the view model. Raise an event in the view model instead (for instance PlayRequested) and listen to this event in the view:
view model:
public event EventHandler PlayRequested;
...
if (this.PlayRequested != null)
{
this.PlayRequested(this, EventArgs.Empty);
}
view:
ViewModel vm = new ViewModel();
this.DataContext = vm;
vm.PlayRequested += (sender, e) =>
{
this.myMediaElement.Play();
};
2) You can expose in the view model a public boolean property, and bind the Visibility property of your controls to this property. As Visibility is of type Visibility and not bool, you'll have to use a converter.
You can find a basic implementation of such a converter here.
This related question might help you too.
For all the late-comers,
There are many ways to achieve the same result and it really depends on how you would like to implement yours, as long as your code is not difficult to maintain, I do believe it's ok to break the MVVM pattern under certain cases.
But having said that, I also believe there is always way to do this within the pattern, and the following is one of them just in case if anyone would like to know what other alternatives are available.
The Tasks:
we don't want to have direct reference from the ViewModel to any UI elements, i.e. the the MediaElement and the View itself.
we want to use Command to do the magic here
The Solution:
In short, we are going to introduce an interface between the View and the ViewModel to break the dependecy, and the View will be implementing the interface and be responsible for the direct controlling of the MediaElement while leaving the ViewModel talking only to the interface, which can be swapped with other implementation for testing purposes if needed, and here comes the long version:
Introduce an interface called IMediaService as below:
public interface IMediaService
{
void Play();
void Pause();
void Stop();
void Rewind();
void FastForward();
}
Implement the IMediaService in the View:
public partial class DemoView : UserControl, IMediaService
{
public DemoView()
{
InitializeComponent();
}
void IMediaService.FastForward()
{
this.MediaPlayer.Position += TimeSpan.FromSeconds(10);
}
void IMediaService.Pause()
{
this.MediaPlayer.Pause();
}
void IMediaService.Play()
{
this.MediaPlayer.Play();
}
void IMediaService.Rewind()
{
this.MediaPlayer.Position -= TimeSpan.FromSeconds(10);
}
void IMediaService.Stop()
{
this.MediaPlayer.Stop();
}
}
we then do few things in the DemoView.XAML:
Give the MediaElement a name so the code behind can access it like above:
<MediaElement Source="{Binding CurrentMedia}" x:Name="MediaPlayer"/>
Give the view a name so we can pass it as a parameter, and
import the interactivity namespace for later use (some default namespaces are omitted for simplicity reason):
<UserControl x:Class="Test.DemoView"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ia="http://schemas.microsoft.com/expression/2010/interactivity"
x:Name="MediaService">
Hookup the Loaded event through Trigger to pass the view itself to the view model through a Command
<ia:Interaction.Triggers>
<ia:EventTrigger EventName="Loaded">
<ia:InvokeCommandAction Command="{Binding LoadedCommand}" CommandParameter="{Binding ElementName=MediaService}"></ia:InvokeCommandAction>
</ia:EventTrigger>
</ia:Interaction.Triggers>
last but not least, we need to hookup the media controls through Commands:
<Button Command="{Binding PlayCommand}" Content="Play"></Button>
<Button Command="{Binding PauseCommand}" Content="Pause"></Button>
<Button Command="{Binding StopCommand}" Content="Stop"></Button>
<Button Command="{Binding RewindCommand}" Content="Rewind"></Button>
<Button Command="{Binding FastForwardCommand}" Content="FastForward"></Button>
We now can catch everything in the ViewModel (I'm using prism's DelegateCommand here):
public class AboutUsViewModel : SkinTalkViewModelBase, IConfirmNavigationRequest
{
public IMediaService {get; private set;}
private DelegateCommand<IMediaService> loadedCommand;
public DelegateCommand<IMediaService> LoadedCommand
{
get
{
if (this.loadedCommand == null)
{
this.loadedCommand = new DelegateCommand<IMediaService>((mediaService) =>
{
this.MediaService = mediaService;
});
}
return loadedCommand;
}
}
private DelegateCommand playCommand;
public DelegateCommand PlayCommand
{
get
{
if (this.playCommand == null)
{
this.playCommand = new DelegateCommand(() =>
{
this.MediaService.Play();
});
}
return playCommand;
}
}
.
. // other commands are not listed, but you get the idea
.
}
Side note: I use Prism's Auto Wiring feature to link up the View and ViewModel. So at the View's code behind file there is no DataContext assignment code, and I prefer to keep it that way, and hence I chose to use purely Commands to achieve this result.
I use media element to play sounds in UI whenever an event occurs in the application. The view model handling this, was created with a Source property of type Uri (with notify property changed, but you already know you need that to notify UI).
All you have to do whenever source changes (and this is up to you), is to set the source property to null (this is why Source property should be Uri and not string, MediaElement will naturally throw exception, NotSupportedException I think), then set it to whatever URI you want.
Probably, the most important aspect of this tip is that you have to set MediaElement's property LoadedBehaviour to Play in XAML of your view. Hopefully no code behind is needed for what you want to achieve.
The trick is extremely simple so I won't post a complete example. The view model's play function should look like this:
private void PlaySomething(string fileUri)
{
if (string.IsNullOrWhiteSpace(fileUri))
return;
// HACK for MediaElement: to force it to play a new source, set source to null then put the real source URI.
this.Source = null;
this.Source = new Uri(fileUri);
}
Here is the Source property, nothing special about it:
#region Source property
/// <summary>
/// Stores Source value.
/// </summary>
private Uri _Source = null;
/// <summary>
/// Gets or sets file URI to play.
/// </summary>
public Uri Source
{
get { return this._Source; }
private set
{
if (this._Source != value)
{
this._Source = value;
this.RaisePropertyChanged("Source");
}
}
}
#endregion Source property
As for Visibility, and stuff like this, you can use converters (e.g. from bool to visibility, which you can find on CodePlex for WPF, SL, WP7,8) and bind your control's property to that of the view model's (e.g. IsVisible). This way, you control parts of you view's aspect. Or you can just have Visibility property typed System.Windows.Visibility on your view model (I don't see any pattern breach here). Really, it's not that uncommon.
Good luck,
Andrei
P.S. I have to mention that .NET 4.5 is the version where I tested this, but I think it should work on other versions as well.

Getting Value from ViewModel through DataContext WITHOUT Binding?

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

WPF Dependency Property workaround

There are 3 UserControls under a MainWindow. Each control have it's own Save Button. The Mainwindow has a SaveAll button.
The MainWindow has a ContentControl and the content property is binded to the VM. At runtime on ButtonClickCommand, the View is instantiated and assigned to the Content Property.
This SaveAll button will internally call methods associated with UserControls Save button. As such, SaveAll doesn't have it's own Method.
This has to be implemented by DependencyProperty.
I had once seen this scenario implemented in a Business App, but somehow missed the concept behind it.
I can't get what was the logic behind this, but it's a very useful thing.
Now I have to implement this, but i'm missing a small thing, I dont know.
I hope the scenario is clear.
Please help me in this scenario, with code.
Thanks,
VJ
Since you mentioned MVVM, here's what you might be looking for. Mind you, this will be a lot cleaner and easier if you use an MVVM framework such as Caliburn, but for this sample, its just vanilla MVVM:
public class MainViewModel
{
public MainViewModel()
{
ViewOneModel = new SubViewModel();
ViewTwoModel = new SubViewModel();
Children = new List<SubViewModel>(new[] { ViewOneModel, ViewTwoModel });
}
public void SaveAll()
{
foreach(var child in Children)
{
child.Save();
}
}
public IList<SubViewModel> Children { get; private set; }
public SubViewModel ViewOneModel { get; set; }
public SubViewModel ViewTwoModel { get; set; }
}
public class SubViewModel
{
public void Save()
{
}
}
and on the UI you basically have subviews (UserControls) composed in your main view:
<StackPanel>
<Button Width="100" Height="20" Content="Save All" />
<local:ViewOne DataContext="{Binding ViewOneModel}" />
<local:ViewTwo DataContext="{Binding ViewTwoModel}" />
</StackPanel>
You just need to bind the save methods to your buttons using an ICommand interface (preferably RelayCommand instance).
Imho in this scenario there is no need for RoutedEvents. The way I would solve it:
There is a Main-ViewModel that exposes 3 properties with the Sub-ViewModels.
The MainViewModel is the Datacontext for the window, and the subviewmodels bound to the datacontext of the 3 usercontrols.
The sub vm's are exposing a property with a Save-Command. This command is bound to the save buttons in the usercontrols.
The main vm is exposing a property with a saveall-command, which is bound to the SaveAll button.
In the handler of the save all command you are then iterating over the sub-vm's and call save on them.

Resources