WPF Combobox DisplayMemberPath - wpf

Ok, I looked at other questions and didn't seem to get my answer so hopefully someone here can.
Very simple question why does the DisplayMemberPath property not bind to the item?
<ComboBox Grid.Row="1" Grid.Column="2" ItemsSource="{Binding PromptList}" DisplayMemberPath="{Binding Name}" SelectedItem="{Binding Prompt}"/>
The trace output shows that it is trying to bind to the class holding the IEnumerable not the actual item in the IEnumerable. I'm confused as to a simple way to fill a combobox without adding a bunch a lines in xaml.
It simply calls the ToString() for the object in itemssource. I have a work around which is this:
<ComboBox Grid.Row="1" Grid.Column="2" ItemsSource="{Binding PromptList}" SelectedItem="{Binding Prompt}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
But in my opinion it's too much for such a simple task. Can I use a relativesource binding?

DisplayMemberPath specifies the path to the display string property for each item. In your case, you'd set it to "Name", not "{Binding Name}".

You are not binding to the data in the class, you are telling it to get it's data from the class member that is named by the member "name" so, if your instance has item.Name == "steve" it is trying to get the data from item.steve.
For this to work, you should remove the binding from the MemberPath. Change it to MemberPath = "Name" this tells it to get the data from the member "Name". That way it will call item.Name, not item.steve.

You should change the MemberPath="{Binding Name}" to MemberPath="Name". Then it will work.

You could remove DisplayMemberPath and then set the path in the TextBlock.
The DisplayMemberPath is really for when you have no ItemTemplate.
Or you could remove your ItemTemplate and use DisplayMemberPath - in which case it basically creates a TextBlock for you.
Not recomended you do both.
<TextBlock text="{Binding Path=Name, Mode=OneWay}"

Alternatively you don't need to set the DisplayMemberPath. you can just include an override ToString() in your object that is in your PromptList. like this:
class Prompt {
public string Name = "";
public string Value = "";
public override string ToString() {
return Name;
}
}
The ToString() will automatically be called and display the Name parameter from your class. this works for ComboBoxes, ListBoxes, etc.

Trying this :
<ComboBox Grid.Row="1" Grid.Column="2" ItemsSource="{Binding PromptList}" SelectedItem="{Binding Prompt}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Content}"/>
</DataTemplate>
</ComboBox.ItemTemplate>

from what i can figure,
"DisplayMemberPath" uses reflection to get the property name in the data context class, if it cant find it nothing will be displayed.
if class
class some_class{
string xxx{ get; }
}
DisplayMemberPath=xxx, will show whatever value "xxx" is
if you want to concatenate properties from the datacontext you need to create an item template, which will show up in the header and the drop down list.
<ComboBox.ItemTemplate>
<DataTemplate DataType="employee">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding first_name}" />
<TextBlock Text="" />
<TextBlock Text="{Binding last_name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
you cannot have "DisplayMemberPath" and "ComboBox.ItemTemplate" set at the same time.

Related

WPF ComboBox ItemTemplate binding to a string collection

I have a combobox in wpf which is bound to a List<string>. All works well, but now for some reason I need to bind to an item template. The XAML for the combo box is
<ComboBox ItemsSource="{Binding Tracks}" SelectedItem="{Binding SelectedTrack}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding **WhatShouldBeHere**}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
If my data source is a custom collection then binding is easy, I should just pass the property name from custom collection, but as the binding source is a list of string, what should the binding property be?.
It should be
<TextBlock Text="{Binding}"/>
which is equivalent to
<TextBlock Text="{Binding Path=.}"/>
See the Remarks section on the Binding.Path MSDN page for further details.

Xaml - combo box - why does selectedValue NOT work with multiple columns?

Hi and thanks for your help.
The following xaml works just fine:
<ComboBox Name="cboCit_type"
IsSynchronizedWithCurrentItem="True"
mvvm:View.FlowsWithPrevious="True"
ItemsSource="{Binding Path=cuCodeInfo.CitTypes}"
SelectedValuePath="code"
DisplayMemberPath="code"
Text="{Binding cit_type}"
IsEditable="true"
IsReadOnly="false"
SelectedValue="{Binding Path=cit_type}">
</ComboBox>
cuCodeInfo.CitTypes is simply a list of items that are available. There are a number of public properties, but the 2 in question are "code" and "description".
Right now, I show the available code values and the user selects one. If one was already selected, then it shows when the page is displayed. This is all good.
So then I thought it might be nice to show both the code and the description. I figured it shouldn't be too hard...
So I removed the DisplayMemberPath statement and added in an ItemTemplate.
When I did so everything looked great until I tried to select an item from the list. When I did so, instead of showing the selected code, I would get an empty string. I have searched the internet trying to find the one thing I need to add to the DataTemplate to fix this, but everything I have tried has failed. Here is the code that is NOT working:
<ComboBox Name="cboCit_type"
IsSynchronizedWithCurrentItem="True"
mvvm:View.FlowsWithPrevious="True"
ItemsSource="{Binding Path=cuCodeInfo.CitationTypes}"
SelectedValuePath="code"
Text="{Binding cit_type}"
IsEditable="true"
IsReadOnly="false"
SelectedValue="{Binding cit_type}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Border BorderThickness="0,0,1,0" BorderBrush="Black">
<TextBlock Text="{Binding Path=code}" mvvm:View.WidthEx="2" ></TextBlock>
</Border>
<TextBlock Text="{Binding Path=description}" mvvm:View.WidthEx="15" Margin="1" ></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Any help will be greatly appreciated.
As an aside, I have to use this exact same format (same list, etc. just different SelectedValue) in a number of forms - so if you want to suggest the best way to do this in xaml, that would be nice. In my pre-xaml days, I would just create a control, set a property or 2, and use that in all my forms. But I am supposed to use xaml, so am not sure of the best way to do this.
Thanks!
I can't believe that I searched all over the net to find an answer and, until now, could not find one. The answer is as easy as I thought it should be.
Just replace:
DisplayMemberPath="code"
with
TextSearch.TextPath="code"
and the code works just fine.
Thanks to all who helped.
I can give you an idea how to use SelectedItem
ViewModel
public class ViewModel
{
public ViewModel()
{
//Suppose your collection CitTypes is Initialized and filled with there Items
//Now you can set first Element as selected in ComboBox
SelectedItem = CitTypes.FirstOrDefault();
}
CitType selectedItem;
public CitType SelectedItem
{
get { return selectedItem; }
set { selectedItem = value; RaisePropertyChanged("SelectedItem"); }
}
}
xaml
<ComboBox Name="cboCit_type"
IsSynchronizedWithCurrentItem="True"
mvvm:View.FlowsWithPrevious="True"
ItemsSource="{Binding Path=cuCodeInfo.CitationTypes}"
Text="{Binding cit_type}"
IsEditable="true"
IsReadOnly="false"
**SelectedItem="{Binding SelectedItem}"**>

WPF: ComboBox with selecteditem set make not use of SelectedIndex=0?

Why is the first element in my combobox popup menu not shown in the selected item area of
my combobox , when I use the SelectedItem binding? Without that it is showing up ?? Using
the same code selecteditem + selectedindex that is no problem!
<ComboBox
ItemsSource="{Binding SchoolclassSubjectViewModels}"
SelectedItem="{Binding SelectedSchoolclassSubjectViewModel}"
SelectedIndex="0"
Height="23"
HorizontalAlignment="Left"
Margin="375,13,0,0"
VerticalAlignment="Top"
Width="151">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding SchoolclassName}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding SubjectName}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Well as workaround I used:
SchoolclassSubjectViewModels.Add(schoolclassSubjectVM);
SelectedSchoolclassSubjectViewModel = schoolclassSubjectVM;
and this:
SelectedItem="{Binding SelectedSchoolclassSubjectViewModel,Mode=TwoWay}"
but I would prefer the xaml only way as it should really work.
It is because the reference inside your ItemsSource collection is not the same as the one in your SelectedItem property. I would venture to guess that you are using one object context to query your database for the list of SchoolclassSubject objects which the ItemsSource is bound to, but another context to query the actual data item to which you bind the SelectedItem. Even though the list contains a reference which represents the value held by your object, it is not really the same reference, but a separate instance of the same data.
There are ways to solve this issue, most of them involve using the SelectedValuePath and SelectedValue instead of the SelectedItem properties, but the concrete solution would be different depending on your particular ORM.

Why is this WPF ComboBox not showing the selected value?

<CombobBox x:Name="cbo"
Style="{StaticResource ComboStyle1}"
DisplayMemberPath="NAME"
SelectedItem="{Binding Path=NAME}"
SelectedIndex="1">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=NAME}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
In the Window OnLoaded event, I wrote the code to set the ItemsSource:
cbo.ItemsSource = ser.GetCity().DefaultView;
While loading the window I can see that the initially the the first item is loading but at the same time it clears the displayed item. I am stuck in this scenario and any help is appreciated.
Regards
Kishore
QUICK ANSWER : Set SelectedIndex = 1 from code-behind.
It seems that the code in XAML executes first (the InitializeComponent() method), which sets SelectedIndex = 1, but ItemsSource is not specified yet! So SelectedIndex won't affect! (And remember, you cannot specify ItemsSource before InitializeComponent())
So you have to manually set SelectedIndex = 1 after setting ItemsSource.
You should do like this :
XAML
<ComboBox x:Name="cbo"
Style="{StaticResource ComboStyle1}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=NAME}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Code
cbo.ItemsSource = ser.GetCity().DefaultView;
cbo.SelectedIndex = 1;
Or this:
XAML
<ComboBox x:Name="cbo" Initialized="cbo_Initialized"
Style="{StaticResource ComboStyle1}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid>
<TextBlock Text="{Binding Path=NAME}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Code
private void cbo_Initialized(object sender, EventArgs e)
{
cbo.SelectedIndex = 1;
}
Also note that i've removed DisplayMemberPath="NAME" because you cannot set both DisplayMemberPath and ItemTemplate at the same time. And also, use either SelectedItem or SelectedIndex, not both.
Resetting the ItemsSource will mess up the selection.
Also, you are setting both SelectedItem and SelectedIndex. You want to set only one of these; if you set both, only one will take effect.
In addition, your SelectedItem declaration is probably wrong. SelectedItem="{Binding NAME}" will look for an item which is equal to the value of the NAME property of the ambient (probably Window-level) DataContext. This will work only if the ComboBox.ItemsSource is a list of strings. Since your ItemTemplate works, I assume ComboBox.ItemsSource is actually a list of City objects. But you are telling WPF that the SelectedItem should be a string (a NAME). This string will never be equal to any City object, so the result will be no selection.
So to fix the problem, after setting ItemsSource, set either SelectedItem or SelectedIndex, depending on your requirements and your data model:
cbo.ItemsSource = ser.GetCity().DefaultView;
cbo.SelectedIndex = 1;
// or: cbo.SelectedItem = "Wellington"; // if GetCity() returns strings - probably not
// or: cbo.SelectedItem = City.Wellington; // if GetCity() returns City objects

M-V-VM WPF: Way to maintain Databinding while passing parent object of Databound object into IValueConverter?

I'm using model-view-viewmodel I currently have a class with 3 pieces of data: 2 integers and an enumeration.
Its constructor looks like this:
//C#
public Outcome(OutcomeEnum outcomeEnum, Int32 acutalOutcomeData, Int32 expectedOutcomeData)
{
m_outcomeEnum = outcomeEnum;
m_actualData = acutalOutcomeData;
m_expectedData = expectedOutcomeData;
}
I have 2 ComboBoxes next to each other that I have bound to one List of outcome objects (List<Outcome>) which I use to the "actual" and "expected" integer values.
This section of code looks like this: (The SelectedItem and ItemsSource are dependency properties in the viewmodel )
<ComboBox ItemsSource="{Binding Path=OutcomeList}" SelectedItem="{Binding SelectedExpectedOutcome, Mode=TwoWay}" x:Name="PART_cbExpectedOutcome" Grid.Column="1" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Path=ExpectedOutcomeData, Converter={StaticResource OutcomeDataToStringConverter}, ConverterParameter=Expected }" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox ItemsSource="{Binding Path=OutcomeList}" SelectedItem="{Binding SelectedActualOutcome, Mode=TwoWay}" x:Name="PART_cbActualOutcome" Grid.Column="2" >
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Path=ActualOutcomeData, Converter={StaticResource OutcomeDataToStringConverter}, ConverterParameter=Actual}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
My problem is: I'd like to pass a reference to the Outcome object into the IValueConverter that I'm using to convert between, but this appears to not be possible using the IConvertParameter but rather I would be forced to use Multibinding per the msdn post here.
I'd like to simplify my approach as creating a multibinding for something relatively as simple as this seems like overkill.
I'd like to do only one thing with the Outcome object that I seek to pass into IValueConverter, which is to determine the enumeration type of OutcomeEnum so I can provide the proper formatting of the expected or actual data value.
Is there any simpler way for me to be able to pass the Outcome object into the IValueConverter OutcomeDataToStringConverter while maintaining a two-way binding with this list of Outcome objects? I'm open to suggestions.
Why don't you bind to the entire Outcome object instead of its ActualOutcomeData or ExpectedOutcomeData properties? That way the converter will receive the entire Outcome object, and based on it and on the converter parameter return the proper value.
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Converter={StaticResource OutcomeToStringConverter}, ConverterParameter=Expected}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
For right now, I ended up changing the Outcome class so it had only one member - outcomeData
Then I created two lists of Outcome objects which were bound to the combobox's ItemsSource - ActualOutcomeList and ExpectedOutcomeList.
I can then bind the Outcome object itself to the Combobox, but by having two lists I avoid the duplicate selection issue I describe in the comment on Avaid's post.
In the end the code looks like this:
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Converter={StaticResource OutcomeToStringConverter}}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<ComboBox
x:Name="PART_cbActualOutcome"
Grid.Column="2"
ItemsSource="{Binding Path=ActualOutcomeList}"
SelectedItem="{Binding SelectedActualOutcome, Mode=TwoWay}"
IsEnabled="{Binding Path=IsOutcomeEnabled}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Converter={StaticResource OutcomeToStringConverter}}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>

Resources