I have the following ItemsControl which handles the display of items added to it:
<ControlTemplate x:Key="MyItemsControlTemplate">
<ItemsControl x:Name="MyItemsControl" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type mynameSpace:MyClass}}, Path=ItemsSource}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<WrapPanel.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:MyCustomClass}">
<Button Command="{Binding}">
<TextBlock Text="{Binding Path=DisplayValue}"/>
</Button>
</HierarchicalDataTemplate>
</WrapPanel.Resources>
</WrapPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</ControlTemplate>
You can see that the Button has a Command="{Binding}" which handles the click event and calls a command.
Now I want to also use a ListView to display the same items and handle the click event. I see from this link WPF: How to bind a command to the ListBoxItem using MVVM? that I have to use Interaction.Triggers so I have did the following:
<ControlTemplate x:Key="MyListViewControlTemplate">
<ListView Name="MyListView" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type mynameSpace:MyClass}}, Path=ItemsSource}">
<ListView.View>
<GridView >
<GridViewColumn Header="Details" DisplayMemberBinding="{Binding Path=DisplayValue}"/>
</GridView>
</ListView.View>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
</ControlTemplate>
The items display properly in the ListView so I know the binding is correct for the text display but I find that the click handler for the ListView does not fire the command like the Button object does.
Any ideas?
How
You should apply the interaction trigger to an element within the ListView. You could use a CellTemplate with a TextBlock that you apply the interaction trigger to:
<ControlTemplate x:Key="MyListViewControlTemplate">
<ListView Name="MyListView" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type mynameSpace:MyClass}}, Path=ItemsSource}">
<ListView.View>
<GridView >
<GridViewColumn Header="Details">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayValue}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<i:InvokeCommandAction Command="{Binding}"></i:InvokeCommandAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</ControlTemplate>
Related
I want to step into next row if I hit a specific key like enter or the arrows on keyboard.I can't find any material on this topic. Could anyone help me out?
I tried with tabnavigation from msdn, but first I want to go on keydown directly to the textbox and second I need to use the arrows and / or the enter key I mentioned earlier.
Thank you!
<ListView AlternationCount="2"
x:Name="lstItems" ItemsSource="{Binding Path=Notes, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Height="300"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.AcceptsReturn="False"
SelectedItem="{Binding Path=Current, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
FontSize="{StaticResource h2FontSize}" Margin="5">
<ListView.View>
<GridView x:Name="gridView" ColumnHeaderContainerStyle="{StaticResource itemsListViewHeader}"
>
<GridViewColumn x:Name="Column1" Header="{x:Static p:Resources.DENOMINATION}" Width="200">
<GridViewColumn.HeaderContainerStyle>
<Style TargetType="GridViewColumnHeader" BasedOn="{StaticResource itemsListViewHeaderLeft}">
</Style>
</GridViewColumn.HeaderContainerStyle>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="BankNoteValue" HorizontalAlignment="Right" Text="{Binding Path=BankNoteValue, StringFormat={}{0:0.##}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="Column2" Header="{x:Static p:Resources.PC}" Width="200">
<GridViewColumn.HeaderContainerStyle>
<Style TargetType="GridViewColumnHeader" BasedOn="{StaticResource itemsListViewHeaderLeft}">
</Style>
</GridViewColumn.HeaderContainerStyle>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Width="{Binding ElementName=Column2, Path=ActualWidth, UpdateSourceTrigger=PropertyChanged}" Name="tbAmount"
Text="{Binding Path=BankNotePiece, StringFormat={}{0:#.###}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" IsEnabled="{Binding isChecked}">
</TextBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
//I am having a Nested datagrid
and i need to fire the event when inner grid row selection changes.
<DataGrid ScrollViewer.CanContentScroll="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
HeadersVisibility="Row"
Height="{Binding ElementName=itemsgrid,Path=ActualHeight}"
Grid.Row="1"
RowDetailsVisibilityMode="Visible"
CanUserAddRows="false"
VerticalAlignment="Top"
AutoGenerateColumns="False"
SelectedItem="{Binding SelectedBasketItem}"
ItemsSource="{Binding SelectedOrderBasketItems}"
CanUserDeleteRows="True">
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid ScrollViewer.CanContentScroll="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
CanUserAddRows="false"
HeadersVisibility="Row"
AutoGenerateColumns="False"
ItemsSource="{Binding SelectedBasketItems}"
SelectedValue="{Binding SelectedBasketItemValue}"
SelectedIndex="{Binding SelectedOrderItemIndex}"
SelectedItem="{Binding SelectedSpecificItemInBasket, Mode=TwoWay}"
DataGrid.SelectionMode="Single"
Style="{StaticResource GridItemsControlStyle}"
ctrls:DragDrop.DropHandler="{Binding DropHandler}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DataContext.DgSelectionChangedCommand,
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"
CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTextColumn IsReadOnly="True"
Binding="{Binding Path=ItemName}"
Width="195">
</DataGridTextColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=RulesCount}"
Background="{Binding Path=RulesCount ,Converter={StaticResource ItemColourConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="115">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=LinkedOrderCount}"
Background="{Binding Path=LinkedOrderCount ,Converter={StaticResource ItemColourConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Width="55">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Label Content="{Binding Path=SDICount}">
<Label.Background>
<MultiBinding Converter="{StaticResource SdiItemColourConverter}">
<Binding ElementName="SDIMaxCnt"
Path="Value" />
<Binding Path="SDICount" />
</MultiBinding>
</Label.Background>
</Label>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
//My Delegate Command initialization in ViewModel
protected override void InitializeCommands()
{
DgSelectionChangedCommand= new DelegateCommand<object>(DGSelectionChanged);
}
//My Method in the view model to be called when selection changed event is fired
void DGSelectionChanged(object obj)
{
//Logic
}
The event gets fired when i use the same event in code behind . i am trying to use interaction trigger to achieve the same in mvvm way .Not sure what i am missing .Any help is greatly appreciated.
Set the AncestorLevel of the RelativeSource to 2:
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DataContext.DgSelectionChangedCommand,
RelativeSource={RelativeSource AncestorType={x:Type DataGrid}, AncestorLevel=2}}"
CommandParameter="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
Autopostback is the mechanism, by which the page will be posted back to the server automatically based on some events in the web controls. In some of the web controls, the property called auto post back.
which if set to true, will send the request to the server when an event happens in the control.
AutoPostBack=True;
please go through the link for more:
Difference between AutoPostBack=True and AutoPostBack=False?
My Application consists of a MainWindow with a ContentControl and I change the ViewModel depending on the selected menu.
One of the UserControls I display as content contains the following WrapPanel:
<UserControl ...>
<Grid>
<WrapPanel>
<ItemsControl ItemsSource="{Binding Connections}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.ConnectionSelectCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
FocusManager.FocusedElement="{Binding ElementName=InstanceName}"
Style="{DynamicResource DashboardButton}">
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Center" Text="{Binding Name}" />
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete"
Command="{Binding ConnectionRemoveCommand}"
CommandParameter="{Binding}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</WrapPanel>
</Grid>
</UserControl>
The Command on the ContextMenu doesn't work because it tries to call ConnectionRemoveCommand on the Connection object instead of the ConnectionViewModel which is the DataContext of the UserControl.
How do I bind the Command to the ConnectionViewModel with the CommandParameter being the Connection object?
If you bind the Tag property of the Button to the DataContext of the ItemsControl, you could then bind to it using the PlacementTarget of the ContextMenu:
<Button Command="{Binding DataContext.ConnectionSelectCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
FocusManager.FocusedElement="{Binding ElementName=InstanceName}"
Style="{DynamicResource DashboardButton}"
Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=ItemsControl}}">
<TextBlock TextWrapping="Wrap" HorizontalAlignment="Center" Text="{Binding Name}" />
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete"
Command="{Binding PlacementTarget.Tag.ConnectionRemoveCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding}" />
</ContextMenu>
</Button.ContextMenu>
</Button>
I have a Window containing 2 DataGrids. And if I click from one specific column in the first DataGrid into any column of the other DataGrid, then I get the error
DeferRefresh is not allowed during an AddNew or EditItem transaction
What is going wrong here?
The first DataGrid is
<DataGrid x:Name="FirstDataGrid"
ItemsSource="{Binding Parts, Mode=TwoWay}"
SelectedItem="{Binding SelectedPart, Mode=TwoWay}"
CellEditEnding="DataGrid_OnCellEditEnding" >
<i:Interaction.Behaviors>
<views:ScrollIntoViewBehavior />
</i:Interaction.Behaviors>
<DataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown"></EventSetter>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding Identifications, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Margin="5,0,0,0">
<Hyperlink NavigateUri="{Binding ArticleNumber, Mode=OneWay}"
Command="{Binding ElementName=PartDataGrid, Path=DataContext.OpenIdentificationCommand}" CommandParameter="{Binding}" >
<TextBlock Text="{Binding ArticleNumber, Mode=OneWay}"/>
</Hyperlink>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
The second DataGrid does not matter since I can click on any column to produce the error.
Solved it by myself.
The column that causes problems is read-only. So why allow an edit mode anyway? That is obviously wrong. I fixed that by adding
IsReadOnly="True"
to the WPF column definition.
Yeah, im not sure what i have done. brain feels like swiss cheese
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Focusable" Value="False"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Command" Value="{Binding CommandButtonClicked}"/>
<Setter Property="CommandParameter" Value="{Binding Mode=OneWay, RelativeSource={RelativeSource Self}}"/>
</Style>
</Window.Resources>
Button that will Fire.
<Grid DockPanel.Dock="Top">
<TextBox BorderBrush="Transparent" BorderThickness="0" x:Name="txtInput" Padding="50,10,15,15" AcceptsReturn="True" TextChanged="txtInput_TextChanged" TextWrapping="Wrap" Foreground="Crimson" Text="{Binding UserInput, Mode=TwoWay}" Background="Transparent"/>
<Button Width="30" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="10,3,0,0">
<Image Source="Images/Actions list add user.ico" />
</Button>
</Grid>
Buttons that will not fire
<ListView x:Name="ListViewUsers" ItemsSource="{Binding UserNames}" DockPanel.Dock="Top" Foreground="Green" BorderBrush="Transparent">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.HeaderTemplate>
<DataTemplate>
<Button Margin="3,0,3,0" Width="30">
<Image Source="Images/Actions user group delete.ico"/>
</Button>
</DataTemplate>
</GridViewColumn.HeaderTemplate>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button>
<Image Source="Images/Actions list remove user.ico" Width="25"/>
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="User ID" DisplayMemberBinding="{Binding}"/>
</GridView>
</ListView.View>
</ListView>
i have also tried adding in a separate DelegateCommand per button, but that doesnt help either. it seems to be anything that inside this ListView and is Generated at runtime after the initial render will not fire.
any ideas ?
In the "cell" DataTemplate it will have its DataContext set to the "item" within the data collection assigned to the ListView for which the "look" is being provided i.e. a "username".
But your style is using a binding that needs to access the object that contains the "CommandButtonClicked" property.
Thus you need a way to get back to the DataContext that is holding your "CommandButtonClicked" property.....so assuming you had set your "ViewModel" onto your Windows DataContext you could do this...
<ListView x:Name="ListViewUsers" ItemsSource="{Binding UserNames}" DockPanel.Dock="Top" Foreground="Green" BorderBrush="Transparent">
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.HeaderTemplate>
<DataTemplate>
<Button Margin="3,0,3,0" Width="30" DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DataContext}">
<Image Source="Images/Actions user group delete.ico"/>
</Button>
</DataTemplate>
</GridViewColumn.HeaderTemplate>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DataContext}">
<Image Source="Images/Actions list remove user.ico" Width="25"/>
</Button>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="User ID" DisplayMemberBinding="{Binding}"/>
</GridView>
</ListView.View>
</ListView>
There are alternative ways to do what you are doing...just one suggestion...look up routed commands (you could define one and then use it in your style e.g. Command="{x:Static mynamespace::MyCommands.DoSomething}" and then set up a CommandBinding to handle them).