CompositeCollection doesn't propagate changes in underlying collections - c#

I have what I thought was an ideal scenario for a CompositeCollection, except it seems that changes to the items of the underlying collection do not appear in the CompositeCollection (or, at any rate, not in the control whose source is this CompositeCollection).
EDIT 1: both underlying collections are ObservableCollections.
EDIT 2: the new/updated item gets added, but the contents of that item are not reflected in the drop-down area of the combobox. Each item implements INotifyPropertyChanged.
Am I doing something wrong or is this not supported?
Here's what I have:
<ComboBox SelectedItem="{Binding Products}">
<ComboBox.Resources>
<CollectionViewSource x:Key="CustomProductsSource" Source="{Binding CustomProducts}" />
</ComboBox.Resources>
<ComboBox.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={x:Static local:Products.Standard}}" />
<Separator/>
<CollectionContainer Collection="{Binding Source={StaticResource CustomProductsSource}}"/>
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>

What type of collections are Products.Standard and CustomProducts? If they do not have change notification (do not implement INotifyCollectionChanged), then CompositeCollection has no means to know that something changed.
ObservableCollection<> for example should work just fine in this scenario.

I fixed it - the problem was (as it is most often is) an idiot programmer (that would be me).
It so happened that Item of the collection (individual Product class) had an overriden ToString() method returned the Name property of the Product instance. So, the combo box was showing what it was supposed to show, which is the name of the product, except it was doing it based on the ToString() method... BECAUSE I FORGOT TO CREATE A DATATEMPLATE for the ComboBox. No wonder then that changes to the Name were not reflected

Related

WPF - Create ComBox with multiple separators

I want to make a ComboBox with 0-N Separators
I was able to do this with CompositeCollection like so
<ComboBox.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection ={Binding mysrc1}/>
<ComboBoxItem IsEnabled = "False">
<Separator Background = "White" Height=10/>
</ComboBoxItem>
<CollectionContainer Collection ={Binding mysrc2}/>
<ComboBoxItem IsEnabled = "False">
<Separator Background = "White" Height=10/>
</ComboBoxItem>
</CompositeCollection>
</ComboBox.ItemsSource>
But this is kind of clunky and with a lot of code duplication but in .View and in .ViewModel
I was thinking about using custom control that will Inherit from ComboBox and use DependencyProperty.RegisterAttached for the location of the Separator(s)
but couldn't find a way to do so
P.S
i prefer not use any code behind and not use <Setter Property="Template"> because i have base style and template that a lot of ComboBoxs in my program user
As you are using databinding, the items are grouped, so I would using grouping.
See this answer: Grouping items in a ComboBox (solved)
If you do not want to use grouping, then you could use this method: Insert ComboBox Item Separator Which is Filled Through Data Binding

WPF change selected value in one ComboBox by another ComboBox in MVVM

I am having a problem. I have two combobox in WPF first:
<ComboBox commands:PropertyChangeBehavior.Command="{Binding GetCurrentsModuleCommand}" SelectedIndex="0">
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem IsEnabled="True">All</ComboBoxItem>
<CollectionContainer Collection="{Binding Source={StaticResource ResourceKey=AxmModulesCombo}}" />
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
</StackPanel>
Lets call it Source and it is binded to command:
public ICommand GetCurrentsModuleCommand => new Command(module =>
{
SelectedAxmModule = module.ToString();
Stuff(module);
});
And this combo box has no SelectItem property (well it don't needs any as parameters are only pass to method).
And a Target CombxBox
<ComboBox ItemsSource="{Binding AxmModules}"
SelectedItem="{Binding SelectedAxmModule, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedValuePath="{Binding SelectedAxmModules}"
IsSynchronizedWithCurrentItem="True"/>
Witch SelectedItem property is binded to:
private string selectedAxmModule;
public string SelectedAxmModule
{
get => selectedAxmModule;
set
{
selectedAxmModule = value;
OnPropertyChanged();
}
}
Now those combo boxes are similar in values, now I want to do if I click value on source as you see in source command i want to select same value from source (I handled values from source and not in target in code so that is irrelevant).
I tried adding Update property and SelectedValuePath but no good value in target combo box is still empty after selecting value from source, and OnPropertyChange works as inteded. Is there a way to achieve this?
If AxmModules is an IEnumerable<byte>, SelectedAxmModule should be a byte property. You cannot set a string property to a byte value. The types must match.
And SelectedValuePath is superfluous when using SelectedItem.
Since you have
IsSynchronizedWithCurrentItem="True"/>
Then the selecteditem should be the current. You should work with the current item of the collectionview rather than binding selecteditem.
You can read more, probably more than you want to read, about collectionview and this sort of stuff here:
https://social.technet.microsoft.com/wiki/contents/articles/26673.wpf-collectionview-tips.aspx
Also.
See this
SelectedValuePath="{Binding SelectedAxmModules}"
The selectedvaluepath is a string not a binding. It should be the name of whichever field you want to use.
If, for whatever reason, you want to bind to the current item in a collection then you can use the / notation.
https://social.technet.microsoft.com/wiki/contents/articles/29859.wpf-tips-bind-to-current-item-of-collection.aspx
I'm not sure how this is going to fit with a composite collection though. I prefer a separate control to indicate selection of "all" and hence wouldn't use a composite collection in this way. You might want to consider that approach yourself.

Wpf ComboboxEdit from binding global list

<dxg:GridColumn.EditTemplate>
<ControlTemplate>
<dxe:ComboBoxEdit
HorizontalContentAlignment="Left"
ItemsSource="{Binding HizmetSaglayiciList}"
SelectedItem="{Binding Hiz_Sag_Id, Mode=TwoWay}"
ValueMember="Hiz_Sag_Id"
IsTextEditable="False"
AllowNullInput="False"
AutoComplete="False"
ImmediatePopup="False"
EditMode="InplaceActive"/>
</ControlTemplate>
</dxg:GridColumn.EditTemplate>
I have global list called HizmetSaglayiciList, but
The combobox does not open when I press the edit button.
I am writing missing any place.
I think this is one of those cases in which the DataContext is not accessible as certain elements (in this case dxg:GridColumn) are not part of visual or logical tree. A solution may be using a Freezable class. Check this Link.
DataContext for the ComboBoxEdit is not the same as for the GridControl, that's why ItemSource binding fails. Assuming your GridControl has a name (let's say it's x:Name="gridTest"), you can simply do the following:
ItemsSource="{Binding DataContext.HizmetSaglayiciList, ElementName=gridTest}"
Actually, you can bind ItemSource to any named element's DataContext.

Binding a List within a bound ObservableCollection

In my WPF application I have an Observablecollection "CollOfPersons" of Persons, where each Person Object has a property "NotesOnPerson" of type
List<Notes>
(among other properties). Now I bind "CollOfPersons" to a listbox lb in code via
lb.ItemsSource = CollOfPersons;
Now I have set up a template how to display a person, namely I wrap each person in a 'Expander' and display the basic properties (e.g., Name, Age) in Expander.header, and this works fine, e.g.,
<Expander.Header>
<StackPanel Orientation="Horizontal">
...
<TextBlock Text="{Binding Path=Name}"/>
...
</StackPanel>
</Expander.Header>
However, now I'd like to bind the NotesOnPerson list of notes to the Expander.Content. But since this is again a list of varying size I don't know how to do it. Same strategy as above does not work, because I don't know the name of the Expander (as I knew the name 'lb' of the big listbox in which all the stuff is). Something like
<Expander.Content>
<ListBox ItemTemplate="{StaticResource NoteTemplate}"
ItemsSource="{Binding Path=NotesOnPerson}"/>
</Expander.Content>
doesn't seem to work. I seem to be confused about code and XAML binding. How should I solve this?
Is this what you're looking for?
<Expander.Content>
<ListBox ItemTemplate="{StaticResource NoteTemplate}"
ItemsSource="{Binding NotesOnPerson}"/>
</Expander.Content>
I'm not familiar with the Expander, but since NotesOnPerson is (presumably) a property of Person and not of Name, that's the syntax you should use. (the Path= is optional, since just putting it in like that is another way to declare the Path)

How to dynamically generate MenuItems using x:Reference?

I have a MenuItem whose ItemsSource is set to the following CompositeCollection:
<CompositeCollection>
<MenuItem x:Name="SpinnerMenuItem" Header="Waiting..."/>
<CollectionContainer
Collection="{Binding DataContext.Source,
Source={x:Reference SpinnerMenuItem},
Converter={StaticResource NoOpConverter}}"/>
</CompositeCollection>
The breakpoint inside my NoOpConverter is telling me that my collection is successfully getting bound to the CollectionContainer. The problem is, the menu is showing up completely empty! All I get is a popup about 3 pixels high and 10 pixels wide.
Why are my menu items not being displayed? Even the "SpinnerMenuItem" disappears once the bound list is populated. I was not having this issue in the simpler case, when I was just binding to a CollectionViewSource static resource.
This appears to be an issue with CompositeCollection. The workaround is to use a StaticResource instead of a Binding or similar. More info here: Why is CompositeCollection not Freezable?

Categories

Resources