Change binding source back to view model inside container in XAML - silverlight

I am using Silverlight 4 and the MVVM pattern.
My view model has two properties:
SomeProperty and
MyCommand
SomeProperty is a complex type and has a lot of subproperties. MyCommand is a property to handle commanding from a Button.
I have a child window (the view) with a Grid as the LayoutRoot which is bound to the SomeProperty property of the view model.
<Grid x:Name="LayoutRoot" DataContext="{Binding SomeProperty, Mode=TwoWay}">
...
</Grid>
However, inside the Grid I want to bind a Button's Command property to the MyCommand property of the view model:
<Button Command={Binding MyCommand} />
But this is not working because MyCommand is a property of the view model, and not a property of the view model's SomeProperty property. (When I click on the Button it does not execute the command.)
Anywho, is there a way using data binding in Silverlight 4 such that I can have a container UI element set its DataContext property explicitly, but then have a different control within the container reference a property that's a sibling (or parent or whatever) of the DataContext of the containing control?
My current workaround is to define the binding in the view's class, but I'd rather have it in the XAML.
Thanks

If you give your root element (ChildWindow, UserControl, whatever) a name, then you can use ElementName to get to the view model.
<UserControl x:Name="MyUserControl">
<Grid x:Name="LayoutRoot" DataContext="{Binding SomeProperty, Mode=TwoWay}">
<Button Command="{Binding MyCommand}" DataContext="{Binding DataContext, ElementName=MyUserControl}" />
</Grid>
</UserControl>
Or, here's another way to do the same thing.
<UserControl x:Name="MyUserControl">
<Grid x:Name="LayoutRoot" DataContext="{Binding SomeProperty, Mode=TwoWay}">
<Button Command="{Binding DataContext.MyCommand, ElementName=MyUserControl}" />
</Grid>
</UserControl>

You try add datacontext to binding? The datacontext have to point to your viewmodel, because the default data context is a parent control or parent data context, in this case your layout root.
See this
and this
I hope this help.
Regards.

I use a version of the BindableProxy described in this post:
http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx
Above your Grid (probably within the UserControl.Resources) you would create:
<UserControl.Resources>
<ns:BindableProxy x:Key="BindableProxy" />
<UserControl.Resources>
Then, in the button binding:
<Button Command="{Binding DataSource.MyCommand, Source={StaticResource BindableProxy}}" />

Related

WPF Set Visibility on DataTemplate UI Element from MVVM View Model

I have a control that is set up as a DataTemplate:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<BooleanToVisibilityConverter x:Key="BoolToVis" />
<DataTemplate x:Key="KEYBOARD_EN">
<StackPanel>
<Button Visibility="{Binding Path=RegisterButtonVisible}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
</StackPanel>
</DataTemplate>
In this DataTemplate there is a control on which I wish to set the Visibility from various view models:
<Button Visibility="{Binding Path=RegisterButtonVisible}" Style="{StaticResource ...} > Register </Button>
I do routed events with my control, so I tried to set up something similar, but no matter what I try, the RegisterButtonVisible property does not get picked up:
public partial class MainKeyboard : UserControl
{
public static DependencyProperty RegisterButtonVisibleProperty;
public Visibility RegisterButtonVisible
{
get { return (Visibility)GetValue(RegisterButtonVisibleProperty); }
set { SetValue(RegisterButtonVisibleProperty, value); }
}
static MainKeyboard()
{
RegisterButtonVisibleProperty = DependencyProperty.Register("RegisterButtonVisible", typeof (Visibility),
typeof (MainKeyboard));
}
}
In my ViewModel I do this:
public Visibility RegisterButtonVisible // get, set, raisepropchange, etc
My DataTemplate with the button in it is wrapped in a userControl:
<UserControl x:Class="Bleh.Assets.MainKeyboard"
x:Name="TheControl"
Unloaded="UserControl_Unloaded">
<Viewbox>
<Grid>
<ContentControl Name="ctrlContent" Button.Click="Grid_Click" />
</Grid>
</Viewbox>
and is used in my views like this:
<assets:MainKeyboard
RegisterButtonVisible="Collapsed"
Loaded="MainKeyboard_Loaded">
<b:Interaction.Triggers>
<b:EventTrigger EventName="Register">
<b:InvokeCommandAction Command="{Binding ConfirmEmailAddressCommand}"/>
</b:EventTrigger>
<b:EventTrigger EventName="Enter">
<b:InvokeCommandAction Command="{Binding EnterKeyCommand}"/>
</b:EventTrigger>
</b:Interaction.Triggers>
</assets:MainKeyboard>
Please note this attribute:
RegisterButtonVisible="Collapsed"
This is my dependency property. It shows up in intelliesense, so the CLR has registered it correctly, but it does NOT pick up the property assignment (Collapsed is ignored).
This makes me feel like it is very close, but I do remember someone telling me I can not do this, thus the EventTriggers (this is a common issue with datatemplates and MVVM apparently).
So one option is to use something in the Interaction namespace, like I do my event triggers ( I just need to fire a "Visibility" trigger on this button somehow, at least I figure).
What is the right ANY way to do this in MVVM?
Fixing your code
In order to make your existing code work, you need to tell need to tell WPF what object RegisterButtonVisible should be read from. If it's a user control, give the UserControl a name and then reference that element via ElementName, like so:
<UserControl ... lots of stuff here
x:Name="TheControl"
>
In your button binding:
<Button Visibility="{Binding ElementName=TheControl, Path=RegisterButtonVisible}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
Of course, if you can't do that because the button and the usercontrol are in different files, you can still use an ancestor binding:
<Button Visibility="{Binding RelativeSource={RelativeSource AncestorType={x:Type assets:MainKeyboard}},
Path=RegisterButtonVisible}"
Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
which, for each button, will walk up to find the closest instance of assets:MainKeyboard and then bind to the RegisterButtonVisible property.
Using MVVM
If you want to achieve the same using MVVM (instead of on a control), you need to use a converter to convert a boolean to a visibility property, like so:
<Button Visibility="{Binding IsRegistrationAllowed, Converter={StaticResource BoolToVis}}" Style="{StaticResource RegisterKeyboardButtonStyle}">Register</Button>
Of course, that assumes that your DataContext is set up correctly and pointing at your ViewModel.

WPF Databinding between two usercontrols

I'll try to give a bit more details of this question
In a WPF application, on the left side of 'mainwindow' I have a UserControl containing a listBox, like this
<UserControl.Resources>
<ObjectDataProvider x:Key="viewModel" ObjectType="{x:Type vm:TemplateListViewModel}"/>
</UserControl.Resources>
<StackPanel>
<ListBox Height="Auto" Name="TemplateList" ItemsSource="{Binding Source={StaticResource viewModel}, Path=TemplateNames}"
</StackPanel>
As shown, listbox items are fetched from a xml file via viewModel.
Now I have another usercontrol2 containing a Label to echo the selection from "TemplateList".
Also under this label I have another usercontrol3 containing a datagrid, which data will be fetched from a xml file based on the label, or the selection from the listbox on the left side of the window through usercontrol3's viewmodel.
So the question is how to pass the "SelectedItem" or "SelectedIndex" to viewModel of the Label and datagrid?
I know it works when I bind a label to a listbox with 'elementName' and Path to 'SelectedItem'. Now I couldn't figure out how to do with usercontrols and 'ObjectDataProvider'. I tried as suggested below couldn't get it to work out. So far for usercontrol2, if I use the same ObjectDataProvider as the above, I can get this label to work as
<Label Name="TemplateNameLabel" Content="{Binding Source={StaticResource viewModel}, Path=TemplateNames[0]}" />
Where 'TemplateNames' if of type 'XmlNodeList' because it was read from xml file. But I really want is something like
<Label Name="TemplateNameLabel" Content="{Binding Source={StaticResource viewModel}, Path=TemplateNames[SelectedIndex]}" />
Can this be done without any command, just like binding to 'elementname' without usercontrol involved?
Thanks.
I've removed not important properties to highlight the main:
<ListBox Name="TemplateList" ItemsSource="{Binding Source={StaticResource viewModel}, Path=TemplateNames}" SelectedItem="{Binding Path=SelectedTemplate, Mode=OneWayToSource}" />
Note, that you will need SelectedTemplate property with public setter of type TemplateName - the same type that is used for single element in the TemplateNames collection in the ViewModel. In that setter you could pass the value to whatever other view-models you need.

WP7 XAML Bind to property in parent

I'm writting an app in WP7 (Silverlight 3). I have a view model that looks like this
public class MainViewModel
{
public List<ActivityTypes> ActivityTypes{get;set;}
public RelayCommand LoadActivity{get;set;}
}
My pages datacontext is set to the view model and I have a listbox with it's item source set to the ActivityTypes collection. In the listbox I'm trying to render a list of buttons who's command is bound to the LoadActivity property on the viewmodel. RelayCommand is part of the MVVM Light toolkit in case you are wondering what it is.
The problem I am having is I can't find a way to bind my button command to the LoadActivity property as my listbox has it's itemsource set to the Activitytypes collection and this property is in the parent. I've read about FindAncester but it doesn't look like this is supported in Silverlight 3.
My XAML looks like this
<UserControl.Resources>
<DataTemplate x:Key="ActivityTypeListTemplate">
<StackPanel>
<Button Command="{Binding LoadActivity}"> <!--binding not found as we're in the collection-->
<TextBlock Text="{Binding Name}" FontSize="50"/>
</Button>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<ListBox Margin="0" ItemsSource="{Binding ActivityTypes}" ItemTemplate="{StaticResource ActivityTypeListTemplate}"/>
</Grid>
What's the best way to code something like this?
There is no "direct" way to do this. You can set the Buttons DataContext to your MainViewModel (preferably as a StaticResource link) and the it would work.
Take a look at Bind to parent object in xaml

Is it possible to Bind the DataContext of a UserControl to a property

I have a user control that I would like to use in two different contexts. The user control needs its DataContext set to an instance of an appropriate ViewModel that has been created by the parent view/viewmodel.
I was hoping for something like:
<local:Child DataContext="{Binding ChildViewModel}"/>
where ChildViewModel is a inpc-styled property of the ViewModel that the page is bound to.
That doesn't seem to work. Is it possible to assign the DataContext by using Binding?
It would probably be simpler to bind the Content of an ContentControl to your child ViewModel like this:
<ContentControl Content="{Binding ChildViewModel}" />
..and then have a DataTemplate to apply your local:Child View, like this
<DataTemplate DataType="{x:Type local:ChildViewModel}">
<local:Child />
</DataTemplate>

Command Binding in UserControl used as ListBox.ItemTemplate

I have a Listbox with a UserControl as the DataTemplate. This UserControl has a Button to remove that item from the list.
<ListBox x:Name="FileList" ItemsSource="{Binding Files}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Views:FileItem/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The ItemsSource is defined as:
ObservableCollection<FileViewModel> m_fileViews = new ObservableCollection<FileViewModel>();
Here is the UserControl simplified:
<UserControl x:Class="Views.FileItem">
<Canvas x:Name="LayoutRoot">
<TextBlock x:Name="FileName" Text="{Binding FileName}" />
<Button Content="Remove"/>
</Canvas>
</UserControl>
When the user clicks the Remove button, it should remove this item from the ObservableCollection.
The problem is, the DataContext for each ListBoxItem is a different ViewModel than the ViewModel that holds the ObservableCollection.
I'm not sure how to bind the Remove button to an ICommand in the "parent" ViewModel. Any help would be appreciated. Thanks so much.
I would bind the button to an ICommand in the UserControl's ViewModel, then send a message across to the parent ViewModel using loosely-coupled messaging (available in most Mvvm frameworks, like MvvmFoundation)
Let the parent VM register for the 'remove me' message, and update the ObservableCollection accordingly...
Hope this helps :)

Resources