WPF access info from the containing Control - wpf

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).

Related

Binding property with parent ViewModel

Please refer to How can I tell my DataTemplate to bind to a property in the PARENT ViewModel?
I have similar problem... But this solution didn't work for me. I have a MainViewModel which has an observable collection of another view model say View1/ViewModel1. This view has a tree control and I need context menu for the tree. My main view has a menu. Those main menu and context menu are connected. So how can I bind the context menu commands to the main viewmodel's properties?
Basically, you need to use a RelativeSource binding. The standard way is to find an ancestor (or parent) of the control of a particular type:
{Binding DataContext.PropertyName, RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type YourViewsNamespace:YourParentView}}}
Assuming that your parent view has a view model set to its DataContext, this binding will access it... the DataContext is the DataContext of the view, eg. the view model that is set as the DataContext. So, the PropertyName property is a public property from that view model.
Regarding the part of your question that has been asked so many times before, please see the following links (or just search online):
Context Menus in WPF
Binding ContextMenu to its logical Parent
1. ParentViewModel has NavigateRecordCommand
2. Parentview has the DataContext Set to my ParentViewModel.
<UserControl x:Class="SampleProject.UI.ParentView"
<Grid>
....
<!--User control is here-->
<local:NavigationControl Grid.Row="1" />
....
</Grid>
3. Child Control as below. Not bounded to its ViewModel. Bounded to Parent Views DataContext i.e. ParentViewModel.
<UserControl x:Class="SampleProject.UI.NavigationControl"
...
...
xmlns:Local="clr-namespace:SampleProject.UI">
<Button Command="{Binding DataContext.NavigateRecordCommand, RelativeSource={RelativeSource AncestorType=Local:ParentView}}"
CommandParameter="MoveFirst"/>

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.

Binding from usercontrol to property of parent usercontrol

Let's say I have a user control called Graph in a window. This user control contains a grid, within which there is another user control, called Toolbar. Now Graph exposes a public property called Mode, which uses a dependency property called ModeProperty as a backing store. I want an element in the Toolbar user control to bind to the Mode property in its ancestor Graph. How can I do this? Even just getting a reference to Graph is proving to be harder than I expected, this.parent gets a reference to the grid rather than the Graph.
You can bind it in xaml only like this -
<Toolbar Mode="{Binding Path=Mode, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType = UserControl, AncestorLevel =2}}"/>
Also you can use the ElementName in your binding like this -
<Toolbar Mode="{Binding Path=Mode, ElementName=GraphUserControl}"/>
You have to set x:Name property on your userControl to which you want to bind to -
<UserControl x:Name="GraphUserControl"></UserControl>
Or in case you won't to do in code behind, you can look at this useful post for travelling to ancestor parent - Visual Tree Navigator

Where is the datacontext for the CustomerViewModel in the official MSDN MVVM article?

I speak about josh smith article.
can anyone show me please how the CustomerView.xaml specifically this:j
<TextBox
x:Name="firstNameTxt"
Grid.Row="2" Grid.Column="2"
Text="{Binding Path=FirstName, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
Validation.ErrorTemplate="{x:Null}"
/>
Why is there a Binding to FirstName which is public property in the CustomerViewModel.
There is a datacontext set for the MainViewModel, but not for the CustomerViewModel, so why does the binding work ???
Check out the ResourceDictionary in MainWindowResources.xaml. Josh uses the following code to describe what View should be used if an instance of CustomerViewModel is shown in the main window:
<DataTemplate DataType="{x:Type vm:CustomerViewModel}">
<vw:CustomerView />
</DataTemplate>
We've described that when our DataType is of Type CustomerViewModel, we'll create a new instance of the CustomerView. WPF takes care of the DataContext and creation when it sees the CustomerViewModel type.
From the rest of the article:
Applying a View to a ViewModel
MainWindowViewModel indirectly adds
and removes Workspace ViewModel
objects to and from the main window's
Tab Control. By relying on data
binding, the Content property of a
TabItem receives a
ViewModelBase-derived object to
display. ViewModelBase is not a UI
element, so it has no inherent support
for rendering itself. By default, in
WPF a non-visual object is rendered by
displaying the results of a call to
its ToString method in a TextBlock.
That clearly is not what you need,
unless your users have a burning
desire to see the type name of our
ViewModel classes! You can easily tell
WPF how to render a ViewModel object
by using typed DataTemplates. A typed
DataTemplate does not have an x:Key
value assigned to it, but it does have
its DataType property set to an
instance of the Type class. If WPF
tries to render one of your ViewModel
objects, it will check to see if the
resource system has a typed
DataTemplate in scope whose DataType
is the same as (or a base class of)
the type of your ViewModel object. If
it finds one, it uses that template to
render the ViewModel object referenced
by the tab item's Content property.
The MainWindowResources.xaml file has
a Resource Dictionary. That dictionary
is added to the main window's resource
hierarchy, which means that the
resources it contains are in the
window's resource scope. When a tab
item's content is set to a ViewModel
object, a typed DataTemplate from this
dictionary supplies a view (that is, a
user control) to render it, as shown
in Figure 10.
The DataContext for the MainViewModel in App.xaml.cs serves as a starting point for our application.

Resources