First of all, one of the requirements of binding
is the referenced property to be public.
link
The properties you use for the binding properties for a binding must be public properties of your class. Explicitly defined interface properties can not be accessed for binding purposes, nor can protected, private, internal, or virtual properties that have in base implementation.
In this case we must correct the itens
property, thus:
public ObservableCollection<string> itens;
About CompositeCollection
The problem is related to the fact that the class CompositeCollection is not derived from a FrameworkElement
and therefore it does not have the DataContext
property to support DataBinding
. As we can see in Console
System.Windows.Data Error: 2: Can not find governing FrameworkElement or FrameworkContentElement for target element.
It does not make sense for this class to have this kind of problem. Researching it looks like it is an old bug and has discussions about it in 2008 ( as we can see here ).
(I did tests on .NET FRAMEWORK 4.7.1 and the bug still exists.)
One of the solutions is to use a kind of proxy ( quoted in this blog in 2011 )
Solutions:
1 - Practical solution (from what you already have)
Use CollectionViewSource
(proxy xaml) to fetch the data:
<ComboBox SelectedIndex="0" >
<ComboBox.Resources>
<CollectionViewSource x:Key="itens" Source="{Binding itens}"/>
</ComboBox.Resources>
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem IsEnabled="False" Foreground="Gray" Content="selecione..."/>
<CollectionContainer Collection="{Binding Source={StaticResource itens}}" />
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
2 - More complicated solution (ideal for better quality)
I've created a template for ComboBox
while retaining the default style (you can customize this to the level you want).
<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="MainGrid" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="Shdw" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=MainGrid}">
<Border x:Name="DropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=DropDownBorder}" Height="{Binding ActualHeight, ElementName=DropDownBorder}" Width="{Binding ActualWidth, ElementName=DropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
<ToggleButton BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxReadonlyToggleButton}"/>
<ContentPresenter ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
<TextBlock x:Name="ExibeSelecione" HorizontalAlignment="Left" Margin="5,0,0,0" VerticalAlignment="Center" Visibility="Hidden" IsEnabled="True" Foreground="Gray" Text="Selecione..."/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="Shdw" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="Shdw" Value="#71000000"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="DropDownBorder" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
<Setter Property="Background" Value="#FFF4F4F4"/>
</Trigger>
<Trigger Property="SelectedItem" Value="{x:Null}">
<Setter Property="Visibility" Value="Visible" TargetName="ExibeSelecione"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
This TextBlock
is displayed whenever no value is selected
<TextBlock x:Name="ExibeSelecione" HorizontalAlignment="Left" Margin="5,0,0,0" VerticalAlignment="Center" Visibility="Hidden" IsEnabled="True" Foreground="Gray" Text="Selecione..."/>
This Trigger
is responsible for identifying when there is no value selected and displaying TextBlock
<Trigger Property="SelectedItem" Value="{x:Null}">
<Setter Property="Visibility" Value="Visible" TargetName="ExibeSelecione"/>
</Trigger>
- Edit -
@LeandroLuk The use of Collection="{Binding Source={StaticResource itens}}
is unrelated to data refresh. If you are going to set a new value for itens
property like you are doing here
itens = new ObservableCollection<string>();
Then you need to use the INotifyPropertyChanged
interface to update the data in the view. Staying like this:
private ObservableCollection<string> _itens;
public ObservableCollection<string> itens { get { return _itens; } set { _itens = value; NotifyPropertyChanged(); } }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private async void carregaItens(object sender, RoutedEventArgs e)
{
itens = new ObservableCollection<string>();
var result = await new WebService().getItens();
result.ForEach(x => itens.Add(x));
}
The rest is with you ... I hope this already helps xD