Select ListView item when combobox/textbox is selected WPF MVVM - c#

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

Related

Problem with multiple ListBox binding same SelectedItem property

I have a screen in WPF that needs to list data from same database table, but separately by status. In the print each column is a different status and each is a ListBox, the SelectedItem binding for all ListBox is the same property in ViewModel. The problem is when an item is selected in some ListBox it still selected even after I select an item from other ListBox and when the focus return to the first ListBox (with Tab key for example) the item gets the selected style. Any idea how to resolve this? I can change the ListBoxes for other solution too, at this moment i can't see other solution.
Sorry for my bad English, and I don't know if the explanation is much clear this feature is a bit complicated to explain, but any doubts I'll provide the answers.
Thanks for all.
do you want to have the same item selected in each listbox? Try adding IsSynchronizedWithCurrentItem="true" to every listbox in xaml:
<StackPanel>
<!--both comboboxes are synchronized with each other-->
<ComboBox ItemsSource="{Binding Products}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>
<ComboBox ItemsSource="{Binding Products}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>
</StackPanel>

How to bind dynamically generated TextBoxes to DataGrid values in WPF?

My problem is basically described in the title of this topic.
I have a datagrid which is dynamically filled with various entries:
<DataGrid
x:Name="searchResult"
AutoGenerateColumns="True"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=SearchResult}" />
Next to it is a bunch of TextBoxes, also dynamically generated:
<ItemsControl
x:Name="searchForm"
ItemTemplate="{StaticResource formInputTest}"
ItemsSource="{Binding SearchForm, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
The template I use in the ItemsControl looks basically like this:
<TextBox
Tag="{Binding Index}"
Text="{Binding ElementName=searchResult, Path=SelectedValue.a, UpdateSourceTrigger=PropertyChanged}" />
Now lets asume my datagrid has 2 columns (a and b) and I have 2 TextBoxes with identical names (a and b).
Now I want to select a datagridrow and like the two text boxes to be automatically filled with the values from the corresponding row.
Unfortunately, I don't know how to get this dynamically Binding.
If I bind the TextBox's Text-property statically to searchResult->SelectedValue.a it works fine, but as you can see, only static.
Thats what I've done in the third code-box above.
The ItemsControl, which contains the TextBoxes, has it's binding to "SearchForm". SearchForm has the property "Index", which is the name of the Column.
So, the TextBox knows the name of the column it has to be binded to. To clarify this, I binded the TextBox.Tag to Index, which works fine.
Basically, I want my TextBox-Binding to look like this:
<TextBox
Text="{Binding ElementName=searchResult, Path=SelectedValue.{Binding Index}, UpdateSourceTrigger=PropertyChanged}" />
But, as you might know, a binding inside a binding does not work.
Does anybody know how to bind these to dynamically generated objects together?
Thanks for any hint!
UPDATE 2019/03/07
Thanks dymanoid!
This has got me some progress but I'm still not at the finish line.
I created the view-model property and binded both the Datagrid-SelectedItem and the TextBox-Text property to it.
Since the SelectedItem is a kind of a dictionary (BindablePublicDictionary) I use a converter (IMultiValueConverter) at the TextboxBinding, to convert the dictionary entry to both the index of the textbox and the text/value.
This is working fine, I select a datagrid-row and the 3 dynamically generated inputfields are beeing filled.
Unfortunatelly, if I edit one of these filled textboxes, the other 2 are emptied and my datagrid is unchanged.
I implemented OnPropertyChanged, my bindings are TwoWay and the ConvertBackFunction is called and returns the data in compliance with the targetType Type from the function itself.
Does anybody have experience with the bindings/converter between a datagrid/dictionary and textboxes, especially regarding the convertBack function?

combobox and listview binding issue wpf c#

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.

How to bind different properties to SelectedItem and SelectedIValue of ComboBox?

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";
}

WPF Combobox issue

I have a combo box in a wpf c# application. In the xaml i am trying to do the following.
The ItemsSource comes from one variable.
SelectedItem sets a value on another variable
But i want the text displayed to come from a new variable.
How do i stop making the selected itemssource appear as the main text?
<ComboBox x:Name="ComboPlay" FontSize="14" MinHeight="20" Margin="0,2,4,4" Grid.Row="1" Grid.Column="3" MinWidth="160"
ItemsSource="{Binding ComboBoxList}"
SelectedItem="{Binding OutputChannel.Value, Converter={StaticResource ResourceKey=ValueToStringConverter}}" Grid.ColumnSpan="1"
IsEnabled="{Binding IsDriveChoiceEnabled}"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
If you mean changing the display of the item currently selected (the portion of the control shown when the dropdown is closed), take a look at Can I use a different Template for the selected item in a WPF ComboBox than for the items in the dropdown part?
Honestly, a quick search dug up that one and many similar ones. Probably the simplest way, like the linked answer, is to figure out if your item is wrapped in a ComboBoxItem and display it differently then. Or you could re-template the ComboBox. Or you could derive from it (and re-template it) and provide a separate dependency property for the template of the selected item, if you expect to reuse it in different contexts. The sky's the limit.

Categories

Resources