How to create ComboBox like UserControl in WPF - wpf

I'm trying to build an usercontrol wich is able to take elements from XAML like this:
<ComboBox >
<ComboBoxItem />
<ComboBoxItem />
<ComboBoxItem />
</ComboBox>
In the ComboBox, you can just add the Items between the ComboBox tags, and I would like to copy this, but I don't know where to start.
Finished it should look like this:
<cis:ReportControl Grid.Row="3">
<cis:ReportItem />
</cis:ReportControl>
In the cis:ReportControl, there are some Buttons and a ComboBox, and basically I only want to feed the ComboBox with Items.
The Report Item is just a ComboBoxItem with some extra properties.
Edit:
I've implemented it according to #Snowbears answer, but the problem now is that the control has itself as an item.
I think this is because I have some content, and by defining the ContentProperty to my ComboBox, it is redirected into the Box.
What can I do to avoid this?
Edit II:
It fully works now with this:
private ItemCollection reportItems;
public ItemCollection ReportItems
{
get
{
if (reportItems == null)
{
reportItems = this.ComboBoxReports.Items;
}
return reportItems;
}
}
with the [ContentProperty("ReportItems")] Attribute. ComboBoxReports is the ComboBox in the Control, and I had to inherit from ItemsControl to get it to work.

You should create property in your UserControl which will expose something implementing IList interface. Let's say this property will be named ReportItems. This property should not have setter and it should be initialized in UserControl itself either in constructor in by field initialization on backing field.
UserControl should be marked with ContentProperty attribute with your property name (ReportItems)
Internal combobox should have it's ItemsSource bound to UserControl's ReportItems property

if you look for How to Create Your own Control, You must look for two things:
Custom Control 1 or User Control 1 (it's depend on your need)
Dependency Properties (use them in control)
I think you might need to use Custom-Control. Also you can inherit your Custom-Control from ComboBox or other Controls.

Related

Binding with parent DataContext

I'm trying to bind combobox editor in a PropertyGrid to a list.
<dxprg:PropertyGridControl SelectedObject="{Binding SelectedEmployee}">
<dxprg:PropertyDefinition Path="EmployeeCountryID">
<dxprg:PropertyDefinition.EditSettings>
<dxe:ComboBoxEditSettings
ItemsSource="{Binding Path=DataContext.Countries, ElementName=rootWindow}"
ValueMember="CountryId" DisplayMember="CountryName" />
</dxprg:PropertyDefinition.EditSettings>
</dxprg:PropertyDefinition>
</dxprg:PropertyGridControl>
This example is from a third-party control but the problem may be just general.
The "rootWindow" DataContext has been set to a ViewModel which holds a property List(of Country) that I want have as ItemsSource in a Combobox.
I was trying to access that list by setting the Combobox ItemsSource to the rootWindow.DataContext.Countries property but I don't get any data.
Tried also all those RelativeSource FindAncestor bindings but no data appeared either.
Why can't I bind through a DataContext of a given element like this?
This became solved. The problem was not with the binding at all but realated to how I defined the third-party control: Instead of EditSettings I should have defined CellTemplate -> DataTemplate.

Binding a combobox in XAML to a childwindow property

I want to display a child window that contains a combobox with several values coming from one of the child window's property:
public partial class MyChildWindow : ChildWindow
{
private ObservableCollection<MyClass> _collectionToBind = // initialize and add items to collection to make sure it s not empty...
public ObservableCollection<MyClass> CollectionToBind
{
get { return _collectionToBind; }
set { _collectionToBind = value; }
}
}
How do I bind in XAML my combobox to the ComboBoxContent collection (both are in the same class)? I've tried several things such as:
<ComboBox x:Name="linkCombo" ItemsSource="{Binding Path=CollectionToBind }" DisplayMemberPath="Description">
I've only been able to bind it in the code behind file and would like to learn the XAML way to do it.
Thank you!
In this case I would use ElementToElement binding like this:-
<ComboBox x:Name="linkCombo" ItemsSource="{Binding Path=Parent.CollectionToBind, ElementName=LayoutRoot }" DisplayMemberPath="Description">
You give the Content element of the ChildWindow the x:Name of LayoutRoot (in the standard template for child window this is done for you). Hence you can bind to this named element and navigate to the containing ChildWindow by using its Parent property.
Using DataContext = this is tempting and works in simple scenarios but things can get awkward in more complex requirements when the DataContext has already been taken in this way.
You need to set the DataContext of the ChildWindow to what contains the values you'd like to bind to. In this case where you're putting the values you want to bind to on the ChildWindow itself so just put a line in the constructor assigned the DataContext to itself.
DataContext = this;
You can also do this using a RelativeSource binding in the XAML, like this:
{Binding Path=CollectionToBind, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}
However, a better way to do this would be to put the CollectionToBind in a separate class and assign it to the Window DataContext. Now both the Window and the XAML Bindings can all refer to the same class as the DataContext and you can isolate more of your logic into this class rather than putting it in the Window implementation.

Wpf SelectedItem wont work for a Combobox in a ListView

I´ve got a problem with a Combobox in a ListView.
I´ve got a class called "Substrate". This class contains an object of a class called "SubstrateType". I want to show the objects of the class "Substrate" in a Listview. Each property of the "Substrate" is presented in the columns of the Listview. For the different "SubstrateType" I want to use a combobox.
In the XAML I tried it like this:
<ComboBox Name="CBType"
ItemsSource="{Binding ElementName=SettingsSubstrate, Path=TypeList}"
SelectedItem="{Binding Path=Substrate.SubstrateType}"
DisplayMemberPath="Description"/>
In the code-behind I got two ObservableCollections. One for all Substrates and one for all possible SubstrateTypes. The combobox displays all SubstrateTypes if you click on it. But the combobox has no selecteditem if you dont select one.
http://i44.tinypic.com/2eakxav.png
Thanks in advance.
I do not know your exact code, but if your ListView rows display objects of type Substrate, then your Binding Path for the SelectedItem should be just SubstrateType because the DataContext of a ListViewItem is already set to the Substrate object:
SelectedItem="{Binding Path=SubstrateType}"
Furthermore, you need to make sure that your SubstrateType instances are actually considered as equal. If the SubstrateType instance in your Substrate object is not exactly the same as the one from the TypeList property, it will not be selected. You can fix that by overriding the Equals(...) method and define your custom comparison for equality.
If this does not work, please provide more code, e.g. the surrounding XAML and the code of Substrate and the code-behind/ViewModel/whatever.

How to make my WPF UserControl work with CompositeCollection

I have a wpf user control which exposes an IEnumerable ItemsSource DependencyProperty. I bind this property to a ListBox control in my UserControl.
I would like to know how I can make my user control work when a CompositeCollection is given. Currently I'm utilising my control like this:
<my:uc>
<my:uc.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{My Binding}"></CollectionContainer>
<CollectionContainer Collection="{My Binding}"></CollectionContainer>
</CompositeCollection>
</my:uc.ItemsSource>
</my:uc>
I would like this to display the contents of these CollectionContainers in the list box, but at the moment its not enumerating through the containers. The only items my listbox shows is two "System.Data.CollectionContainer" items.
[b]Edit:[/b]
The XAML designer informs me that "Property 'ItemsSource' does not support values of type 'CompositeCollection'." I think this is my problem.. but how do I make it "support" CompositeCollection?
I think that what you are getting is the ToString method of your object, which by default returns the name of the type.
Try overriding ToString and returning the value you want to see in your list.
The correct answer was to create a class that extends ItemsControl. You can not use any XAML when designing the control, but it does give you the special ItemsSource property which supports CollectionContainers. The view of the control should be defined in its ContentTemplate property.

Where is IsSynchronizedWithCurrentItem property (or equivalent) for a TreeView?

Tell me it ain't so.
I have a typical windows/file explorer like setup.
Left Side I have a TreeView all data bound showing nodes in a hierachy
Right Side I have a ListView showing Node.Properties
ListView has a IsSynchronizedWithCurrentItem property, which rocks. e.g. If I had another ListView showing a list of nodes and both listViews have this property set to true. Changing selection of node in NodesListView will update the PropertiesListView automatically.
Now I need the same thing with a NodesTreeView and a PropertiesListView... and seems like TreeView has no such property.
Is there a more 'the WPF way' kind of solution to this problem ? Or do I have to handle the NodeSelectionChanged event of the Tree and refresh the listView via code.
A really simple solution is to bind your "details" UI elements to the SelectedValue property of the TreeView. For example, if your TreeView looked like this:
<TreeView Name="CategoryName" ItemsSource="{Binding Source={StaticResource A_Collection}, Path=RootItems}" />
Then you could bind details UI elements (like a textbox) using:
<TextBox Text="{Binding ElementName=CategoryTreeView, Path=SelectedValue.Name}"/>
Would cause the text box to be bound to Name property of the items currently selected in the TreeView.
If you want to bind many UI items as details for the selected TreeView item, consider setting up a DataContext on the elemtent that contains all the details controls (DockPanel / Grid / StackPanel, etc).
<ListView Name="listView1"
ItemsSource="{Binding Path=SelectedItem.Modules,
ElementName=treeView1, Mode=OneWay}"
IsSynchronizedWithCurrentItem="True">
Where ".Modules" is the collection of child items off the selected treeview item you want to display. Don't worry about wiring up the "SelectedItemChanged" event on the treeview.
Why exactly it doesn't implement the property, I do not know, but i have a suggestion down below.
Your code above will work, however, it is not what the IsSynchronizedWithCurrentItem property does. Any ItemsControl binds to the ICollectionView of the ItemsSource property. To get that ICollectionView, we can call CollectionViewSource.GetDefaultCollectionView(object o). Depending on the type of object o, you get a different concrete implementation of the ICollectionView inteface. CollectionView and ListCollectionView are two concrete classes that come to mind.
The ICollectionView interface contains a member called CurrentItem. What the IsSynchronizedWithCurrentItem does is: whenever an item is clicked on the ItemsControl, it sets the CurrentItem for the collection view. The ICollectionView also has two events: CurrentItemChanging and CurrentItemChanged. When the IsSynchronizedWithCurrentItem property is set, the ItemsControl will update the SelectedItem based on what the ICollectionView's CurrentItem is. Makes sense?
In master/detail WPF scenarios, we simply are binding to ICollectionViews and their CurrentItem (the CurrentItem syntax is something like {Binding Items/Name}, where Name is the Name property on the collection's CurrentItem.
So although your code works for your purposes, it doesn't do what that property does. To do what the property does, you need to do the following:
When an item is selected, you need to figure out which collection it belongs to. How do we do this? I believe this is why TreeView doesn't implement it. The selected item is displayed in a TreeViewItem. The DataContext is the object itself, but what is the parent collection ? I guess to get it you could either cache it in some hashmap (silly, but will work) or you can walk up the logical tree and get the TreeViewItem's parent that happens to be an ItemsControl. The ItemsSource property will be your collection.
Get the ICollectionView for that collection.
Need to cast that ICollectionView into a CollectionView (ICollectionView doesn't implement CurrentItem setter)
Call SetCurrent(.. , ..) on the CollectionView instance
Now, anything that is bound to that ICollectionView's CurrentItem will be updated.
This became longer than I expected. Let me know if any further clarification is necesary.
My solution to this turned out to be pretty tiny.. don't know if it is equivalent to IsSynchronizedWithCurrentItem. ListView refreshes as expected.
// the XAML
<TreeView DockPanel.Dock="Left" x:Name="tvwNodes" ItemsSource="{Binding}" SelectedItemChanged="OnNewNodeSelected"/>
<ListView x:Name="lvwProperties" ItemsSource="{Binding Path=Properties}"
// the code-behind
private void OnNewNodeSelected(object sender, RoutedPropertyChangedEventArgs<object> e)
{
lvwProperties.DataContext = tvwNodes.SelectedItem; // this returns the selected Node obj
}

Resources