Wpf usercontrol dependency Property Binding - wpf

In my mainwindow I have got an UserControl, which ViewModel has got the Dependency Property "Message", I'm trying to bind the Dependency Property to an Property of the ViewModel of the Main Window, but actually it isn't Working, is there any soulution or is it genarally impossible?
Content of the Main Window:
<local:MessageLayer>
<local:MessageLayer.DataContext>
<local:MessageBoxViewModel Message="{Binding RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}, Path=DataContext.Message}"/>
</local:MessageLayer.DataContext>
</local:MessageLayer>

View models should not have DependencyPropertys in them and should certainly not extend the DependencyObject class, because these are UI related classes. I'm sure that you, along with many others have been confused by Microsoft's terribly worded error below:
A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
This is really only referring to the UI element side of Bindings and not the data element side. For data binding data objects, we implement the INotifyPropertyChanged interface instead, which provides similar property change notification functionality to DependencyPropertys.
So, had you set the Window.DataContext to an instance of a view model that implemented INotifyPropertyChanged, with a property declared in it named Message, then your code would have worked just fine:
<local:MessageLayer>
<local:MessageLayer.DataContext>
<local:MessageBoxViewModel Message="{Binding RelativeSource={RelativeSource
AncestorType=Window, Mode=FindAncestor}, Path=DataContext.Message}"/>
</local:MessageLayer.DataContext>
</local:MessageLayer>

Related

Binding Parent View DataContext to DataTemplate Child View DataContexts

I've been experimenting with WPF, Xaml, MVVM, and DependencyInjection lately. Consequently, I am creating a UI using MVVM principles. A certain portion of the UI is designed to act like a wizard wherein not all of the available options are presented to the user at the same time. Each section of options is its own View (sub-View) with a single View (Parent View) hosting these sub-Views in a ContentControl. The user sets certain options and uses buttons to move from one section to the other.
View Navigation
To switch between these views I'm using a DataTemplateSelector with each sub-View defined as a DataTemplate in my Xaml resources.
Content Control in the Main View:
<ContentControl Content="{Binding ElementName=ParentViewControl, Path=ViewState, Mode=TwoWay}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
Example sub-View Data Template:
<DataTemplate x:Key="SubViewATemplate">
<local:SubViewAView x:Name="SVAView" DataContext="{Binding ElementName=ParentViewControl, Path=DataContext}" ViewState="{Binding ElementName=ParentViewControl, Path=ViewState, Mode=TwoWay }" />
</DataTemplate>
On the Parent View and each sub-View I've created a Dependency Property called ViewState (an enum). These bind to each other through the DataTemplates. In each View's code-behind I update this ViewState Property based off user input and it propagates up to the Parent View which in turn triggers the DataTemplateSelector. So far, so good. The navigation works beautifully.
ViewModel Info
The Parent View has a ViewModel which implements INotifyPropertyChanged as its DataContext. I'm attempting to use this single ViewModel to bind Properties to the Parent View and the sub-Views. The problem is the DataContext binding in the DataTemplate snippet above does not work. (Which is odd to me since the ViewState binding does.) After various attempts to get this to work, the DataContext on a sub-View is either null or the ViewState control variable.
I am currently using the UnityContainer as my dependency injector.
Various Attempts
Here are the various other things I've tried that have all failed:
1) Registered the ViewModel as a singleton in the UnityContainer thereby using Constructor Injection on the sub-Views to set the DataContext. (Does not work because there must be a Parameter-less Constructor for the DataTemplate resource.)
2) Registered the ViewModel as a singleton in the UnityContainer and then using Property Injection on the sub-Views to set the DataContext. (Does not work. I think this is due to the UnityContainer not working when an object is instantiated in Xaml.)
3) Creating sub-ViewModels for each sub-View that needs a ViewModel to display properties that would have existed on the Parent ViewModel. I've used this before to get around the Xaml instantiation problem with the UnityContainer. I then replace the DataContext binding in the DataTemplate with the associated sub-ViewModel. (Does not work because for some reason the DataContext of my Parent View is getting set to the ViewState variable instead of remaining my ViewModel which I've set in the view's Constructor. This in turn means the sub-ViewModel property on my Parent ViewModel can't be found to bind to the DataContext of the sub-View.) Are the Content and DataContext of UserControls the same thing? Does setting one affect the other?
4) Moving the ViewState dependency property from the View to the ViewModel and then setting the ContentControl's Content to bind to the ViewModel. This violates MVVM principles but by this time I was trying anything to get this to work. (It doesn't work because when ViewState is changed in the code-behind of the view the ViewModel does not trigger as changed.) I haven't gone any further with this one because I didn't want to go deeper violating MVVM.
Conclusion
I've found most of these attempted solutions on this site over the last couple days. I haven't had any formal training in WPF, Xaml, and MVVM so I suspect I'm missing something obvious, or am attempting to do something that isn't possible. I'm going to keep attempting variations on the above and researching until I find something that works, but I thought I would tap into the collective knowledge here to help me find a solution.
What I'd prefer is to have the group of Views use the single ViewModel as their DataContext so I can bind properties to their controls. And have the Views' navigation be controlled by a DataTemplateSelector. Is there a way to do this that I'm not seeing?
Thank you for your time!
I have had similar issues before, I have had good luck using a RelativeSource binding. Maybe try something like this:
DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContentControl},Path=DataContext}"
Just a thought.

trying to bind datagrid item source to a property in another class

I have a WPF app with a MainWindow. The MainWindow consists of several CLR properties of type ObservableCollection. The MainWindow has a datagrid, whose ItemsSource property is bound to one of the observable collections (works fine). Next, I have a dialog. Its purpose is to display one of the observable collections from the main window in a datagrid. The dialog gets instantiated in the MainWindow. Initially I was passing the ObservableCollection to the dialog's constructor, and copying it into the dialog's CLR property. Then I would set the DataContext of the dialog to itself, and bind the ItemsSource property in the datagrid to the name of the CLR property. This worked fine.
Is there a better way to do this instead of passing the observable collection through the constructor? I tried setting the ItemsSource property of the Datagrid in the dialog to the observable collection in the MainWindow by using the GUI editor, which generated a binding using RelativeAncestor, but the data did not show. The problem is I have a bunch of dialogs that are meant to display data from the MainWindow, and I feel like there should be a simpler solution rather than passing everything to dialog's constructor. Also, would the dialogs be considered SubViews? The main window is a view.
Let's say your Dialog control is named DialogControl and has a DependencyProperty named Items defined in its code behind. In the XAML, I would bind this property to the DataGrid like this:
<DataGrid ItemsSource="{Binding Items, RelativeSource={RelativeSource Mode=
FindAncestor, AncestorType={x:Type DialogControl}}" />
This RelativeSource binding will go off and search through the properties of your DialogControl class and find the Items property. Note: Do NOT set the DataContext of the UserControl to itself.
Now in your MainWindow.xaml.cs file where you instantiate your DialogControl, you can set the Items property:
DialogControl dialogControl = new DialogControl();
dialogControl.Items = someCollection;
dialogControl.Show();
UPDATE >>>
Oh I see what you're after now... you want to bind from your UserControl to the actual collection in the MainWindow.xaml.cs file. You can still follow my advice, but instead of having the DependencyProperty in your DialogControl, you need to have it in your MainWindow.xaml.cs file. In that case, your binding in the UserControl would be:
<DataGrid ItemsSource="{Binding Items, RelativeSource={RelativeSource Mode=
FindAncestor, AncestorType={x:Type MainWindow}}" />
For this to work, the Items property must be a DependencyProperty.

How do i access a combobox text in a usercontrol from the wpf forms viewmodel?

I have a UserControl with 4 combobox bound to collections in viewmodel for that usercontrol.
I have used this control in a wpf form. This wpf form has its own viewmodel.
How do i access the text from the 4 comboboxes within the wpf form's viewmodel?
EDIT: i saw that you have different viewmodels. now it depends of the use of your usercontrol and the use of mvvm:)
you can use messenger or eventaggregator to comunicate the seleteditems from usercontrolviewmodel to mainviewmodel.
you can also use RelativeSource binding in your usercontrol to bind the selecteditem to your mainviewmodel directly (usercontrol then is just a composition of controls).
you can can rid of the usercontrol viewmodel and put all in the mainviewmodel and take my old example
you can create DependencyProperties for the SelectedItems in your usercontrol!(not usercontrol viewmodel!) and bind these to the properties in your mainviewmodel. i think thats the cleanest way if the usercontrol should be a real usercontrol.
old example:
in your viewmodel: //the real code should of course implement INotifyPropertyChanged and raise it properly
public ObservableCollection<string> MyFirstCollection {get; set;}//init once, add,remove,clear to alter
public string MySelectedCombobox1Value {get;set;}
in your usercontrol:
<ComboBox ItemsSource="{MyFirstCollection }" SelectedItem="{Binding MySelectedCombobox1Value, Mode=TwoWay}" />
thats all relating to your question. be sure that you set the DataContext right. you can check this with tools like snoop. the code i posted expected that the dataconext for the combobox is the viewmodel.
The UserControl should inherit the data context of the form you're adding it to which would be the view model. Any bindings in the UserControl would then be relative to the inherited data context. Have you tried binding to a view model property to ComboBox.Text?
UPDATE
Sorry, misread your question. Didn't see that the user control already has its own view model.
While it seems like there's a better approach, you could expose dependency properties on the user control that exposé the text of each combobox. Just thinking out loud.
The only clean way to do this is with binding, and the only way that would be recommended is if the user control exposes a DependencyProperty for the ViewModel or the individual text properties (as was suggested by sellmeadog) for consumption. Then you can have a property in the parent ViewModel that binds directly to that Dependency Property.

Checkbox binding not working

I searched the forum and did everything as advised to create dependancy property and bind it to checkbox, but for some reason it doesn't bind.
<CheckBox IsChecked="{Binding ElementName=MainWindow, Path=isLoop}" Content="" Height="22" HorizontalAlignment="Left" Margin="250,208,0,0" x:Name="checkBox1" VerticalAlignment="Top" Width="22" />
C#
public bool isLoop
{
get { return (bool)GetValue(isLoopProperty); }
set { SetValue(isLoopProperty, value); }
}
public static readonly DependencyProperty isLoopProperty =
DependencyProperty.Register("isLoop", typeof(bool), typeof(MainWindow), new UIPropertyMetadata(true));
You've made some key mistakes in your sample.
First, you are not binding to an object that supports your "isLoop" property (unless "MainWindow" is a custom control that has that property). Somewhere in that CheckBox's hierarchy, you need to set the DataContext to an object that supports it, or bind to an element that has that property.
Second, you should rarely, if ever, create a dependency property in your business object. For business objects, follow the INotifyPropertyChanged pattern. Typically, you should create dependency properties in visual UI elements, such as custom controls in order to be able to bind data to them (a target, not the source).
So, to fix your problem, you should probably create an object that implements INotifyPropertyChanged, create an IsLoop property that throws the NotifyPropertyChanged event in the setter, and set this object as the DataContext to the CheckBox's parent container (or further up the hierarchy if appropriate).
HTH
You are binding to the Window itself. Do you mean to do that? Unless your code example is in the code behind then the binding will not work.
Since you're using an ElementName binding, I am guessing you are binding to a UI element. The problem is, none of the default UI elements come with a property called isLoop, so your binding is invalid.
There are a few things you can try.
If your isLoop property is part of the object named MainWindow's DataContext, change your binding to DataContext.isLoop
<CheckBox IsChecked="{Binding ElementName=MainWindow, Path=DataContext.isLoop}" ... />
If isLoop is actually a property on a custom class called MainWindow, such as your dependency property implies, verify that the object named MainWindow is actually of type MainWindow
<local:MainWindow x:Name="MainWindow" />
And if neither of those work, post your full XAML (particularly the part named MainWindow), the code for the class MainWindow, and the code that ties the MainWindow class object with the XAML UI.
The isLoop won't trigger when the checkbox is clicked. That is simply for accessing the depency property in code. You should add a PropertyCallback function and register that in the metadata.

WPF access info from the containing Control

I have a UserControl(a) with a stackpanel which has its ItemSource set to a collection.
The StackPanel then contains a set of UserControl(b) that contain a few buttons and a datagrid control.
Is there a way from the code behind in the UserControl(b) to access properties in the code behind of the parent UserControl(a).
Basically when UserControl(a) loaded into a window a parameter is passed in that contains whether the form will be considered read only or not. I would like bind the visibility of the buttons in Usercontrol(b) to the readonly property in the codebehind of the parent UserControl(a).
Normally with WPF I'd suggest you implement the Model-View-ViewModel pattern (see MSDN).
With this pattern you'd create a ViewModel with all of the data in that you want to bind. This would be set as the data context for the (a) usercontrol. That control would then bind all of it's controls to properties on the datacontext.
The child (b) usercontrol would inherit this datacontext and could therefore bind it's controls to the same properties as (a) uses. This is because datacontexts are inherited down the logical (and visual) tree until such point as it's overridden.
So for you I'd be looking at creating a ViewModel that contains the property ReadOnly. You can then set this ViewModel object as the datacontext for the (a) usercontrol. The (b) usercontrol, since it's under the (a) usercontrol hierarchy will inherit the same datacontext. This will then allow you to bind controls within (b) to the same properties as (a) as shown below.
<Button IsEnabled="{Binding ReadOnly}"
Context="Click me!"
Command="{Binding ClickMeCommand}" />
To set the datacontext in the view code-behind I do something like this constructor shown below.
public MyView(IMyViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
MyView is the class that inherits from UserControl in your instance. You don't have to get the viewmodel in the way I have, I'm using Unity to inject the viewmodel into the views that are constructed automatically since I'm using Prism but you can just create it as a normal object and assign it to the datacontext.
Note that I've also bound the command to the button using the datacontext as I usually expose those via the ViewModel too, this is easy if you create a wrapper class that implements ICommand and proxies to a delegate. See DelegateCommand blog article or look at the DelegateCommand class in Prism if you are interested.
If for some reason you do override the datacontext, which can happen when using a master/details view where you change the datacontext of the details section of the view to be the currently selected item in the list, then you can still access the parent datacontext by using a relative source binding.
E.g.
<ComboBox Grid.Row="1" Grid.Column="1" x:Name="Unit" IsReadOnly="True"
ItemsSource="{Binding Path=DataContext.AvailableUnits, RelativeSource=
{RelativeSource Mode=FindAncestor,
AncestorType={x:Type Window}}}"
DisplayMemberPath="Name" SelectedItem="{Binding Unit}" />
Note the ItemsSource binding uses a relative source to find the parent window and then bind to a property of it's datacontext. I've also split the ItemsSource binding within the quotes across multiple lines for clarity here but don't do that in your xaml, I'm not sure it'll work there (not tried to see if markup extensions are that tolerant of whitespace).

Resources