I am working on a Silverlight application which makes an extensive use of Prism, the MVVM pattern and MEF. For several reasons, I have chosen to follow a View-first approach.
In one of the Views there is a DataGrid, and one of the columns of this grid is a DataGridTemplateColumn, which has just a Button.
I'd like to define both a Command and a CommandParameter on the Button. The Command should be a DelegateCommand of the ViewModel. CommandParameter should be the SelectedItems list coming straight out of the dataGrid.
I've tried several approaches to do this, but either Command or CommandParameter are null.
It follows the code that I originally wrote:
<sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="15" Height="15" Content=">"
Command="{Binding UpdateSearchParametersCommand}"
CommandParameter="{Binding SelectedItems, ElementName=dataGrid}">
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
Could someone advice me on what the best way to go about it is?
Thanks in advance,
Gianluca.
Your current binding is pointing to DataGridRowItem.UpdateSearchParametersCommand. You need to change it to point to DataGrid.DataContext.UpdateSearchParametersCommand
<sdk:DataGrid x:Name=dataGrid>
<sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="15" Height="15" Content=">"
Command="{Binding DataContext.UpdateSearchParametersCommand, ElementName=dataGrid}"
CommandParameter="{Binding SelectedItems, ElementName=dataGrid}">
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid>
If you bind your DataGrid using ItemsSource, then Command and CommandParameter binding is associated to the current item - the way you've written.
You should use alternative source in this case. Command should be binded to the DataContext.UpdateSearchParametersCommand and CommandParameter - to DataContext.SelectedItems.
In your case neither UpdateSearchParametersCommand, nor SelectedItems cannot be found in the binded item.
UPDATED
Be sure to set the right type for ancestor. I've set it to window, but maybe you are using UserControl.
<sdk:DataGridTemplateColumn>
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="15" Height="15" Content=">"
Command="{Binding Path=DataContext.UpdateSearchParametersCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
CommandParameter="{Binding Path=DataContext.SelectedItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
In silverlight 5 you can do this
<Button Command="{Binding Path=DataContext.PreviewPublishCommand, RelativeSource={RelativeSource AncestorType=controls:ChildWindow}}" Content="Publish" />
Just adjust AncestorType to be whatever your top level element is (UserControl, ChildWindow, etc).
Many of you tried to help me out on this. Thank you for that.
Unfortunately the provided answers were mostly relative to WPF.
Here is how I've solved the problem:
<helpers:BindingHelper.Binding>
<helpers:BindingList>
<helpers:RelativeSourceBinding TargetProperty="Command" Path="DataContext.ToggleDataArchiveInheritanceCommand" RelativeMode="FindAncestor" AncestorType="ChildWindow" />
</helpers:BindingList>
</helpers:BindingHelper.Binding>
Ok, this comes from another point of the same application, but the principle is the same.
If a binding is defined inside a , the only way you have in Silverlight to reach out other elements that normally would be out-of-scope (as they are not part of the DataTemplate) is to walk through the xaml object tree. That's what the BindingHelper does.
Posting here as I hope the information will be useful to someone else.
Cheers,
Gianluca
Related
I have an ItemsControl whose for the ItemTemplate DataTemplate contains a Button. I want the Command on the button to bind to a Command on the DataContext of the ItemsControl, not the ItemTemplate. I think the solution has to do with using RelativeSource, but my attempts so far have failed:
<ItemsControl ItemsSource="{Binding Games}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Path=GameSelectedCommand, Source={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
CommandParameter="{Binding}"
Style="{StaticResource MenuButtonStyle}"
Content="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
How can I get the Button to bind to the GameSelectedCommand of the ItemsControl's DataContext object?
You're setting the source of the binding to the ItemsControl itself. Therefore, you'll need to dereference the DataContext of the ItemsControl:
Command="{Binding DataContext.GameSelectedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}"
How would you have known this? Take a look at your debug output window when running the app. You'll see a message along the lines of "Cannot resolve property 'GameSelectedCommand' on type 'ItemsControl'".
I do have an Window (DataContext VM) and a Listview (ItemSource Observable List in VM)
Data comes from an sqlite DB, the Observable List is Type of Model, representing Residents.
Each Resident should have an "CheckIn" or "CheckOut" Button, for them, i do have ICommands in my VM.
<ListView ItemsSource="{Binding Oc_BewohnerList}">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name:" Width="50"/>
<TextBlock Text="{Binding Nachname, StringFormat={}{0}\, }" FontWeight="Bold"/>
<Button Content="Check In" Command="{Binding Source={RelativeSource AncestorType={x:Type viewModels:BewohnerVM}}, Path=CheckInCommand}" CommandParameter="{Binding Id}" />
Why do i get an Error "Die Eigenschaft "CheckInCommand" wurde im Objekt vom Typ "RelativeSource" nicht gefunden." That the CheckInCommand is not found?
I did it first with Xamarin.Forms and there it worked in a similiar way ... i cant find a solution myself, tried tons of combinations for realtivesource and ancestorlevel ...
Thx in advance
Nala
A relativesource binding is up the visual tree.
As in controls not viewmodels.
You're not going to be looking for a viewmodel.
If the command is a property on VM and that's the datacontext of the window then the ListView inherits that datacontext.
You've not explained everything but I think you need more like:
<Button Content="Check In"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListView}},
Path=DataContext.CheckInCommand}"
CommandParameter="{Binding Id}" />
Likely just a small syntax error and you want (note the double usage of RelativeSource , once as a property of Binding once as MarkupExtension...)
EDIT and also the AncestorType should be an UIElement, whose DataContext exposes that CheckInCommand.
<Button Content="Check In"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListView}}, Path=DataContext.(viewModels:BewohnerVM.CheckInCommand)}"
CommandParameter="{Binding Id}" />
I have two controls in a grid.
<TextBlock Text="{Binding Name}" TextAlignment="Center" />
<TextBox Visibility="{Binding ElementName=EditMode,Source={Binding RelativeSource=
{RelativeSource FindAncestor, AncestorType={x:Type Window}}},
Converter={StaticResource BoolToVis}}" Text="{Binding Name}"
TextAlignment="Center" />
I am trying to implement something like an editable/non editable behaviour. I know i might choose for a TextBox and simply change the IsEditable property but still, in my scenarion i would need to DataContext, at least that's what i am thinking of.
In my example, the TextBlock works fine, and the Text property on the TextBox also works fine but for the Visibility part, i want to bind to a data property ( EditMode which is a boolean) found on some other layer. Is there a way to change the DataContext to that, but only for the Visibility ? The Text property should remain as it is now.
Should i try a hack, to define an invisible checkbox, change IsChecked when my Edit button is clicked and bind directly to that ? I will try this. I think this way, no DataContext changing is needed.
#FrumRoll is correct that you can access a property that is not in the set DataContext object using a RelativeSource Binding. However, I'm not sure that their code is quite right... try this:
<TextBox Visibility="{Binding DataContext.EditMode, RelativeSource={RelativeSource
AncestorType={x:Type YourXamlPrefix:MainWindow}}}, Converter={StaticResource
BoolToVis}}" Text="{Binding Name}" TextAlignment="Center" />
Clearly, you'll need to change the YourXamlPrefix value with your local XAML Namespace Prefix and MainWindow with the name/type of your Window if it is not called MainWindow. This also assumes that your EditMode property has been defined in that Window.
This may also work, but is not specifically looking for your exact Window, so may have some problems:
<TextBox Visibility="{Binding DataContext.EditMode, RelativeSource={RelativeSource
AncestorType={x:Type Window}}}, Converter={StaticResource BoolToVis}}"
Text="{Binding Name}" TextAlignment="Center" />
It seems to me you were almost there, you should be able to use a RelativeSource to do this. The issue is that you misused ElementName, ElementName is to bind to a property of a named source and would be used instead of RelativeSource. What you meant to use was Path, which is optional as seen below.
<TextBox Visibility="{Binding DataContext.EditMode, RelativeSource={RelativeSource AncestorType={x:Type Window}},
Converter={StaticResource BoolToVis}}"
Text="{Binding Name}" TextAlignment="Center" />
I have written a tool in which a ListBox is bound to a ObserservableCollection<object> with varying datatypes I've define. I use a PropertyDataTemplateSelector to present the data in the ListBox. The PropertyDataTemplateSelector references several DataTemplates that are set as UserControls. There is a background class that provides logic to the PropertyDataTemplateSelector by checking the object type and then applying the correct DataTemplate.
Here's an abbreviated example of the XAML for the UserControls and the MainWindow.
UserControl1
<TextBlock Text="{Binding Path=Val1}"
Style="{StaticResourse Yes}" />
<Button Content="I'm Button 1"
Command="{Binding Path=PathtoCommand1}"
CommandParameter="{Binding Parameter1}"
IsEnabled="{Binding IsEnabled1}" />
<Button Content="I'm Button 2"
Command="{Binding Path=PathtoCommand2}"
CommandParameter="{Binding Parameter2}"
IsEnabled="{Binding IsEnabled2}"
Tag="{Binding Path="DataContext.TagItem2}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem IsCheckable="True"
IsChecked="{Binding Path=Tag}"
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</UserControl>
UserControlN
<UserControl x:Class="AwesomerControl">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=FancyName2}"
Style="{StaticResourse Yes}" />
<Button Content="Clicker 1"
Command="{Binding Path=DoSomethingGreat1}"
CommandParameter="{Binding Greatness1}"
IsEnabled="{Binding IsTurnedOn1}" />
<Button Content="Clicker 2"
Command="{Binding Path=DoSomethingGreat2}"
CommandParameter="{Binding Greaterness2}"
IsEnabled="{Binding IsTurnedOn2}"
Tag="{Binding Path="DataContext.TagItem2}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem IsCheckable="True"
IsChecked="{Binding Path=Tag}"
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>
</UserControl>
Here I set the UserControls to a specified DataTemplate. The UserControls were moved out to make the XAML easier to read/navigate. In actuality the UserControls are a few hundred lines each.
<Window.Resources>
<DataTemplate x:Key"Template1">
<customControls:AwesomeControl/>
</DataTemplate>
...
<DataTemplate x:Key"TemplateN">
<customControls:AwesomerControl/>
</DataTemplate>
<dts:PropertyDataTemplateSelector x:Key="templateselector"
Template1="{StaticResource Template1"}
...
TemplateN="{StaticResource TemplateN"}
</Window.Resources>
The ListBox is defined as this.
<ListBox ItemSource="{Binding Path=CollectionofMyObjects}"
ItemTemplateSelector="{StaticResource templateselector}" />
I am using a single ViewModel to drive the MainWindow and the UserControls.
So that's where I'm at, essentially. I have this currently working as I'd like, but in an ongoing effort to learn (this is my first MVVM/WPF/C# project) I'd like to keep exploring how to make my code "better" (however that's defined). I'm not looking to solve an error here. So to avoid a general/broad question, I'll ask what I think I want to know. Someone can correct me and I'll update the "question(s)" appropriately
Question: How can I go about producing a ViewModel for each of the UserControls? Some of the ViewModels, for the UserControls, will occasionally require two-way communication to the MainWindow_ViewModel. The main crux of my problem is figuring out how the multiple VMs will communicate.
You're close, but it's not quite MVVM yet. ;)
First, break out all the functionality that is relevant to each UserControl into their own classes. These are your view-model classes.
Your controls should now become "view" classes, and they deserve their own mark-up file. Rather than use a template selector, you can use the DataTemplate.DataType to automatically connect the view-model class type to its view.
There are a lot of options for communication between view-models. To further your education, I'd consider looking at a light-weight MVVM framework that has built-in solutions for communication. My personal favorite is Caliburn.Micro, which includes an EventAggregator, a service that provides the ability to publish an object from one view-model to another in a loosely-coupled fashion.
Keep learning, you're on the right track!
i have a datagrid declared in the xaml as follows:
<sdk:DataGrid x:Name="ProductsForProjectDataGrid" AutoGenerateColumns="True" ItemsSource="{Binding Path=Products.ProductsForProject}">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn x:Name="DeleteTemplate" Width="10*">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="DeleteProductButton" Command="{Binding DeleteProductCommand}" CommandParameter="Products.SelectedProduct" >
<Button.Content>
<Image x:Name="DeleteProductImage" Visibility="Visible" Height="20" Source="C:\Documents and Settings\DELETE.GIF" Width="20"/>
</Button.Content>
</Button>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
<sdk:DataGridTextColumn Binding="{Binding Product}" Header="Product Name" IsReadOnly="True" Width="40*"/>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
and in the viewModel i've to link the button to a command, i can get the reference to the datagrid using this,
_dlgProducts.ProductsForProjectDataGrid
where _dlgproducts is the object,
now how can i get a reference to the delete button,
once i get the reference i can bind a command to it,
i need something like
_dlgProducts.ProductsForProjectDataGrid.DeleteProductButton
or something, im not sure how to get it...
Thanks :)
Binding inside DataTemplate in DataGrid in Silverlight doesn't work as you would expect. You need to use DataContextProxy. See sample here:
http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx
Don't really know what your problem is. You want to set the command for a Button in the view model (which is by the way breaking your mvvm...) but you have the delete command bound to the button in XAML. If i understand correctly your binding is not working and you want to do it in code behind. If that is so please look at this question and the answer. Two more things:
when you get it working your CommandParameter binding should look similiar to this CommandParameter="{Binding}"
you'd better add your delete image to the app resources and use the pack/component syntax for the Source property, ie like here