Dynamic stack of ComboBoxes trying to add new items - c#

I have an Items control bound to a observable collection of TaskActivity objects.
<ItemsControl ItemsSource="{Binding TasksActivities, UpdateSourceTrigger=PropertyChanged}" Margin="20, 0, 20, 20">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="2, 0, 2, 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width= "*" />
<ColumnDefinition Width= "*" />
<ColumnDefinition Width= "*" />
<ColumnDefinition Width= "70"/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="test" IsEditable="True" ItemsSource="{Binding Source={StaticResource Locator}, Path=Main.AvailableActivities, Mode=TwoWay}" SelectedValue="{Binding ActivityId}" Text="{Binding Name, UpdateSourceTrigger=LostFocus}" SelectedValuePath="Key" DisplayMemberPath="Value" HorizontalAlignment="Stretch" Grid.Column="0">
</ComboBox>
<TextBox Text="{Binding Length}" Grid.Column="1" />
<TextBox Text="{Binding Comment}" Grid.Column="2" />
<Button Height="24" Content="Remove" HorizontalAlignment="Right" Margin="10, 0, 10, 0" Style="{StaticResource LinkButton}" Grid.Column="3">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand Command="{Binding Source={StaticResource Locator}, Path=Main.DeleteActivityCommand, Mode=OneWay}" CommandParameter="{Binding Name}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
When a value is typed in the combobox that is doesn't exist and focus is lost, I want to have a prompt appear asking if they want to add add that value to the AvailableActivities list (which is just a Dictionary). Right now the border just goes red, and while it does update the "Name" property of the object inside the OC, it's not real because it can't set the ActivityId since it doesn't actually exist in the list of AvailableActivities.
I've tried an EventToCommand for SelectionChanged and LostFocus, but when a new value is entered, the value I'm returned is 'null' so I can't add it.
Normally I could just bind the text value to a property on the VM and just do it all there, but since it's a property inside a ObservableCollection of TaskActivity objects I'm not sure that's possible.
Any suggestions for achieving this functionality?

First make the ComboBox.Text a two-way binding so that the view model property gets updated:
Text="{Binding Path=Name, UpdateSourceTrigger=LostFocus, Mode=TwoWay}"
With that in place, add a command to the ComboBox's LostFocus event:
<ComboBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<cmd:EventToCommand Command="{Binding Source={StaticResource Locator},
Path=Main.AddNewActivityCommand}"
CommandParameter="{Binding ElementName=test,Path=Text}"
/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
And finally, the "AddNewActivityCommand" should just add the new item (in the "Name" property) to the "AvailableActivities" collection.

Related

How to define a Command as a StaticResource WPF

I am trying to bind a to a command defined in the DataContext of the xaml in a DataGridColumn, however I cannot use RelativeSource as columns are not part of the hierarchy, so my current solution is so define the command in the ResourceDictionary and reference it.
However my problem is I can't seem to find how to define an ICommand in the ResourceDictionary, how can I do this? Or any other ways to access my command in DataContext from a DataGridColumn ?
xmlns:input="clr-namespace:System.Windows.Input;assembly=System"
...
<UserControl.Resources>
<ResourceDictionary>
<input:ICommand x:Key="propertyChangedEvent">
"{Binding PropertyChangedEvent}"
</input:ICommand>
</ResourceDictionary>
</UserControl.Resources>
...
<DataGridTemplateColumn Header="Notes" MinWidth="350" Width="*" IsReadOnly="False">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Margin="1">
<TextBox Text="{Binding Notes, UpdateSourceTrigger=LostFocus}" BorderThickness="0" AcceptsReturn="True" TextWrapping="Wrap" >
<i:Interaction.Triggers >
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{StaticResource propertyChangedEvent}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Try to use Element Binding by providing element name whose has the DatacContext.
<i:InvokeCommandAction Command="{Binding ElementName=Window1, Path=DataContext.CommandPropertyName}" />

TextBoxes not synchronized after introducing CollectionViewSource

I'm having trouble with some TextBoxes which are to be synchronized with the current element selected in a ListBox. E.g. I have the following TextBoxes:
<TextBox Style="{StaticResource IdTextboxStyle}" Grid.Row="2" Grid.Column="1" Text="{Binding Path=ID, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Style="{StaticResource TextboxStyle}" Grid.Row="3" Grid.Column="1" Text="{Binding Path=CodeName, UpdateSourceTrigger=PropertyChanged}" />
I also have the following ListBox:
<ListBox Name="ListOfAgentsBox" Margin="20, 5, 5, 5" Grid.Row="2" Grid.Column="2" Grid.RowSpan="4"
IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Source={StaticResource AgentsViewSource}}"
SelectedIndex="{Binding Path=CurrentIndex, UpdateSourceTrigger=PropertyChanged}">
Which contains the current elements in a collection named Agents, which is a collection of Agent elements.
My problem is, before introducing the following CollectionViewSource:
<CollectionViewSource x:Key="AgentsViewSource"
Source="{StaticResource Agents}">
<CollectionViewSource.SortDescriptions>
<componentModel:SortDescription PropertyName="ID" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
Everything seemed to work fine - I could click the different elements in the ListBox and edit them just fine in the TextBoxes. However, the TextBoxes and the ListBox are no longer syncrhonized, and I have no idea why.

how to bind a command from DataContext of Page to a list item

I have a Page and a viewmodel is set as its datacontext. in that page I have a list. which is populating through a property in the viewmodel. List has a user control. and that user control has a button. I want that button to be bind with a command that is in viewmodel. Is there anyway to do that?
<Page DataContext=PageViewModel>
...
<ScrollViewer Grid.Row="3" Margin="20,0" Visibility="{Binding ByVenueSelected, Converter={StaticResource BooleanToVisibilityConverter}}">
<StackPanel>
<ItemsControl ItemsSource="{Binding EventsListByVenue}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<myControls:EventDetails /> <!--in this control i want to bind a command available in PageViewModel-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
...
</Page>
with help of #FunksMaName, I solved this. I am sure there are more elegant and better approach, but yet this is a quick & easy solution for me:
<Button Style="{StaticResource NoHighlightButtonStyle}" Tag="{Binding link}" CommandParameter="{Binding Path=Tag,RelativeSource={RelativeSource Mode=Self}}" Visibility="{Binding link,Converter={StaticResource DataAvailabilityToVisibilityConverter}}" Command="{Binding Path=Event.LinkCommand,Source={StaticResource Locator}}" >
<TextBlock Margin="0,5" Foreground="SkyBlue" Text="{Binding link}" TextWrapping="Wrap" FontSize="16"/>
</Button>
things to note:
i think xaml searched the command parameter in context of command only, so it was giving me null parameter, while the same binding was working fine for textblock inside Button. So i tricked it to store the value in tag, and used it from there.
Tag="{Binding link}" CommandParameter="{Binding Path=Tag,RelativeSource={RelativeSource Mode=Self}}"

WPF ListBox selection

I have a problem with extended selection in ListBox. Let's say I have a ListBox with 10 items and I'm selecting first 5 of them using Shift button. The SelectionChanged event is fired with 5 items. After that I want to select 3 items from those 5, again with Shift button pressed, but SelectionChanged event is not fired. How can I react to the second selection of items, when I'm selecting 3 of those 5 previously selected ones?
can you show the xaml code as well I would like to see what your binding looks like
it should looks something like this.. but I can't be certain unless I see your code
In Command binding you have used binding which has relative source binding...
consider making these changes in binding
1) using list box as Ancestortype
2) While binding use Path=DataContext.SelectionChangedCommand otherwise it will take list box as datacontext.
<catel:EventToCommand Command="{Binding Path=DataContext.SelectionChangedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" DisableAssociatedObjectOnCannotExecute="False" PassEventArgsToCommand="True" />
here is an example of what the XAML would look like for ListBoxItem Template
<Grid>
<StackPanel Orientation="Horizontal">
<Label Width="180">Field1</Label>
<ListBox Height="200"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding List1, Mode=OneWay}"
Name="listBox1"
SelectionMode="Single"
Width="300">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Width="290">
<TextBlock Width="90" Text="{Binding}"></TextBlock>
<ComboBox Width="180" ItemsSource="{Binding DataContext.List2, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" DisplayMemberPath="Field1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<catel:EventToCommand Command="{Binding Path=DataContext.SelectionChangedCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" DisableAssociatedObjectOnCannotExecute="False" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>

DependencyProperty Binding Does Not Update To Collection CurrentItem

I am trying to bind a dependency property to a collection's current selection and for reasons I can't seem to grasp, the binding does not update when the collection changes.
In the example below, I show two example. One is updating correctly (on the textblock/run), and the other only displays the initial element and doesn't change when the data grid selection changes.
<Grid>
<Grid.Resources>
<CollectionViewSource Source="{Binding Path=List}" x:Key="myViewModel"/>
<my:UpdateNotWorking MyObjModel="{Binding Source={StaticResource myViewModel}, Path=CurrentItem}" x:Key="updateNotWorking" />
</Grid.Resources>
<DataGrid ItemsSource="{Binding Source={StaticResource myViewModel}}" Name="mylistbox"/>
<TextBlock TextWrapping="Wrap" FontWeight="Bold" Foreground="#FF50CEFF" FontSize="24" TextAlignment="Center" Height="75">
<Run Text="{Binding Source={StaticResource myViewModel}, Path=text}" Foreground="#FF00E200" />
</TextBlock>
<TextBox Text="{Binding Source={StaticResource updateNotWorking}, Path=MyObjModel.text}" Height="22"/>
</Grid>
My dependency property in this example is "MyObjModel" on the "UpdateNotWorking" dependency object which is instantiated from the xaml code.
I would appreciate any information as to why my property is not updating correctly.
Example Project
Paste this XAML into your MainWindow.
<Grid>
<Grid.Resources>
<CollectionViewSource Source="{Binding Path=List}" x:Key="myViewModel" />
<my:UpdateNotWorking x:Key="updateNotWorking" />
</Grid.Resources>
<DataGrid ItemsSource="{Binding Source={StaticResource myViewModel}}" Name="mylistbox"
SelectedItem="{Binding Source={StaticResource updateNotWorking}, Path=MyObjModel, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock TextWrapping="Wrap" FontWeight="Bold" Foreground="#FF50CEFF" FontSize="24" TextAlignment="Center"
Height="75">
<Run Text="{Binding Source={StaticResource myViewModel}, Path=text}" Foreground="#FF00E200" />
</TextBlock>
<TextBox Text="{Binding Source={StaticResource updateNotWorking}, Path=MyObjModel.text, UpdateSourceTrigger=PropertyChanged}"
Height="22" />
</Grid>
What it is now doing is setting updateNotWorking's MyObjModel property based on the DataGrid's SelectedValue, with UpdatePropertyTrigger set to PropertyChanged to see the changes immediately. No longer do we need to define updateNotWorking's property through the List's CurrentItem because it is not going to change just by selecting it with a DataGrid. You can keep it set, but it is not required as we are doing all the manual labor with the DataGrid SelectedValue.

Categories