I have an establishment collection that I display in a combobox. One of the properties is "IsSelected", which allows me to select multiple items in a combobox.
<ComboBox Name="CmbEtabTout"
ItemsSource="{Binding EtablissementsUtilisateur}"
Grid.IsSharedSizeScope="True"
Grid.Column="2"
Grid.ColumnSpan="3"
Grid.Row="2"
Height="25"
Width="250">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition SharedSizeGroup="AgentA" Width="auto" />
<ColumnDefinition Width="5" />
<ColumnDefinition SharedSizeGroup="AgentB" Width="auto" />
</Grid.ColumnDefinitions>
<CheckBox IsChecked="{Binding IsSelected}" Grid.Column="0"/>
<TextBlock Text="{Binding IdEtablissement}" Grid.Column="1"/>
<TextBlock Text="{Binding Nom}" Grid.Column="3"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ListView x:Name="LVAgent"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding Agents}" Grid.ColumnSpan="2" Margin="150,0,42,0" Grid.Column="2" Grid.Row="4" Grid.RowSpan="5" >
<ListView.View>
<GridView>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}"
Command="{Binding DataContext.SelectAgentCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
CommandParameter="{Binding}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
<CheckBox IsChecked="{Binding SelectAllAgents}"
IsEnabled="True"/>
</GridViewColumn>
<GridViewColumn Header="Matricule"
Width="110"
DisplayMemberBinding="{Binding Matricule}"/>
<GridViewColumn Header="Nom"
Width="120"
DisplayMemberBinding="{Binding Nom}"/>
<GridViewColumn Header="Prénom"
Width="120"
DisplayMemberBinding="{Binding Prenom}"/>
</GridView>
</ListView.View>
</ListView>
My combobox collection :
private ObservableCollection<Etablissement> _EtablissementsUtilisateur;
public ObservableCollection<Etablissement> EtablissementsUtilisateur
{
get
{
return _EtablissementsUtilisateur;
}
set
{
if (value != _EtablissementsUtilisateur)
{
_EtablissementsUtilisateur = value;
RaisePropertyChanged(nameof(EtablissementsUtilisateur));
}
}
}
I'm trying to find out how to bind these comboboxes to refresh a list: if I choose three establishments, the list displays the agents of these three establishments. With a command maybe?
<CheckBox IsChecked="{Binding IsSelected}" Grid.Column="0"/>
Look like this :
Since I already bind my checkbox with "IsSelected" (used for SelectAll), I don't know how to bind to refresh the list of Agent behind, when I check, without having to press a button like "validate".
Edit : My problem now is if I want to do something like this, for exemple :
<CheckBox IsChecked="{Binding IsSelected}" Command="{Binding }" Grid.Column="0" />
I can only bind to the Etablissement Class and not to the ViewModel. (because of the itemSource of combobox i think)
the goal is, "when any checkbox is checked or unchecked, if I choose three establishments, the list displays the agents of these three establishments".
"when any checkbox is checked or unchecked" => property changed event handler
"if I choose three establishments" => if statement
"the list displays the agents of these three establishments" => method call
assuming Etablissement : INotifyPropertyChanged, we could add the event handler to each Etablissement.PropertyChanged. the other option is to add the handler to CheckBox.Checked and CheckBox.Unchecked.
bonus: event handlers can be async, so if "method call" is async, you're right at home to await it, meaning your UI remains responsive and does not lock up.
i would add the handlers inside a Loaded event for your UserControl or perhaps the ComboBox
Loaded += delegate
{
PropertyChangedEventHandler propertyChanged = delegate
{
//if number of checked items != 3
//return;
//update agents
};
foreach (var etablissement in EtablissementsUtilisateur)
etablissement.PropertyChanged += propertyChanged;
}
Related
I have a WPF datagrid that is bound to a Realm backed object. The grid has an autocomplete box that allows the user to enter/select an account number which should then update the source object and the UI. Instead, when a selection is made, the UI box goes back to empty and the stored object is not updated. The Realm documentation states Modifying the collection (e.g. adding or removing items) must happen in a write transaction, so somehow, when the user makes a selection I need to trigger a write transaction on that specific row object. This is further confirmed by the error in the output window
System.Windows.Data Error: 8 : Cannot save value from target back to source. BindingExpression:Path=SAccount; DataItem='TransactionDetails' (HashCode=24381833); target element is 'DataGridRow' (Name=''); target property is 'NoTarget' (type 'Object') RealmInvalidTransactionException:'Realms.Exceptions.RealmInvalidTransactionException: Cannot modify managed objects outside of a write transaction.
I am trying to use an EventTrigger bound to a command in my VM but the trigger never fires. Can someone explain (or maybe even quickly demonstrate) how to make this work?
In my ViewModel I'm retreiving the object like this:
Transaction = realm.All<Transaction>().Where( t => t.ID == tid ).First();
and then binding it in my XAML view
<DataGrid x:Name="TransactionDataGrid"
DataGridCell.Selected="TransactionDataGrid_GotFocus"
AutoGenerateColumns="False"
CanUserSortColumns="True"
CanUserReorderColumns="False"
HorizontalAlignment="Left"
Margin="10,0,0,0"
VerticalAlignment="Top"
Height="556"
Width="1012"
CanUserAddRows="False"
ItemsSource="{Binding Transaction.Rows}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<DataGrid.Columns>
<DataGridTextColumn x:Name="Source" Header="Source Account" Width="Auto" Binding="{Binding Description}" IsReadOnly="True" />
<DataGridTemplateColumn x:Name="AccountNum" Header="Account Number" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<TextBox BorderThickness="0" Text="{Binding SAccount.RawAccountNumber}"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel>
<toolkit:AutoCompleteBox
Background="AliceBlue"
IsTextCompletionEnabled="True"
FilterMode="Contains"
MinimumPrefixLength="2"
ValueMemberPath="RawAccountNumber"
PreviewTextInput="AutoCompleteBox_PreviewTextInput"
SelectedItem="{Binding SAccount, Mode=TwoWay}"
Text="{Binding Path=SAccount.RawAccountNumber}"
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.Accounts}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding UpdateRowCommand}"
CommandParameter="{Binding Path=ID}"/><!-- the command parameter should be the ID of the row we are updating? -->
</i:EventTrigger>
</i:Interaction.Triggers>
<toolkit:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Path=RawAccountNumber}" FontWeight="Bold" Width="100"/>
<TextBlock Text="{Binding Path=Description}" />
</StackPanel>
</DataTemplate>
</toolkit:AutoCompleteBox.ItemTemplate>
</toolkit:AutoCompleteBox>
</StackPanel>
The command in my ViewModel
public ICommand UpdateRowCommand
{
set
{
MessageBox.Show( "Hello UpdateRowCommand" );
}
}
I've a little Problem. On my Window, i've a GridView and on every Row of the GridView is a Button (The Button is Part of the GridViewColumn.CellTemplate). Now i want, that when i press the Button, a Popup should be open under the pressed Button.
But how i can bind the Popup on the Button in a Grid because the Button is dynamic (for every Row one Button).
<Popup Name="popup_Zuordnungen">
<controls:Anlagenzuordnung Grid.Row="3" x:Name="VertragsAnlagenPopup" Margin="0,20,0,0"> </controls:Anlagenzuordnung>
</Popup>
<ListView Grid.Row="1" Name="lv_Leistungserbringer" DataContextChanged="lv_Leistungserbringer_DataContextChanged" SelectionChanged="lv_Leistungserbringer_SelectionChanged" ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:VertragsLeistungserbringerZuordnung}}, Path=DataSource}" Height="150" VerticalAlignment="Bottom">
<ListView.View>
<GridView>
<GridViewColumn Header="ID">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ID}" Width="40" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Bezeichnung">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Beschreibung}" Width="500" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Zuordnungen anzeigen" Name="cmd_Zuordnungen"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The Button is in the last Column.
Cab anywhere help me?
This is a Window with code-behind?
In that case, you can simply add a click event to your button (in the DataTemplate), and handle the popup placement in the event handler:
XAML:
...
<DataTemplate>
<Button Content="Zuordnungen anzeigen" Name="cmd_Zuordnungen"
Click="cmd_Zuordnungen_Click" />
</DataTemplate>
Code:
private void cmd_Zuordnungen_Click(object sender, RoutedEventArgs e)
{
popup_Zuordnungen.IsOpen = false;
popup_Zuordnungen.PlacementTarget = (Button)sender;
popup_Zuordnungen.IsOpen = true;
}
I have a nested Listbox (collection of objects in the main object list) that needs to delete the underlying items.
When the item is removed, I could reset the ItemsSource of the main list, but the main list will have tons of items and each time a item is removed from its underlying collection the main scrollbar would be also reset, making users willing to kill me in a very painful way.
My question: How can i find the container of the item which button has been clicked and how can i find the item itself so i can kill the #&!$*&#$# (cursing onomatopoeia)?
Here's the XAML exemple of my lists:
<ListView Name="mainList">
<ListView.View>
<GridView>
<GridViewColumn Header="Column 1" />
<GridViewColumn Header="Column 2" />
<GridViewColumn Header="Column 3" />
<GridViewColumn Header="Collection column">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ListBox ItemsSource="{Binding BindingCollectionProperty}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Grid.Column="0" Text="{Binding Item.Property}" />
<TextBlock Grid.Column="1" Text="{Binding Item.AnotherProperty}" />
<Button Content="remove" Grid.Column="2" Click="DeleteClickEvent" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
How's my DeleteClickEvent(object sender, RoutedEventArgs e) should be like?
You could have a command and pass it your item as parameter instead of a click handler:
<Button ... Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type YOURCUSTOMCONTROL}}, Path=DataContext.YOURCOMMAND}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" />
If you want to keep your event handler you could use VisualTreeHelper.GetParent twice on the sender.
ListBoxItem item = VisualTreeHelper.GetParent(VisualTreeHelper.GetParent((DependencyObject)sender)) As ListBoxItem;
BindingCollectionProperty.Remove(item);
How do I access a control in XAML that is nested in a GridViewColumn.CellTemplate? By accessing the combo box I want to set its ItemsSource in the code behind.
Code:
<GridViewColumn Width="80">
<GridViewColumnHeader Content="UseCLUT"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=UseCLUT}" Style="{StaticResource GridBlockStyle}"/>
<ComboBox x:Name="combTrueFalse" SelectedItem="{Binding Path=UseCLUT}" Style="{StaticResource GridEditStyle}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
I have named the comboBox as combTrueFalse and tried to referenced it in the code behind but it could not be found.
I find a work around to the problem.
I have set the collection of the combo box to a class which contains the collection for the combox's selection.
The mainWindow class contains the data class's variable.
ItemsSource="{Binding ElementName=mainWindow, Path=data.comboxTFSmall}"
Meaning to say I have set the combox's item towards a Class which contains the collection and not from the code behind.
<GridViewColumn Width="80" >
<GridViewColumnHeader Content="UseCLUT"/>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=UseCLUT, Mode=TwoWay}" Style="{StaticResource GridBlockStyle}"/>
<ComboBox ItemsSource="{Binding ElementName=mainWindow, Path=data.comboxTFSmall}" SelectedValue="{Binding Path=UseCLUT}" Style="{StaticResource GridEditStyle}" />
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Please do correct me if you find that my explanation is misleading thanks.
regards
The following xaml code produces a ListView with three columns. The ListView ItemsSource is an observablecollection. The first column shows the name of the object in a particular row. The second and third columns show buttons associated to the object in a particular row.
<Grid Width="497" Height="260">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="309*" />
<ColumnDefinition Width="188*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="478*" />
<RowDefinition Height="4*" />
</Grid.RowDefinitions>
<GroupBox Header="ObservableCollection Object List" HorizontalAlignment="Left" Width="497" BorderBrush="Black" BorderThickness="2" Grid.ColumnSpan="2">
<ListView ItemsSource="{Binding Path=CollectionList}" Name="QueueListView">
<ListView.Resources>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="PreviewGotKeyboardFocus" Handler="SelectCurrentItem"/>
</Style>
</ListView.Resources>
<ListView.SelectedItem>
<Binding Path="SelectedObjectFromList" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" >
</Binding>
</ListView.SelectedItem>
<ListView.View>
<GridView>
<GridViewColumn Width="140" Header="Object Name" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="160" Header="Property Information">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Get Property Info" Command="{Binding Path=GetObjProperties}"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="180" Header="Transfer">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button Content="Transfer Object" Command="{Binding Path=TransferObjHere}"
DataContext="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType=ListView}}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</GroupBox>
</Grid>
The SelectCurrentItem event handler binds the ListView.SelectedItem to the SelectedObjectFromList property in my view model when the user clicks the "Transfer Object" or "Get Property Info"button. I an use this property to expose the selected object to my view model.
Here is my SelectCurrentItem handler c# code in code behind:
protected void SelectCurrentItem(Object sender, KeyboardFocusChangedEventArgs e)
{
ListViewItem viewItem = (ListViewItem)sender;
viewItem.IsSelected = true;
}
This works great! When the user has clicked a button, the SelectedObjectFromList property is updated from the ListView observablecollection object for that row. (No need to manually click the ListView row to set the property before clicking the button.)
One problem: As I click buttons in the list, the recently selected rows still appear to be selected (they are highlighted in the GUI).
I tried to solve the problem by setting the isFocused property of the ListView:
protected void SelectCurrentItem(Object sender, KeyboardFocusChangedEventArgs e)
{
ListViewItem Item = (ListViewItem)sender;
Item.IsSelected = true;
Item.IsFocused = true;
}
Of course that gives me a StackOverflow exeption :). Does anyone have example code to update the ListView.Selection in the GUI in this case? Thanks in advance.
ListViews allow you to have multiple items selected at once. When you click on a 2nd item, you're not resetting the IsSelected property of the old selected item to False, so it is staying selected
You can try either setting the ListView's SelectionMode to Single
<ListView SelectionMode="Single" ... />
or try getting the old SelectedItem and unselecting it when you're selecting the new one
protected void SelectCurrentItem(Object sender, KeyboardFocusChangedEventArgs e)
{
var temp = myListBox.SelectedItem as ListViewItem;
if (temp != null)
temp.IsSelected = false;
ListViewItem viewItem = (ListViewItem)sender;
viewItem.IsSelected = true;
}