First of all, one of the demands of binding
is the property referenced to be public.
https://docs.microsoft.com/en-us/dotnet/framework/wpf/data/binding-sources-overview
The properties you use as Binding source properties for a Binding must be public properties of your class. Explicitly defined interface properties cannot be accessed for Binding purposes, nor can protected, private, Internal, or virtual properties that have no base implementation.
Then we must correct the property itens
being like this:
public ObservableCollection<string> itens;
About the CompositeCollection
The problem is related to the class fact Compositecollection not be derived from a FrameworkElement
and so she doesn’t have the property DataContext
to support DataBinding
. As we can see in the Console
System.Windows.Data Error: 2 : Cannot find Governing Frameworkelement or Frameworkcontentelement for target element.
It doesn’t make sense that this class has this kind of problem. Searching, it seems that it is an old bug and has discussions about it in 2008 (as we can see here).
(I ran tests on . NET FRAMEWORK 4.7.1 and the bug still exists.)
One of the solutions is to use a kind of proxy (cited in this blog in 2011)
Solutions:
1 - Practical solution (starting from what you already have)
Use the CollectionViewSource
(proxy xaml) to fetch 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 created a template for ComboBox
keeping 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>
That one TextBlock
is displayed every time 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..."/>
That one Trigger
is responsible for identifying when there is no value selected and display the 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 the data update. If you set a new value for property itens
like you’re doing here
itens = new ObservableCollection<string>();
Then it is necessary to use the interface INotifyPropertyChanged
to update the data in the view. So:
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 up to you... I hope it already helps xD
What’s wrong with using a Staticresource to bind? I’m not sure but I think it will be the only way.
– ramaral
No problem, but the point is that I need to use multiple instances of content and I see no logic in working with N-Staticresource’s... After all if it is possible to add a Compositecollecion and join a standard combo, there should be a way to update only this Collectioncontainer
– LeandroLuk
"but the point is that I need to use multiple instances of content and I see no logic in working with N-Staticresource’s... " - Put an example in the question.
– ramaral
The my real problem is that my Combobox content is not static but dynamic, and in addition it is Reactive, so if there is an update on my webservice, it should change the Combobox options when the user is re-selecting.
– LeandroLuk
You are making this code in the Canvas class (code Behind) or are using MVVM?
– ramaral