WPF Combobox binding - wpf

I got two Comboboxes and both of them have binding with the same Source.
<ComboBox ItemsSource="{Binding Source={StaticResource UsersViewSource}}"
And when I change something in the first one, it reflects also to the second one. And I dunno how to keep their SelectedItem values separately, using the same ItemsSource.

The IsSynchronizedWithCurrentItem property should be set to False:
true if the SelectedItem is always
synchronized with the current item in
the ItemCollection; false if the
SelectedItem is never synchronized
with the current item; null if the
SelectedItem is synchronized with the
current item only if the Selector uses
a CollectionView. The default value is
null.
Here's a sample:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<x:Array x:Key="myStrings" Type="sys:String">
<sys:String>one</sys:String>
<sys:String>two</sys:String>
<sys:String>three</sys:String>
<sys:String>four</sys:String>
<sys:String>five</sys:String>
</x:Array>
</Page.Resources>
<StackPanel Width="200">
<ComboBox IsSynchronizedWithCurrentItem="False" Margin="25"
ItemsSource="{Binding Source={StaticResource myStrings}}" />
<ComboBox IsSynchronizedWithCurrentItem="False" Margin="25"
ItemsSource="{Binding Source={StaticResource myStrings}}" />
</StackPanel>
</Page>

You just need to set the IsSynchronizedWithCurrentItem property to false (by default it's null)

I'd guess (from the name of your binding) that the reason this is happening is that you're binding to a CollectionViewSource (that wraps a collection). This class is a proxy that WPF uses that includes (amongst other things) the selected item of a collection. Obviously if you're sharing this collection between two comboboxes, you're also sharing the selected item.
If you set ItemsSource to something that's not a CollectionViewSource, the control will automatically wrap it in one. So, my suggestion would be to bind directly to a collection instead of wrapping in a CollectionViewSource - or, alternatively, create two CollectionViewSource instances, one for each ComboBox.

You can separately bind the SelectedItem property for each combo box separately.
i.e.
SelectedItem={Binding SelectedItem1}
This way when each one's item gets set, it gets stored into a different place.

Related

WPF Binding of a Control inside a UserControl

I have a ComboBox inside a UserControl. I use the UserControl in a Window with a DataGrid: the UserControl DataContext is the DataGrid SelectedItem. The ComboBox inside the UserControl is bound to the "ID" field of the SelectedItem through the SelectedValue Property. To achieve that I programmed a DependencyProperty in the UserControl like this
Public Shared SelectedValueProperty As DependencyProperty = _
DependencyProperty.Register("SelectedValue", GetType(Object), GetType(ucEditCombo))
Public Property SelectedValue() As Object
Get
Return CType(GetValue(SelectedValueProperty), Object)
End Get
Set(ByVal value As Object)
SetCurrentValue(SelectedValueProperty, value)
End Set
End Property
and bound the ComboBox SelectedValue Property to the UserControl SelectedValue Property in XAML:
<ComboBox SelectedValuePath="{Binding ElementName=EditCombo,Path=SelectedValuePath}"
DisplayMemberPath="{Binding ElementName=EditCombo,Path=DisplayMemberPath}"
ItemsSource="{Binding ElementName=EditCombo,Path=ItemsSource}"
SelectedValue="{Binding ElementName=EditCombo,Path=SelectedValue,Mode=TwoWay}"
Visibility="{Binding ElementName=EditCombo,Path=ComboVisibility}"
Name="cmb"/>
(EditCombo is the x:Name I give to the UserControl in XAML).
Running the application I not the following:
If I change the DataGrid selection, then the ComboBox displayed value changes correctly.
As soon as I change the ComboBox selected value directly on the control, the value in the DataGrid doesn't get updated AND the ComboBox isn't bound to the DataGrid SelectedItem any longer.
The problem regarding the update of the original DataGrid SelectedItem is not apparent, although I believe it is to do with the bindings on the ComboBox. I would need to see the Window xaml and the UserControl's code behind to be sure.
I attempted to reproduce the problem with an example, but I was unable to. So to speed up your movement forward, I have posted the example below.
Assumption: The UserControl is attempting to modify properties of the object bound to the SelectedValue DependencyProperty, not attempting to replace the object. If the assumption is incorrect, update the question with the extra code that is relevant to the problem and I will attempt to update my answer.
Firstly, I have a model that I use when creating code for StackOverflow questions called ItemModel.
Public Class ItemModel
Public Property Id as Guid
Public Property Text as String
End Class
and on the Window, I have created an ObservableCollection(Of ItemModel) property called Items. It contains 3 items with each item having its Text property set to one of the values "One", "Two" or "Three". The Id of each item is set to a new guid.
So, my main window xaml looks like
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:StackOverflow._20798974"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom">
<this:ucEditCombo SelectedValue="{Binding ElementName=MyGrid, Path=SelectedItem}" />
</StackPanel>
<DataGrid x:Name="MyGrid" ItemsSource="{Binding Path=Items}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Id}"/>
<DataGridTextColumn Header="Text" Binding="{Binding Text}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</Window>
The code behind of the ucEditCombo UserControl contains the DependencyProperty you have defined in the question above and an ObservableCollection(Of String) which is populated with the values "One", "Two", "Three" and "Four".
The xaml for the user control is
<UserControl x:Class="ucEditCombo" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:this="clr-namespace:StackOverflow._20798974"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type this:ucEditCombo}}}">
<ComboBox ItemsSource="{Binding Path=Items}" SelectedValue="{Binding Path=SelectedValue.Text}" />
</Grid>
</UserControl>
The above example allows me to selected a DataGrid item and have the Text property of that item immediately appear in the ComboBox. It also allows the selection of a different value in the ComboBox and have that value updated in the DataGrid.
Points to note.
Setting the DataContext of the Control in the Controls constructor will break the SelectedItem binding on the Window. To get around this, I have set the DataContext on the first content element of the UserControl (a Grid element).
The DataContext is set to the UserControl itself, simplifying some of the bindings on the Combobox.
As we are modifying a property of a DependencyProperty, a lot of the WPF binding subsystem notifications are taken care of for us.
The default binding mode for SelectedValue and SelectedItem is TwoWay.
I hope this helps.

WPF Databinding between two usercontrols

I'll try to give a bit more details of this question
In a WPF application, on the left side of 'mainwindow' I have a UserControl containing a listBox, like this
<UserControl.Resources>
<ObjectDataProvider x:Key="viewModel" ObjectType="{x:Type vm:TemplateListViewModel}"/>
</UserControl.Resources>
<StackPanel>
<ListBox Height="Auto" Name="TemplateList" ItemsSource="{Binding Source={StaticResource viewModel}, Path=TemplateNames}"
</StackPanel>
As shown, listbox items are fetched from a xml file via viewModel.
Now I have another usercontrol2 containing a Label to echo the selection from "TemplateList".
Also under this label I have another usercontrol3 containing a datagrid, which data will be fetched from a xml file based on the label, or the selection from the listbox on the left side of the window through usercontrol3's viewmodel.
So the question is how to pass the "SelectedItem" or "SelectedIndex" to viewModel of the Label and datagrid?
I know it works when I bind a label to a listbox with 'elementName' and Path to 'SelectedItem'. Now I couldn't figure out how to do with usercontrols and 'ObjectDataProvider'. I tried as suggested below couldn't get it to work out. So far for usercontrol2, if I use the same ObjectDataProvider as the above, I can get this label to work as
<Label Name="TemplateNameLabel" Content="{Binding Source={StaticResource viewModel}, Path=TemplateNames[0]}" />
Where 'TemplateNames' if of type 'XmlNodeList' because it was read from xml file. But I really want is something like
<Label Name="TemplateNameLabel" Content="{Binding Source={StaticResource viewModel}, Path=TemplateNames[SelectedIndex]}" />
Can this be done without any command, just like binding to 'elementname' without usercontrol involved?
Thanks.
I've removed not important properties to highlight the main:
<ListBox Name="TemplateList" ItemsSource="{Binding Source={StaticResource viewModel}, Path=TemplateNames}" SelectedItem="{Binding Path=SelectedTemplate, Mode=OneWayToSource}" />
Note, that you will need SelectedTemplate property with public setter of type TemplateName - the same type that is used for single element in the TemplateNames collection in the ViewModel. In that setter you could pass the value to whatever other view-models you need.

XAML Control array for static number of combo boxes

I would like an array of 10 combo boxes on a wpf form.
The ItemsSource of the combo boxes are identical - an ObservableCollection of selectable Items.
Each Selected Item will bound to an item in a different ObservableCollection, imaginatively called 'SelectedItems'..
What is the best way to do the array? I could of course have 10 separate combo boxes but this would be not very elegant..
I don't think an ItemsControl template is what I'm after as the number of combo boxes is static.
Thanks
Joe
If I understand you right, you have 10 ComboBoxes with the same item list, but different data sources
In that case, I could create a common style for the ComboBox which sets the common properties such as ItemsSource (and SelectedItem if the binding is the same for all items), and then actually create the individual ComboBoxes on the form as needed.
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type ComboBox}">
<!-- Set the binding to wherever your ItemsSource resides. In this
case,I'm binding to a static class called Lists and a static
property called ComboBoxItems -->
<Setter Property="ItemsSource"
Value="{Binding Source={x:Static local:Lists.ComboBoxItems}}" />
<!-- Only use this setter if your binding is the same everywhere -->
<Setter Property="SelectedItem" Value="{Binding SelectedItem}" />
</Style>
</StackPanel.Resources>
<ComboBox DataContext="{Binding Item1}" />
<ComboBox DataContext="{Binding Item2}" />
<ComboBox DataContext="{Binding Item3}" />
<ComboBox DataContext="{Binding Item4}" />
<ComboBox DataContext="{Binding Item5}" />
<ComboBox DataContext="{Binding Item6}" />
<ComboBox DataContext="{Binding Item7}" />
<ComboBox DataContext="{Binding Item8}" />
<ComboBox DataContext="{Binding Item9}" />
<ComboBox DataContext="{Binding Item10}" />
</StackPanel>
Of course, if the DataSource for your ComboBoxes CAN be put in a collection, it is preferred that they are and that you use an ItemsControl to display the ComboBoxes
<ItemsControl ItemsSource="{Binding SelectedItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding }"
ItemsSource="{Binding Source={x:Static local:Lists.ComboBoxItems}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Each Selected Item will bound to an item in a different ObservableCollection, imaginatively called 'SelectedItems'..
Given that you're effectively binding to a collection, I would do an ItemsControl template, and just treat it that way. Unless you want to customize the layout (ie: these won't be arranged together in the View), that will simplify the design, even if the number of items is always "static".
If you want to have the items arranged separately on the View, then just having 10 combo boxes may be more appropriate.
Personally I think an ItemsControl which has an ItemTemplate which constructs each ComboBox is the way to go! Are you always going to have exactly 10 of these?
From an MVVM perspective, I can imagine a parent View Model which has a collection of selection view models. Each selection view model will have the list of items that can be selected, and the currently selected item. This view model will easily bind to your view.
Not knowing why you need this... which would probably be important in deciding how to do this... here is my stab at it.
Why not design the UI, give each ComboBox a name, create a List and Add each into that List at runtime?

How to bind to CurrentItem of ICollectionView

I want to a property to the current item of a ICollectionView how can I do it? The ICollectionView is used for binding to a combo box, how can I bind another control to the ICollectionView's selected item?
Check out this cheat sheet. In particular, check out the / binding symbol, which references the current item in a collection view.
Setting IsSynchronizedWithCurrentItem on the ComboBox will update the current item with its selection (not sure if you're already doing this). You can then bind the same collection and access its current item with the binding:
<ComboBox ItemsSource="{Binding Names}" IsSynchronizedWithCurrentItem="True" />
<Button Content="{Binding Path=Names/}"/>
Give your ComboBox a name and bind to it's SelectedItem.
For example:
<ComboBox x:Name="MyComboBox" ItemsSource="{Binding MyList}" />
<Grid DataContext={Binding ElementName=MyComboBox, Path=SelectedItem>
...
</Grid>

Display default selection in silverlight comboxbox

I have a silverlight combobox inside of a dataform as follows:
<dataControls:DataForm x:Name="newScheduleMasterForm" Height="350" Width="450" MinWidth="400"
VerticalAlignment="Top"
CommandButtonsVisibility="None"
Header="Add New Master Schedule"
HorizontalAlignment="Left" AutoGenerateFields="False" ContentLoaded="newScheduleMasterForm_ContentLoaded" >
<dataControls:DataForm.EditTemplate>
<DataTemplate>
<StackPanel>
<dataControls:DataField>
<ComboBox x:Name="cbScheduleType" SelectedItem="{Binding Schedule, Mode=TwoWay}" SelectedIndex = "0"
ItemsSource="{Binding GetScheduleTypeValues, Source={StaticResource validDataSource}}"
/>
</dataControls:DataField>
</StackPanel>
</DataTemplate>
</dataControls:DataForm.EditTemplate>
</dataControls:DataForm>
The combobox cbScheduleType ItemsSource has values of "Interior" and Exterior. I am unable to display the default selected value "Interior" in the text box of the combobox. Is there a way to do it.
Thanks in advance
Mohit
The problem is that you're trying to both set SelectedIndex and bind Selected Item. I would just set Schedule to Interior in your constructor in codebehind and remove the SelectedIndex attribute.
There are some problems with data binding the SelectedItem property in the default Silverlight ComboBox control.
One way to work around this by creating a custom control that inherits from ComboBox and adds a SelectedValue dependency property.
There's a good example of this on Rockford Lhotka's blog at:
http://www.lhotka.net/weblog/SilverlightComboBoxControlAndDataBinding.aspx

Resources