I have a bound ComboBox where I need to show default item "No Selection". ComboBox should close with no text selection and no selected item, When the user selects this option ("No Selection") from list.
These are the data resources
<UserControl.Resources>
<my:iTimeKeepBaseDataSet x:Key="iTimeKeepBaseDataSet" />
<CollectionViewSource x:Key="codeSetsViewSource" Source="{Binding Path=codeSets, Source={StaticResource iTimeKeepBaseDataSet}}" />
<CollectionViewSource x:Key="allMattersViewSource" Source="{Binding Path=allMatters, Source={StaticResource iTimeKeepBaseDataSet}}" />
<my:CodeIdToDetailsConverter x:Key="codeIdDetailsConverter" />
</UserControl.Resources>
This is ComboBox data template
<DataTemplate x:Key="CodeSetDataCellEditTemplate">
<ComboBox DataContext="{StaticResource codeSetsViewSource}"
ItemsSource="{Binding}"
SelectedValuePath="{Binding Path=codeSetId}"
SelectionChanged="OnCodeSetsSelectionChanged"
Style="{StaticResource ComboboxTemplate}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} - {1}">
<Binding Path="codeSetId" />
<Binding Path="codeSetName" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
Template column of DataGrid
<DataGridTemplateColumn x:Name="codeSetId1Column"
Width="SizeToHeader"
CellEditingTemplate="{StaticResource CodeSetDataCellEditTemplate}"
Header="Code Set ID 1"
my:DataGridAttachedProperty.ColumnName="codeSetId1">
Please suggest me a solution for this.
Thanks in advance
You can use CompositeCollection
<CompositeCollection>
<ComboBoxItem IsEnabled="False" Foreground="Black">Select Item</ComboBoxItem>
<CollectionContainer Collection="{Binding Source={StaticResource DataKey}}" />
</CompositeCollection>
But, you can't use Binding here, workaround is to use BindingProxy with CompositeCollection
See my answer here and comments for more details
Related
I can't seem to get my combobox to display the "None" item i inserted into my composite collection once the combobox is loaded. If a user decides to change it afterwards, I don't care, but the initial load should set the "None" to be selected first. Here is my XAML
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox DisplayMemberPath="SName" SelectedItem="{Binding BModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="GId" SelectedValue="{Binding BModel.GId, Mode=TwoWay}"
HorizontalContentAlignment="Stretch"
MinWidth="{Binding ElementName=BAssigned, Path=MinWidth}"
Style="{StaticResource SPanelComboBox}">
<ComboBox.ItemsSource>
<CompositeCollection>
<emodels:SModel SName="None" GId="-1"/>
<CollectionContainer Collection="{Binding DataContext.BListModels,
Source={x:Reference SDataGrid}}"/>
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate >
<DataTemplate>
<ComboBox SelectedItem="{Binding BModel, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalContentAlignment="Stretch"
Style="{StaticResource SPanelComboBox}">
<ComboBox.ItemsSource>
<CompositeCollection>
<emodels:SModel SName="None" GId="-1"/>
<CollectionContainer Collection="{Binding DataContext.BListModels,
Source={x:Reference SDataGrid}}"/>
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Take a look at examples of setting SelectedIndex and SelectedItem in a ComboBox.
<Window.Resources>
<sys:String x:Key="zero">Zero</sys:String>
<spc:StringCollection x:Key="score">
<sys:String>First</sys:String>
<sys:String>Second</sys:String>
<sys:String>Third</sys:String>
</spc:StringCollection>
<CompositeCollection x:Key="strings">
<StaticResource ResourceKey="zero"/>
<CollectionContainer Collection="{Binding Mode=OneWay, Source={StaticResource score}}"/>
</CompositeCollection>
</Window.Resources>
<StackPanel Orientation="Horizontal">
<!--Only index specified-->
<ComboBox Width="100" VerticalAlignment="Top"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource strings}}"
SelectedIndex="0" />
<!--Only SelectedItem set-->
<ComboBox Width="100" VerticalAlignment="Top"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource strings}}"
SelectedItem="First" />
<!--SelectedIndex and SelectedItem set to the same item-->
<ComboBox Width="100" VerticalAlignment="Top"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource strings}}"
SelectedIndex="0"
SelectedItem="Zero" />
<!--SelectedIndex and SelectedItem set to different items-->
<ComboBox Width="100" VerticalAlignment="Top"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource strings}}"
SelectedIndex="0"
SelectedItem="First" />
</StackPanel>
The example shows that SelectedItem has a higher priority than SelectedIndex. And if both properties are set then SelectedItem will be used.
In your code, the BModel property contains an item from the BListModels collection or is null.
And using SelectedIndex = 0 you want to set the SelectedItem to SName = "None".
Since SelectedItem has a different meaning, setting SelectedIndex = 0 will be ignored.
I see two solutions.
The first is when initializing the BModel property to set its value to non-null, but SName = "None".
I cannot show you how to do this.
Since you haven't given enough code, I need to see how you have implemented the BListModels, emodels: SModel collection types, of an element that has a BModel property.
The second variant is to set the SelectedIndex value AFTER the ComboBox is loaded.
I'll show you an variant here using C # code, but you can also implement it using AP property, Behavior, or whatever.
<StackPanel>
<ComboBox Width="100" VerticalAlignment="Top"
ItemsSource="{Binding Mode=OneWay, Source={StaticResource strings}}"
SelectedIndex="0"
SelectedItem="First" Loaded="ComboBox_Loaded" />
<x:Code>
<![CDATA[
private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
((Selector)sender).SelectedIndex = 0;
}
]]>
</x:Code>
</StackPanel>
I have this code:
<ComboBox Name="cbxWorkers" HorizontalContentAlignment="Right"
ItemsSource="{Binding Workers}">
<ComboBoxItem IsSelected="True" Content="Select" />
<ComboBox.ItemTemplate>
<DataTemplate>
<ComboBoxItem Content="{Binding LastName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Everything works fine except the second line.
It gives me a runtime exception:
Items collection must be empty before using ItemsSource.
How can I deal with that so I will get also all the Workers, and also the item - "Select" as the first item of the combobox?
Thanks a lot :)
You can't have two sources. You'd have to specify in code from the ItemsSource which item you want selected.
<ComboBox Name="cbxWorkers" HorizontalContentAlignment="Right" ItemsSource="{Binding Workers}">
<ComboBox.ItemTemplate>
<DataTemplate>
<ComboBoxItem Content="{Binding LastName}" IsSelected="{Binding isSelected}" />
</DataTemplate>
</ComboBox.ItemTemplate>
You can do this, or you can just make an extra first Item in C#/VB and make sure it's selected.
You can do this with a CompositeCollection:
<ComboBox x:Name="cbxWorkers" SelectedIndex="0">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=LastName}" />
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem Content="Select" />
<CollectionContainer Collection="{Binding Workers}" />
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
Note: you need to set the SelectedIndex="0" on the ComboBox, because when the ComboBoxItem is in the ItemsSource, its IsSelected property won't set the selection on the ComboBox.
Edit concerning CollectionContainer
As #H.B. pointed out, the Binding on CollectionContainer won't work this way. You have a couple of options. They are spelled out for you by this CodeProject article, so I won't repeat them here. The one method that doesn't mention is the newish (as of .NET 4) x:Reference option. It would be used like this:
<CollectionContainer Collection="{Binding DataContext.Workers, Source={x:Reference cbxWorkers}}" />
I have a XmlDataProvider, a ListBox and a DataGrid.
The underlying xml file has this kind of structure:
<Root>
<Person name="test">
<item name="bla" value="test"/>
<item name="bla" value="test2"/>
</Person>
<Root>
The ListBox lists all persons, while the DataGrid lists all items, corresponding to the selected Person. This works as intended.
Now i want to group the data in the DataGrid, but having looked at examples i still don't get how to do it with an XmlDataProvider (how/where to create a ListCollectionView off of the XmlDataProvider).
Could someone please give me a quick xaml example for doing this by e.g grouping the items by name?:)
Thanks for any help in advance :)
regards
UPDATE:
Now the grouping works, but when i add something to the xml, it is not shown instantly anymore (in listbox or datagrid).What is wrong? I am really new to wpf, so there might be things redundant or unnecessary, i got no problems with you pointing them out :)
Here is the relevant code that is used:
<Grid.DataContext>
<XmlDataProvider x:Name="XmlData" Source="entries.xml" XPath="Root/Person" />
</Grid.DataContext>
<ListBox Name="PersonListBox"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource listBoxTemplate}"
IsSynchronizedWithCurrentItem="True"
Visibility="Visible" SelectionMode="Single" SelectedIndex="-1" DataContext="{Binding}">
</ListBox>
<DataGrid IsSynchronizedWithCurrentItem="True" Name="itemGrid"
DataContext="{Binding ElementName=PersonListBox, Path=SelectedItem}"
CanUserAddRows="true"
IsReadOnly="true"
AutoGenerateColumns="False">
<DataGrid.Resources>
<CollectionViewSource x:Key="items" Source="{Binding XPath=item}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="#name"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</DataGrid.Resources>
<DataGrid.ItemsSource>
<Binding Source="{StaticResource items}"/>
</DataGrid.ItemsSource>
<DataGrid.Columns>
<DataGridTextColumn Width="*" Header="Name" Binding="{Binding XPath=#name}"/>
<DataGridTextColumn Header="Wert" Binding="{Binding XPath=#value}"/>
</DataGrid.Columns>
<DataGrid.GroupStyle>
<GroupStyle />
</DataGrid.GroupStyle>
</DataGrid>
Here is an example, should be quite self-explanatory but if something is not clear feel free to ask:
<DataGrid>
<DataGrid.Resources>
<CollectionViewSource x:Key="items" Source="{Binding SelectedItem, ElementName=lb}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="#name"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</DataGrid.Resources>
<DataGrid.ItemsSource>
<Binding Source="{StaticResource items}"/>
</DataGrid.ItemsSource>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding XPath=#value}"/>
</DataGrid.Columns>
<DataGrid.GroupStyle>
<GroupStyle />
</DataGrid.GroupStyle>
</DataGrid>
(You can also set IsSynchronizedWithCurrentItem to true on the ListBox and then bind the Source via the current item instead (i.e. {Binding /, Source={StaticResource data}})
Based on a selected string value in the combobox I want to either show red/blue datatempalte inside the grid.
Can this be done without a ContentControl?
<UserControl.Resources >
<DataTemplate x:Key="red">
<TextBox Text="red" />
</DataTemplate>
<DataTemplate x:Key="blue">
<TextBox Text="blue" />
</DataTemplate>
</UserControl.Resources>
<ComboBox ??? />
<Grid>
// Show red or blue datatemplate here
</Grid>
Why not use a ContentControl?
To make this work simply i would put the templates in an array, which the ComboBox then can bind to:
<x:Array x:Key="templates" Type="{x:Type DataTemplate}">
<DataTemplate>
<DataTemplate.Resources>
<sys:String x:Key="DisplayString">Red</sys:String>
</DataTemplate.Resources>
<TextBox Text="red" />
</DataTemplate>
<DataTemplate>
<DataTemplate.Resources>
<sys:String x:Key="DisplayString">Blue</sys:String>
</DataTemplate.Resources>
<TextBox Text="blue" />
</DataTemplate>
</x:Array>
<ComboBox Name="combo" ItemsSource="{StaticResource templates}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Resources[DisplayString]}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Grid>
<ContentControl ContentTemplate="{Binding SelectedItem, ElementName=combo}" />
</Grid>
I want to use the DataGridevents (DataGridBeginningEdit, DataGridCellEditEnding, ..etc) to handle and detect changes. As far as I understood, without a "CellTemplate" these are not triggered. So I am trying to create an appropriate celltemplate using a TextBlock, but I guess it is not very straightforward with the binding I am using for the Combobox in the CellEditingTemplate, because I am using "DisplayMemberPath"..
There are examples of simpler cases but I couldn't find smth for this scenario. See Xaml snippet below;
<data:DataGridTemplateColumn Width="100">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock HorizontalAlignment="Center" />
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
<data:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox HorizontalAlignment="Stretch"
ItemsSource="{Binding DurationTypeList, Source={StaticResource itemSourceProvider}}"
SelectedValuePath="Code"
SelectedValue="{Binding Path=DurationTypeCode, Mode=TwoWay}"
DisplayMemberPath="Template" />
</DataTemplate>
</data:DataGridTemplateColumn.CellEditingTemplate>
</data:DataGridTemplateColumn>
Thank you
It turns out, i have two options..
Solution #1
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock HorizontalAlignment="Left" Text="{Binding Path=DurationType.Template, Mode=OneWay}" />
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
<data:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox HorizontalAlignment="Stretch"
ItemsSource="{Binding DurationTypeList, Source={StaticResource itemSourceProvider}}"
SelectedValuePath="Code"
SelectedValue="{Binding Path=DurationType, Mode=TwoWay}"
DisplayMemberPath="Template" />
</DataTemplate>
</data:DataGridTemplateColumn.CellEditingTemplate>
I changed the binding path from string to the object with Code and Template properties..
This blog helped a lot..