I have a combobox which displays listview on dropdown, I am following MVVM Pattern and i have also set the public property in my Viewmodel and it works fine when i am assigning it to the Label but for Combobox it doesn't seem to rely on my binding. i tried numerous ways but unable to find the issue.
XAML :
<ComboBox Name="SearchBox" IsEditable="True" Background="White" md:HintAssist.Hint="Search MUID" Grid.Column="1" Margin="5 0 0 0"
Grid.Row="0" Height="40" Width="400" HorizontalContentAlignment="Left" HorizontalAlignment="Left" SelectedItem="{Binding ElementName=lstview ,Path=SelectedItem}" >
<ComboBoxItem>
<ListView x:Name="lstview" ItemsSource="{Binding filterSW}"
SelectedItem="{Binding SelectedMU}"
Height="200" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListView.View>
<GridView>
<GridViewColumn Width="130" Header="Mu-ID" />
<GridViewColumn Width="130" Header="MU-Identifier" DisplayMemberBinding="{Binding MU_Identifier}" />
<GridViewColumn Width="130" Header="Status" DisplayMemberBinding="{Binding RequestType}" />
<GridViewColumn Width="130" Header="UniqueID" />
</GridView>
</ListView.View>
</ListView>
</ComboBoxItem>
</ComboBox>
This works fine for me when i am using the public property and accessing its element , i also tried setting text={Binding SelectedMU.MU_Identifier} and selectedvalue but its just not working.
<Label Grid.Column="3" HorizontalAlignment="Center" Background="GreenYellow" Content="{Binding SelectedMU.MU_Identifier}"></Label>
It looks like you're trying to show a multi-column list in your ComboBox dropdown instead of the standard list where each item shows just a text line.
To achieve this effect you've placed a ListView inside the dropdown.
Unfortunately, this is just not going to work.
Both ComboBox and ListView descend from Selector which is an abstraction that allows to select an item from a list. This limits the property SelectedItem to one of the items that are contained in the list. If you try to assign to this property any value that it not in the list, the assignment is not going to work and the property will retain the value it had before you did the assignment.
Now, the list could either be specified right inside XAML or provided as a binding to property ItemsSource. You do the binding correctly for the ListView. But for the ComboBox you don't specify that binding. Instead you specify exactly one item of type ComboBoxItem which contains the whole ListBox as its value. So the only value that could be successfully assigned to the SelectedItem property of the ComboBox is that single ComboBoxItem. But your binding is never going to assign that value, that's why the ComboBox never shows anything when closed.
When it's open it does show the single item which contain the ListView but this is just an optical effect. The data binging is not going to work. The reason why it works for the Label is because the Label is not constrained and can show anything that the ListView tells it to show.
You can synchronize the ListView and the ComboBox only when both controls have the same bindings for both ItemsSource and SelectedItem properties. But in this case you won't be able to place the ListView inside the dropdown.
The closest you can get to what you want is by customizing the ComboBox's template as described in https://zamjad.wordpress.com/2012/08/15/multi-columns-combo-box, for example. What this won't give you compared to ListView is the column headers. Also, the columns will be evenly spaced inside the dropdown but this is what you have in your ListView anyway.
If you want to auto-size them, you'd need to add Width="Auto" SharedSizeGroup="cN" to each ColumnDefinition where "cN" should have the column number instead of N to make them unique within the Grid and add Grid.IsSharedSizeScope="True" to the <ComboBox >
That's a lot of trouble for something that one would expect to be much simpler, but, unfortunately, you cannot place a ListView inside the ComboBox's template, that's a limitation of how the base class Selector works with its items list.
There are other options if you are open to consider 3rd party control libraries. I worked with Syncfusion, they have SfMultiColumnDropDown which does what you want. I'm pretty sure other popular libraries have similar controls as well.
Related
I'm using Combobox in TableView, and i want to show the first item text as its default value, i tried to add SelectionIndex=0, but it didn't work, its default value is empty. Different with other question like
Comboxbox auto select first item when data is available
my combobox belongs to TableView, each item has a combobox(actually each grid item has many types of combobox), so i can't use some methods need to handle it one by one.
my code is as below, and the itemsource Cities is a Dictionary, and i use DisplayMemberPath="Value" SelectedValuePath="Value" to show its value(not key):
<dxg:GridColumn FieldName="FloorSetUp" Header="Type" MinWidth="20" HorizontalHeaderContentAlignment="Center" VisibleIndex="1">
<dxg:GridColumn.CellTemplate>
<DataTemplate>
<ComboBox DisplayMemberPath="Value" SelectedValuePath="Value" ItemsSource="{Binding Data.Cities}" SelectedIndex="0"/>
</DataTemplate>
</dxg:GridColumn.CellTemplate>
</dxg:GridColumn>
Anyone can solve this problem?thanks very much!
Is FloorSetUp also KeyValuePair<T,R>?
becuse if not, thats your problem.
The ComboBox has to work with data of same types both in items source and in selected item property.
I have a thread that updates the current Mode property every second. I also want an option to be able to set a mode within the same cell in datagrid. So, in not editing mode I just show the mode. In editing mode, I show a populated ComboBox.
Now,
to be able to show the selected item in ComboBox, when I enter the editing mode, I bind the SelectedItem to Mode.
to be able to get the changes back to the ViewModel I bind SelectedValue to a different property. ( I need to bind to a different property since 'Mode' is updated every second and will overwrite the selected value).
The problem is that though SelectedItem is bound with Mode=OneTime it still triggers SelectedValue property. I want the SelectedValue to be triggered only when user select a value.
I hope it's clear what I'm trying to do. So, how I can achieve this? Other approaches are welcome (even if I need to use a different control).
<DataGridTemplateColumn Header="Mode">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Mode}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource Modes}}"
SelectedItem="{Binding Mode, Mode=OneTime}"
SelectedValue="{Binding ModeToSet, Mode=OneWayToSource}"
/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
The premise of what needs to be done is not attainable due to the way the combobox works and how Mode is constantly changing in the background.
Why?
The problem is that though SelectedItem is bound with Mode=OneTime it
still triggers SelectedValue property.
As it should. OneTime as per the docs (BindingMode Enumeration) states:
"Updates the binding target when the application starts or when the data context changes."
The data context as per your design is always changing once a second. If one reads farther into the description it states
"This type of binding is appropriate if you are using data where either a snapshot of the current state is appropriate to use or the data is truly static. ... This is essentially a simpler form of OneWay binding..."
And if one reads up on OneWay bindings
"Updates the binding target (target) property when the binding source (source) changes. This type of binding is appropriate if the control being bound is implicitly read-only."
A combo box is ultimately designed to change both SelectedItem and to get the value off of SelectedItem into SelectedValue
I want the SelectedValue to be triggered only when user select a value.
The problem is not going out of the control, it is what is coming in....
Create a test project and the combobox behaves the way you specify, selected value is only triggered when a user selects a value. Comboboxes only sets the SelectedX properties when a choice is made or an outside value has changed one so it changes the other.
Since Mode is constantly changing it is pushing the change into the selection, not visa versa.
Suggestion To Fix
I suggest you take a snapshot of mode and place that into a ModeOnEdit variable and bind that. When the user makes the selection, capture the event and change the actual Mode.
Test Project
Bind to your own data source, mine was Ships. Ships is a list with and Name as a property on that object. ToString on the ship object returns Name.
Here is the result, there are two textboxes which show the state of the selected item/value. A button to set the selected value and the combobox itself.
<Label Grid.Row="1" Grid.Column="1">SelectedItem</Label>
<TextBlock Grid.Row="1" Grid.Column="2" Text="{Binding SelectedItem, ElementName=cbMain}"/>
<Label Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2">SelectedValue</Label>
<TextBlock Grid.Row="3" Grid.Column="2" Text="{Binding SelectedValue, ElementName=cbMain}"/>
<Button Grid.Row="5" Grid.Column="1" Click="ChangeSelectedValue">Set Selected Value</Button>
<ComboBox Name="cbMain"
Grid.Row="5"
Grid.Column="2"
ItemsSource="{Binding Ships}"
SelectedValuePath="Name"/>
-- Code behind
private void ChangeSelectedValue(object sender, RoutedEventArgs e)
{
cbMain.SelectedValue = "Pacific Silver";
}
I'm working in a C# project [for school], using WPF and implementing MVP. In this code, I've got a DataGrid showing a list of divers. The first column is the Name, and the second column shall show 'DivingType'. DivingType is a built in object, which has a property ID, such as 103A. There are about 400 of these, stored in a list, and each Diver ('row') has a Dives (List<Dive>) Property, and each of these Dives has a divingType property.
What we want to have, is that this column will by default show the DivingType.ID associated with the diver, but that the dropdown list shall contain ALL diving types, such that you shall be able to change it from there [and update the diver object]. To further complicate it, this is one of many views which we add to our window as UserControls.
With that said, here is the code. I've tried to cut out unnecessary clutter which I'm certain has no impact on the result.
<Window ...>
<Window.Resources>
<local:Presenter x:Key="myPresenter"/>
</Window.Resources>
<DockPanel DataContext="{StaticResource myPresenter}">
<UserControl ...>
<DataGrid ItemsSource="{Binding DiverList}" x:Name="datagrid">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Width="1*" Binding="{Binding Name}"/>
<DataGridTemplateColumn Header="Diving type" Width="1*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl},
Path=DataContext.DivingTypes}"
DisplayMemberPath="ID"
SelectedValue="{Binding Dives[0]}"
SelectedValuePath="divingType">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</UserControl>
</DockPanel>
</Window>
When the program runs I get all DivingTypes.ID inside the combobox, but no selected value. The code does not put any related errors into the output window. I believe that what happens is that it calls DivingType.Equals but passing the DataContext for the row (the Diver) instead of the SelectedValuePath which I specify. Any way to override this behaviour inside XAML? Or is there an easier way to achieve this?
EDIT:
I've since edited the code posted above to be:
<ComboBox ItemsSource="{
Binding RelativeSource={
RelativeSource AncestorType=UserControl
},
Path=DataContext.DivingTypes
}"
SelectedValue="{
Binding Dives[0].divingType, Mode=TwoWay
}"
/>
This makes the correct value show in the combobox at the start, DivingType.ID is loaded from the Diver.Dives[0].divingType, but it still does not set the property when I select a new value in the dropdown box.
Use UpdateSourceTrigger=PropertyChanged.
Explanation here.
Have you tried to implemented INotifyPropertyChanged in your viewmodel and then raise the PropertyChanged event when the SelectedValue gets set.
If this is not working, can you set the SelectedValue
SelectedValue="{Binding Path=divingType, Mode=TwoWay}"
I have a ListView which contains a collection of objects as itemssource and the selected object as SelectedItem.
<ListView Margin="5 0 5 0" ItemsSource="{Binding ObjectCollection}" SelectedItem="{
Binding SelectedObject}" Grid.Row="1">
Inside the ListView.View I have several GridViewColumns which each have a CellTemplate
<GridViewColumn CellTemplate="{StaticResource ReferenceToCellTemplate}" Header="{
Binding ColumnName, Converter={StaticResource upperConverter}}" Width="90"
HeaderContainerStyle="{StaticResource StaticGridViewColumnHeaderStyleWhite}"/>
An example of such a template:
<DataTemplate x:Key="ReferenceToCellTemplate">
<ComboBox ItemsSource="{Binding PossibleValuesForProperty, UpdateSourceTrigger=
PropertyChanged}" SelectedItem="{Binding SelectedProperty, UpdateSourceTrigger=
PropertyChanged}" SelectionChanged="Protocol_ComboBox_SelectionChanged"/>
</DataTemplate>
Now for the issue:
Say that I have 2 comboboxes in this listview. for example a combobox with different software and another with the different versions of this software.
Whenever the software has changed in a certain row, the possible versions should be updated.
The question:
How do I know which object the software combobox belongs too so that I can adjust the possible versions for this object?
When you change the value inside the combobox, this doesn't mean that the row is selected. So when I try to adjust the versions along with the selected row, I might as well be adjust the wrong row.
So the way I see it there are 2 possibilities:
Select the given row whenever something inside that row is adjusted/selected
Get to know which row the changed/selected control is in without selecting it
Any help would be much appreciated.
The solution is to not use an event handler for when the property is changed but just to handle the change in the properties for the row object. So when the property for "software" changes, call a method which adjust the "PossibleVersions" property for this software. All of this inside the VM for the row object.
Basic beginner MVVM mistake I guess
Ok... I'm new to WPF, but I kind of know how to do things using DataTriggers and Converters.
But, what I want to seems a little more complex than that. Let me give you the details:
The DataContext for the ListView control is an IList of objects (object=Room). These are the available rooms. I've got another control (let's say it's a TextBox) that it bound to one of the Room objects contained in the IList. I want to display an image only for the room (ListViewItem) that is bound to the other control.
This is some of my XAML:
<TextBox Name="Room" />
<ListView Name="RoomsList" SelectionMode="Single">
<ListView.View>
<GridView>
<GridViewColumn Width="32">
<GridViewColumn.CellTemplate>
<DataTemplate>
<!--
Here's where I want to change the Source property
depending on whether or not the item matches the
TextBox DataContext.
-->
<Image Source="Images/Check.png" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Room Name" Width="150" HeaderContainerStyle="{StaticResource textHeaderStyle}"
DisplayMemberBinding="{Binding Path=RoomName}" />
</GridView>
</ListView.View>
</ListView>
I'm kind of stuck on this one. Any ideas as to how to approach this?
You can write a MultiValueConverter which returns Visibility back and takes DataContext as the first Converter Value and the specific 'Room' object as the second Value(Use ElementName binding with Element as 'Room')
If the Values matches then show the Image control ie, imgControl.Visibility bind to the Converter
Assuming your room object contains the picture or a link to the picture you can use the SelectedIndex property of ListView and bind to the picture field.
<TextBox Name="Room" Text="{Binding ElementName=RoomsList.SelectedItem, Path=Picture}" />
Where Roomlist.Picture is the photo you want to display. If it's a URL you'll probably have to do this is an image tag contained within the textbox. Note that I haven't tested this code so it might need to be tweaked a little to get it to work.