I have the following structure in my UI. I am trying to have a checkbox, which will fire a command and I want to get the row as command parameter.
<telerik:GridViewColumn Header="Rbl">
<telerik:GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl},Mode=FindAncestor}, Path=DataContext.IsRblMappedChangedCommand}"
CommandParameter="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType=telerik:GridViewRow}}"/>
</DataTemplate>
</telerik:GridViewColumn.CellTemplate>
</telerik:GridViewColumn>
This triggers my command in the view model, and I get the data context as command parameter. The issue comes when I set the IsChecked property of the checkbox.
<DataTemplate>
<CheckBox IsChecked="{Binding IsRblMapped}"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl},Mode=FindAncestor}, Path=DataContext.IsRblMappedChangedCommand}"
CommandParameter="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType=telerik:GridViewRow}}"/>
</DataTemplate>
After this I start getting the data context as null. Any idea why this happens and how to fix it?
Related
I have the split button defined as below (in a control template). When the user selects a menu item, I expect the menu to close. I dont know if it's in the SplitButton or Menu that is not closing. It have tried with a ListBox as well, instead of a menu and got the same result.
IsSplitButtonOpen and all the other bound properties are defined in the viewmodel. IsSplitButtonOpen is set to false in the event handler of the command.
<telerik:RadSplitButton Content="New Search"
Height="22"
Visibility="{TemplateBinding AddButtonVisibility}"
AutoOpenDelay="0:0:0.5"
IsOpen="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:RadTabbedWindow}}, Path=DataContext.IsSplitButtonOpen}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:RadTabbedWindow}}, Path=DataContext.ShowSearchCommand}"
CommandParameter="SearchAccountsView">
<telerik:RadSplitButton.DropDownContent>
<telerik:RadMenu Orientation="Vertical"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:RadTabbedWindow}}, Path=DataContext.NewTabListItems}" >
<telerik:RadMenu.ItemTemplate>
<DataTemplate>
<telerik:RadMenuItem Header="{Binding Text}"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:RadTabbedWindow}}, Path=DataContext.ShowSearchCommand}"
CommandParameter="{Binding ViewName}"
/>
</DataTemplate>
</telerik:RadMenu.ItemTemplate>
</telerik:RadMenu>
</telerik:RadSplitButton.DropDownContent>
</telerik:RadSplitButton>
Please, try to use CloseOnPopupMouseLeftButtonUp property and set it to True. Both RadSplitButton and RadDropDownButton have that property. You can even refuse IsOpen property if it has only one purpose.
My problem is that I can't seem to time the selection changed event from the ComboBox edit, to the TextBlock update. Everything displays property but when I'm getting the Grade in the SelectionChanged event in the code behind, it's not updated with the actual value. To get the actual value I have to change the selection on the ComboBox then click off the line of the datagrid I'm editing. Then when I choose a new grade it is updated to show what the last grade was selected. So even though it seems it should all be connected, the row does not actually update until I click off it, as opposed to when I make a different ComboBox selection. So is there any way to change the way a datagrid updates the data to keep in sync with the ComboBox? Any help is appreciated. Thanks.
<DataGridTemplateColumn Header="Grade" CellStyle="{StaticResource StarNumberCellStyle}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Grade}" Margin="3,1,3,1">
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.Grades, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
SelectedItem="{Binding Grade,Mode=TwoWay, NotifyOnTargetUpdated=True}" Tag="{Binding}" ComboBox.IsTextSearchEnabled = "True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DataContext.GradeSelectCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
CommandParameter ="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
See my answer about DataGridTemplateColumn caveats here: DataGrid and Observable Collection in WPF
In short, this should work for you (UpdateSourceTrigger=PropertyChanged):
<ComboBox ...
SelectedItem="{Binding Grade, UpdateSourceTrigger=PropertyChanged,
Mode=TwoWay, NotifyOnTargetUpdated=True}"
... >
I'm trying to add a keybinding onto a wpf datagrid when the user enters a '?' and have tried the following:
<DataGrid.InputBindings>
<KeyBinding Command="{Binding Path=OpenPrompt}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}, Path=CurrentCell}" Key="OemBackslash" Modifiers="Shift"/>
</DataGrid.InputBindings>
i've also tried having the key set to 'Backslash' and 'Divide'.
Does anyone know how to do this?
Acordding to Key Enumeration it's have to be OemQuestion
I tired it and it works:
<DataGrid.InputBindings>
<KeyBinding Command="{Binding Path=MyCommand}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}, Path=CurrentCell}" Key="OemQuestion" Modifiers="Shift"/>
</DataGrid.InputBindings>
Perhaps you have a Binding problem?
I have 3 listboxes all bound two 3 separate observable collections of the same type. My ViewModel has the observable collections exposed via properties. This is for some drag and drop grouping, source list box can have items dragged onto two different lists. But I want to give the user the ability to right click on a listboxitem and set the item's properties. Things like Type, Name, etc. I am using a data template in the since I want all three boxes to be the same in functionality. This works well, and I can get the context menu to pop up when I click on individual items with no problem. My trouble is that I have one propery called FieldType. It is an enum that has 4 potential values. I can't, for the life of me, figure out how to bind the IsChecked property of the MenuItem to that property... functionally anyway. Here is what I have tried....
<DataTemplate x:Key="SFTemplateWithContextMenu">
<TextBlock Text="{Binding Path=FieldName}" ><!--Tag="{Binding DataContext, ElementName=Window}"-->
<TextBlock.ContextMenu>
<ContextMenu >
<ContextMenu.Resources>
<Configurator:EnumToBooleanConverter x:Key="EnumToBooleanConverterc" />
</ContextMenu.Resources>
<MenuItem Header="Rename..." />
<MenuItem Header="Field Type">
<MenuItem.Resources>
<Configurator:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
</MenuItem.Resources>
<MenuItem Header="String" IsCheckable="True" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=DataContext.FieldType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource EnumToBooleanConverterc}, ConverterParameter={x:Static Configurator:TypeDesc.String}, PresentationTraceSources.TraceLevel=High}"/>
<MenuItem Header="Date" IsCheckable="True" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=DataContext.FieldType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Configurator:TypeDesc.Date}}"/>
<MenuItem Header="Barcode" IsCheckable="True" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=DataContext.FieldType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Configurator:TypeDesc.BarCode}}" />
</MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
In the code above you can see on the String, Date, and Barcode MenuItems what I was trying to do (gotta love code that is a work in process). My issue is the exposed property that it should call. I don't know how, in my ViewModel property, to get to the item in the observable collection that corresponds to the item clicked. I have a value converter EnumToBoolean that will sit on the binding to get the checked or not. The problem is the property that is setting/getting that particular item in the observable collection.
Any thoughts? Need more code? Need me to clarify anything? How close am I? By the way, the ViewModel code is written in VB 2010.
Thanks
Bryce
EDIT:
I have tried the following using Angel's suggestion...
<DataTemplate x:Key="SFTemplateWithContextMenu">
<TextBlock x:Name="Field" Text="{Binding Path=FieldName}" >
<TextBlock.ContextMenu PlacementTarget="{Binding ElementName=Field}">
<MenuItem Header="Rename..." />
<MenuItem Header="Field Type">
<MenuItem.Resources>
<Configurator:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
</MenuItem.Resources>
<MenuItem Header="Date" IsCheckable="True" IsChecked="{Binding PlacementTarget.DataContext.FieldType, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Configurator:TypeDesc.String}, PresentationTraceSources.TraceLevel=High}"/>
</MenuItem>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
But this gives me an error that says...Cannot set properties on property elements. Not sure if this is a TextBlox vs TextBox issue? You used TextBox in your example... my guess is that your code would do the same. So I then tried the following...
<DataTemplate x:Key="SFTemplateWithContextMenu">
<TextBlock x:Name="Field" Text="{Binding Path=FieldName}" ><!--Tag="{Binding DataContext, ElementName=Window}"-->
<TextBlock.ContextMenu>
<ContextMenu PlacementTarget="{Binding ElementName=Field}" >
<MenuItem Header="Rename..." />
<MenuItem Header="Field Type">
<MenuItem.Resources>
<Configurator:EnumToBooleanConverter x:Key="EnumToBooleanConverter" />
</MenuItem.Resources>
<MenuItem Header="Date" IsCheckable="True" IsChecked="{Binding PlacementTarget.DataContext.FieldType, RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource EnumToBooleanConverter}, ConverterParameter={x:Static Configurator:TypeDesc.String}, PresentationTraceSources.TraceLevel=High}"/>
</MenuItem>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</DataTemplate>
But this causes binding errors...
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=Field'. BindingExpression:(no path); DataItem=null; target element is 'ContextMenu' (Name=''); target property is 'PlacementTarget' (type 'UIElement')
So it appears that the binding is not working. Any thoughts?
ContextMenu is not part of visual tree. So it wont, by default, connect \ bind to the datacontext of the TextBlock on which it is applied...
So 2 ways to do this...
Set ContextMenu.PlacementTarget and refer that as the Path in individual MenuItem's Binding.
e.g.
<TextBox x:Name="MyTextBlock">
<TextBox.ContextMenu PlacementTarget="{Binding ElementName=MyTextBlock}">
<MenuItem
Header="{Binding PlacementTarget.DataContext.MyHeader,
RelativeSource={RelativeSource
AncestorType={x:Type ContextMenu}}}"
</TextBox.ContextMenu>
</TextBox>
So in the example above... you want to connect menu item with the data context of the text box. So you define PlacementTarget on the ContextMenu. This placement target can only be set with 2 types of bindings... ElementName or StaticResource. And once the context menu is connected to the visual element via PlacementTarget, use the Path in the binding of the meuitem to resolve the data context property i.e. MyHeader.
OR
Use proxy element approach...
Bind datagrid column visibility MVVM
I'm trying to bind a command to a menuitem in WPF. I'm using the same method that's been working for all my other command bindings, but I can't figure out why it doesn't work here.
I'm currently binding my commands like this:
Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.MyCommand}"
This is where it goes wrong (this is inside a UserControl)
<Button Height="40" Margin="0,2,0,0" CommandParameter="{Binding Name}"
Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ConnectCommand}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" CommandParameter="{Binding Name}"
Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.RemoveCommand}"/>
</ContextMenu>
</Button.ContextMenu>
...
The first command binding works like it should, but the second one refuses to do anything.
I've tried changing the ancestor level and naming my Control to access it through ElementName instead of RelativeSource, but still no change. It keeps saying "Cannot find source for binding with reference..."
What am I missing?
(Edit) Since you mentioned this is in an ItemsControl's template, things are different:
1) Get the BindingProxy class from this blog (and read the blog, as this is interesting information): How to bind to data when the DataContext is not inherited.
Basically the elements in the ItemsControl (or ContextMenu) are not part of the visual or logical tree, and therefore cannot find the DataContext of your UserControl. My apologies for not writing more on this here, but the author has done a good job explaining it step by step, so there's no way I could give a complete explanation in just a few lines.
2) Do something like this: (you may have to adapt it a bit to make it work in your control):
a. This will give you access to the UserControl DataContext using a StaticResource:
<UserControl.Resources>
<BindingProxy
x:Key="DataContextProxy"
Data="{Binding}" />
</UserControl.Resources>
b. This uses the DataContextProxy defined in (a):
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" CommandParameter="{Binding Name}"
Command="{Binding Path=Data.RemoveCommand, Source={StaticResource DataContextProxy}}"/>
</ContextMenu>
This has worked for us in things like trees and datagrids.
ContextMenu is in different logical tree, that's why RelativeSource doesnt work. But context menu inherit DataContext from its "container", in this case it is Button. It is enough in common case but in your case you need two "data contexts", of ItemsControl item and of ItemsControl itself.
I think you have no other choice but combine your view models into one, implement custom class to be used as ItemsControl item data context and contain both "Name" and "Remove command" or your item's view model can define RemoveCommand "proxy", that would call parent command internally
EDIT:
I slightly changed Baboon's code, it must work this way:
<Button Height="40" Margin="0,2,0,0" CommandParameter="{Binding Name}"
Tag="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ConnectCommand}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove"
CommandParameter="{Binding Name}"
Command="{Binding Path=PlacementTarget.Tag.DataContext.RemoveCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
</ContextMenu>
</Button.ContextMenu>
koshdim is spot on, it works like a charm!! Thanks Koshdim
I modified his code to fit in my context menu
<DataGrid
AutoGenerateColumns="False"
HeadersVisibility="Column"
Name="dgLosses"
SelectedItem="{Binding SelectedItem, Mode= TwoWay}"
AllowDrop="True"
ItemsSource="{Binding Losses}"
Tag="{Binding DataContext,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=Window}}">
<DataGrid.ContextMenu >
<ContextMenu >
<MenuItem Header="Move to Top " Command="{Binding PlacementTarget.Tag.MoveToTopCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem>
<MenuItem Header="Move to Period 1" Command="{Binding PlacementTarget.Tag.MoveToPeriod1Command,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem>
<MenuItem Header="Move to Period 2" Command="{Binding PlacementTarget.Tag.MoveToPeriod2Command,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem>
<MenuItem Header="Move to Period 3" Command="{Binding PlacementTarget.Tag.MoveToPeriod3Command,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" ></MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
That's a tricky issue, sure marginally you will find a quick workaround, but here is a no-magic-solution:
<Button Height="40" Margin="0,2,0,0" CommandParameter="{Binding Name}"
Tag={Binding}
Command = "{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ConnectCommand}">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove"
CommandParameter="{Binding Path=PlacementTarget.Tag.Name, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"
Command="{Binding Path=PlacementTarget.Tag.RemoveCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}}"/>
</ContextMenu>
</Button.ContextMenu>
...
It boils down to using the Tag of the PlacementTarget (the Button here).