Issue with ComboBox and ObservableCollection<string> - c#

Here's the Xaml for my combobox:
<ComboBox Grid.Column="4"
Grid.Row="3"
ItemsSource="{Binding Path=Users, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type cs:AdvancedSettingEditor}}}"
Margin="5"
x:Name="UserPicker"
SelectedValue="{Binding Path=StandAloneUserName, Mode=TwoWay}"
TabIndex="1"
Visibility="{Binding Converter={StaticResource InvertedBoolToVisibility}, Path=LoginRequired}" />
In the code behind, I have a simple ObservableCollection of strings:
public ObservableCollection<string> Users { get; set; }
The Users collection is loaded with string data from the database.
There is also a DependencyProperty that is bound to the window's DataContext that has a property called StandAloneUserName. The object stored in this property implements INotifyPropertyChanged.
When I run the program, the combo box has all of the names in the Users collection in the drop down list, but the box is blank. The control is not losing the value of the field, so it just doesn't know what to display.
How do I get my ComboBox to display the name that is in the StandAloneUserName property?
Tony

Set SelectedItem instead of SelectedValue
<ComboBox SelectedItem="{Binding Path=StandAloneUserName, Mode=TwoWay}" ... />
I'm assuming it's evaluating as "doesn't exist" since you don't have SelectedValuePath set to anything.

Related

WPF ComboBox: Text value doesn't affect SelectedItem

If i load string value in dependency property "Text" of ComboBox. ComboBox does not pair string value with ItemsSource objects and i don't have SelectedItem filled.
Need SelectedItem for ToolTip!!
<ComboBox x:Name="FieldComboBox" IsEditable="True" IsTextSearchEnabled="True"
Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="DataCode"
ItemsSource="{Binding FieldAlternative.FieldParts[0].DataItems}"
ToolTip="{Binding Path=SelectedItem.Description, ElementName=FieldComboBox}"/>
If i've empty Text and write, it works fine. If i've opened form with value in Text i have SelectedItem null but value Text is displayed.
Any solution how to forced pairing Text with objects in ItemsSource to fill SelectedValue, or any better solution of my problem.
Thx
You can set a default value to your combobox by the SelectedItem property which will be binded to your Text Field in your ViewModel:
SelectedItem="{Binding Path=Text, Mode=TwoWay}"
So your combobox element becomes like this:
<ComboBox x:Name="FieldComboBox" IsEditable="True" IsTextSearchEnabled="True"
Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Path=Text, Mode=TwoWay}"
DisplayMemberPath="DataCode"
ItemsSource="{Binding FieldAlternative.FieldParts[0].DataItems}"
ToolTip="{Binding Path=SelectedItem.Description, ElementName=FieldComboBox}"/>

Binding to a second property

I have two properties in my viewmodel, called Premises and Towns.
I'm binding my ListViewItems to Premises, and in the itemtemplate I want to bind to Towns, but when I use the following XAML it tries to bind to Premises.Towns instead of Towns.
How can I bind to Towns directly?
Viewmodel:
public class MainWindowViewModel
{
public ObservableCollection<Premise> Premises;
public List<Town> Towns;
}
XAML:
<ListView x:Name="PremisesList" Margin="195,35,10,10"
ItemContainerStyle="{StaticResource OverviewListViewItemStyle}"
ItemsSource="{Binding Premises}" HorizontalContentAlignment="Stretch">
And this is what's in my OverviewListViewItemStyle.
<ComboBox ItemsSource="{Binding Towns}" Grid.Row="2" Grid.ColumnSpan="3">
<ComboBox.ItemTemplate>
<DataTemplate>
<ComboBoxItem>
<TextBox Text="{Binding Name}" />
</ComboBoxItem>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I'd like to be able to select a Town for a Premise via XAML.
You are correct in your assumption. ComboBox looks for Towns in Premise class, which is the class behind each ListViewItem If you want to refer to same context as ListView you need to use RelativeSource binding.
<ComboBox
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.Towns}"
Grid.Row="2"
Grid.ColumnSpan="3"
DisplayMemberPath="Name"/>
Not related to your problem but you also don't need to specify DataTemplate to display single property. DisplayMemberPath will work as well. If you do specify DataTemplate you don't need to use ComboBoxItem as ComboBox will wrap DataTemplate content in ComboBoxItem so effectively you'll end up with ComboBoxItem inside another ComboBoxItem
You bind the ItemsSource to the Premises property therefore if you bind to the Towns in the OverviewListViewItemStyle the binding engine will look up in the Premise object for a property called Towns.
If you want to select a town for a premises you should tell to the combobox where to look from that property. You can try to set the combobox's datacontext to the main viewmodel with relative source in the binding. Something like that:
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.Towns}"

Binding Datagrid Item to Textboxes

I try to bind a selected item in my datagrid to some textboxes.
Sadly the textboxes wont be updated on change.
If you need more Informations fell free to ask.
In my view i try to bind the the data of the selected Item of the Datagrid to the Textboxes.
in the Textboxes there can be a new employee you want to add or one of the Datagrid that you want to edit.
<TextBox Name="TxtName" Text="{Binding Employee.LastName}" Grid.Column="1" Grid.Row="0"></TextBox>
<TextBox Name="TxtFirstName" Text="{Binding Employee.FirstName}" Grid.Column="3" Grid.Row="0"></TextBox>
<TextBox Name="TxtDateOfBirth" Text="{Binding Employee.DateOfBirth, StringFormat=d}" Grid.Column="1" Grid.Row="1"></TextBox>
<ComboBox Name="CmbGender" SelectedItem="{Binding Employee.Gender}" ItemsSource="{Binding Genders}" DisplayMemberPath="Short" Grid.Column="3" Grid.Row="1"/>
<DataGrid Name="GrdAllEmployees" ItemsSource="{Binding Employees}" SelectedItem="{Binding SelectedEmployee}" Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="4" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserResizeRows="False" CanUserSortColumns="False">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding SelectionChanged}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
In my ViewModel i set the Value of the Selected Employee to the Employee that is shown in the Textboxes and raise the Events for both.
public Employee SelectedEmployee
{
get { return _selectedEmployee; }
set
{
_selectedEmployee = value;
NotifyPropertyChanged("SelectedEmployee");
_employee = _selectedEmployee;
NotifyPropertyChanged("Employee");
}
}
After this the values are correct on debugging. but the view will not be updated.
There is a much simpler way of displaying values from the selected item of the DataGrid, or in fact any collection control in WPF. That is to use the Selector.IsSynchronizedWithCurrentItem property. When you set this to true, then you can reference the selected item from that collection using the / notation, which means the current item. Try something like this:
<StackPanel>
<DataGrid ItemsSource="{Binding Employees}" IsSynchronizedWithCurrentItem="True" />
<TextBlock Text="{Binding Employees/Name}" />
</StackPanel>
This would display the Name property value from the currently selected item in the DataGrid. Further information... from the Binding.Path Property page on MSDN:
When the source is a collection view, the current item can be specified with a slash (/). For example, the clause Path=/ sets the binding to the current item in the view. When the source is a collection, this syntax specifies the current item of the default collection view.
Property names and slashes can be combined to traverse properties that are collections. For example, Path=/Offices/ManagerName specifies the current item of the source collection, which contains an Offices property that is also a collection. Its current item is an object that contains a ManagerName property.
It worked for me after i replaced my own classes for the RelayCommand and ViewModelBase with the ones of MVVMlight, that i downloaded from nuget.
https://mvvmlight.codeplex.com/
I just had to change the NotifyPropertyChanged with RaisePropertyChanged.
But also thanks to #Sheridan for his advice.

Two way data Binding issue with combo box - WPF

I have a View that contains a Combobox. The Combobox SelectedItem property is data bound to SelectedX property of View Model as two way data binding. When the viewModel is initialized, the SelectedX property is set correctly. But after that when the view renders, it resets the value of SelectedX(since the binding is two-way).
So the two way data binding for the Combobox is basically not working. Please advise.
This is the xaml for my view. I initialize the View model first with apprpriate values for Relationships and SelectedX. When the view renders, the combo box resets the value for SelectedX. (I figured that by adding breakpoints). Hope this helps
<ComboBox Grid.Row="1" Grid.Column="1" Margin="5" Background="White" BorderBrush="DarkGray"
SelectedItem="{Binding SelectedX, Mode=TwoWay}"
ItemsSource="{Binding Relationships}" DisplayMemberPath="Value"
SelectedValuePath="Value" SelectedValue="{Binding Key, Mode=TwoWay}"
IsEditable="False" IsReadOnly="True" />
SelectedValue="{Binding Key, Mode=TwoWay}"
This will change the SelectedItem to its SelectedValue.

WPF Binding a Dependency Property

I am having an issue binding a dependency property in a UserControl. When it initializes it gets a value but then it will not update. I've probably missed something obvious, here are some code snippets:
This is where I bind the BalanceContent dependency property:
<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4"
BalanceContent="{Binding Source={StaticResource UserData}, Path=SelectedUser.Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Game:PlayerDetails>
Here is the TextBox in the UserControl:
<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}, Path=BalanceContent}"
Grid.Row="7"></TextBox>
Here is the Dependency Property:
public static readonly DependencyProperty BalanceContentProperty = DependencyProperty.Register(
"BalanceContent", typeof(string), typeof(PlayerDetails));
public string BalanceContent
{
get
{return (string) GetValue(BalanceContentProperty);}
set
{SetValue(BalanceContentProperty, value);}
}
Here is the list where the selected user is updated, which is in a view that uses the UserControl:
<ListView x:Name="lstAccounts" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" Grid.RowSpan="4"
ItemsSource="{Binding Source={StaticResource UserData}, Path=CurrentUserSearch}"
SelectedItem="{Binding Source={StaticResource UserData}, Path=SelectedUser}"
And SelectedUser is defined here in a class that implements INotifyPropertyChanged:
public User SelectedUser
{
get
{
return _selectedUser;
}
set
{
_selectedUser = value;
OnPropertyChanged(new PropertyChangedEventArgs("SelectedUser"));
}
}
The idea is that the TextBox should update when a new user is selected in the list but at the moment it is not doing so. I've put the binding on local TextBox and it updates fine, just not on a DependencyProperty. Any help appreciated.
There are some possibilities you could try:
First, your ListView may not be updating yours ViewModel's SelectedUser property. Try setting the binding in your ListView to "TwoWay" mode:
<ListView x:Name="lstAccounts" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="2" Grid.RowSpan="4"
ItemsSource="{Binding Source={StaticResource UserData}, Path=CurrentUserSearch}"
SelectedItem="{Binding Source={StaticResource UserData}, Path=SelectedUser, Mode=TwoWay}"/>
You can organize better the way the DataContext's are defined. Remember that all the child controls of your UserControl will have access to its DataContext without the use of relative binding (they inherit it). As your PlayerInfo control depends on the SelectedUser, consider setting it's DataContext to the SelectedUser, either binding it to the SelectedUser of the ListView or the SelectedUser in the UserData viewmodel.
<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4" DataContext="{Binding Source={StaticResource UserData}, Path=SelectedUser}"
BalanceContent="{Binding Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Game:PlayerDetails>
The source of the current SelectedUser could also be the ListView:
<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4" DataContext="{Binding SelectedItem, ElementName=lstAccounts}"
BalanceContent="{Binding Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Game:PlayerDetails>
Either way, you will then be able to do the following on the TextBox, because its DataContext will be the same as the one of its parent:
<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding Balance}"
Grid.Row="7"></TextBox>
If the usercontrol depends on the root viewmodel for things like commands and other high level logic, then set the DataContext to it in a way you can easily access the SelectedUser.
<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4" DataContext="{StaticResource UserData}"
BalanceContent="{Binding SelectedUser.Balance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Game:PlayerDetails>
So you can do this:
<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding SelectedUser.Balance}"
Grid.Row="7"></TextBox>
In this second approach, however, you will have to check one thing I'm not sure about. I know that when the DataContext of a control changes, it will update all the dependent bindings allright. For example, if you changed the PlayerDetails DataContext to another UserData instance, the BalanceContent property would update as well. However, in this case the BalanceContent depends on property of the SelectedUser of the UserData. So it will listen to Property changes of that instance of User. If the SelectedUser.Balance changes (and User implements INotifyPropertyChanged, or it is a DependencyProperty), BalanceContent will update. Now, if the SelectedUser instance in the UserData changes, I'm not sure BalanceContent will update, because I think that a binding does not listen to changes of every object in its path.
EDIT
The last point was perhaps the first problem I hit when developing with xaml. I had a DataGrid in Silverlight whose entity type had a property of a complex type. One of the columns depended on a property of the complex type. If I changed the value of the complex type, the column would update fine (it implemented INPC). If I changed the complex type instance of an entity, the column would not... The solution was to cascade DataContexts: I created a template column, set the binding of the column for the complex type, instead of its property. Then I bound the text of the TextBox of my template to the property of the complextype, because it was now the TextBox's DataContext.
In your case you can do it for the TextBox.Text, but not for the PlayerDetails.BalanceContent. You can bind the TextBox.DataContext to the SelectedUser of the UserData and then bind Text to the Balance property.
<TextBox VerticalAlignment="Center" DataContext="{Binding SelectedUser}" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding Balance}"
Grid.Row="7"></TextBox>
Please try testing after changing your binding for text box inside your user control to bind to BalanceContent which is User Controls Dependency Property (your original binding source seems to be data context property)
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}, Path=BalanceContent}"
EDIT: Please try the following code
public User SelectedUser
{
get
{
return _selectedUser;
}
set
{
_selectedUser = value;
OnPropertyChanged(new PropertyChangedEventArgs("SelectedUser"));
OnPropertyChanged(new PropertyChangedEventArgs("SelectedUserBalance"));
}
}
public string SelectedUserBalance
{
get
{
return _selectedUser.Balance;
}
}
<Game:PlayerDetails x:Name="SelectedPlayerDetails" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Grid.RowSpan="4"
BalanceContent="{Binding Source={StaticResource UserData}, Path=SelectedUserBalance, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
</Game:PlayerDetails>
<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type UserControl}}, Path=BalanceContent}"
Grid.Row="7"></TextBox>
The Textbox is trying to bind to the UserControls DataContext. give an x:Name to the Usercontrol definition and set the TextBox's DataContext to DataContext="{Binding ElementName=[YourControlName]}"
<TextBox VerticalAlignment="Center" FontFamily="Formata" FontSize="20" Grid.Column="2"
Text="{Binding SelectedItem.BalanceContent, ElementName=lstAccounts}" Grid.Row="7"></TextBox>
forget binding of UserControl , Directly bind TextBox to SelectedItem . I hope this will help.
You are binding a StaticResource to the property. StaticResources, as opposed to DynamicResources, are loaded only once, when the Window (or UserControl) is being initialised. DynamicResources are loaded only when needed (and each time when needed). Any change that is made to a DynamicResource is picked up immediately and the property is updated accordingly. Simply replace the keyword "StaticResource" with "DynamicResource", and the property binding should update each time the resource changes.
Note: If the StaticResource is an object that derives from the Freezable class, it will also behave somewhat like a DynamicResource. For example, if your resource is a Brush, the bound property will update each time you change the Brush object in any way (by changing its opacity, for example), but this is due to the behaviour inherited from the Freezable class. However if you replace the Brush object in the StaticResource by a new Brush object, this change won't be picked up, because the change has been made to the Resource, which is Static, not the Brush.
Also Note: Even resources that are only available as StaticResources, such as data SystemColors, SystemFonts (which provide access to system settings) can be wrapped in a DynamicResource to ensure that the property is updated at each change. This can be done this way:
<Control Property="{DynamicResource {x:Static SystemColors.XXX}}"></Control>.

Categories