Binding Datagrid Item to Textboxes - c#

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.

Related

Setting default or a combobox with Xaml (despite Binding)

I want to Bind the selectedItem of an RadCombobox to an Observable Collection in the DataContext. This works perfectly fine.
But now I want to set the default value for the combobox, this also works if I set the Variable in the Data Context. But It would be better in my opinion to set the deault within the Xaml. This should be possible with SelectedIndex="0" unfortunatly ths doesnt work anymore (no default shown) since the binding is active.
Is there an option to set deault value or the combobox while there is a binding for the SelectedItem?
<telerik:RadComboBox x:Name="radComboBox"
ItemsSource="{Binding xyList, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="0" SelectedItem="{Binding Selectedxy}"
HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" Width="250" Height="25" Grid.Row="1">
</telerik:RadComboBox>
.
public string Selectedxy {get;set; }
public void FillxyDropdown()
{
xyList = new ObservableCollection<string>();
foreach (KeyValuePair<string, int> Line in model.ReadxyList())
{
xyList.Add(Line.Key);
}
//Sets the default value but isnt the desired way.
Selectedxy = "XY3.31";
}
I agree with The One comment. You definitely should specify the default values in the View Model.
If you still want to do this in the XAML, you can set the binding mode of the SelectedItem property to OneWayToSource. In this case, Selectedxy will be updated when SelectedItem changes, but not the other way around.
<telerik:RadComboBox x:Name="radComboBox"
ItemsSource="{Binding xyList, UpdateSourceTrigger=PropertyChanged}" SelectedIndex="1"
SelectedItem="{Binding Selectedxy, Mode=OneWayToSource}" HorizontalAlignment="Left"
Margin="0,0,0,0" VerticalAlignment="Top" Width="250" Height="25" Grid.Row="1">
</telerik:RadComboBox>

WPF: Ordering and Displaying ListView SelectedItems in MVVM

I have a ListView with SelectionMode=Multiple and two TextBoxes. One should display the top-most selected item, one should display the bottom most selected item. I am also working using the MVVM design pattern.
The issues are as follows:
"SelectedItems" is indexed from the first selection point - so the SelectedItems[0] can be the bottom-most selected item, which is undesirable. I want the top-most item to display in the top box and the bottom-most item to display in the bottom box.
I can't seem to reference SelectedItems[ SelectedItems.Count - 1] from the XAML in order to display the last selected item.
Here's a look at my current XAML:
<ListView x:Name="myListView" ItemsSource="{Binding MyList}"
SelectionMode="Multiple">
<TextBox x:Name="topTextBox" Grid.Column="1" Grid.Row="2"
Text="{Binding ElementName=myListView, Path=SelectedItems[0].ID}" />
<TextBox x:Name="bottomTextBox" Grid.Column="1" Grid.Row="3"
Text="{Binding ElementName=myListView, Path=SelectedItems[SelectedItems.Count-1].ID}" />
I'm not sure what the best approach to take is.
The solution I settled on is as follows:
I used the interactive namespace (Don't forget to reference the DLL) and the following XAML:
xmlns:interact="http://schemas.microsoft.com/expression/2010/interactivity"
<TextBox x:Name="startTextBox" Grid.Column="1" Grid.Row="2"
Text="{Binding LatestSelectedItem}" />
<ListView x:Name="myListView" ItemsSource="{Binding MyList}"
SelectionMode="Extended" >
<interact:Interaction.Triggers>
<interact:EventTrigger EventName="MouseUp"> <!-- Alternatively, OnSelectionChanged -->
<interact:InvokeCommandAction Command="{Binding MyListViewSelectionChangedCommand}"
CommandParameter="{Binding ElementName=myListView, Path=SelectedItems}" />
</interact:EventTrigger>
</interact:Interaction.Triggers>
</ListView>
Then I provided appropriate Properties in my ViewModel, along with the following command:
private void MyListView_SelectionChanged(object param)
{
IList selectedItems = (IList)param;
List<MyViewModel> myList = selectedItems.OfType<MyViewModel>().ToList();
if (myList.Count > 0)
{
myList.Sort(); // Implement comparator on MyViewModel
LatestSelectedItem = myList[myList.Count-1];
}
}
Works fine.

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>.

Issue with ComboBox and ObservableCollection<string>

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.

Categories