Wpf set edit mode for all cells in datagridrow MVVM - wpf

Using the MVVM pattern in a WPF application, I want to set on the 'Editing State' of a records in row.
Every time the user starts editing a record by clicking on Edit button, that row should switch to the 'editing' mode.
Finished, he can save all changes in the row by clicking the same or another button
How can I set edit mode (IsReadOnly=true/false) for All cells in selected Row on click "Edit" button?
Any help is appreciated!
This is my current code:
XAML
<Window x:Class="TotalRows.TotalRowsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TotalRows"
mc:Ignorable="d"
x:Name="xMainWindow"
Title="RowsTotalWindow" Height="450" Width="800">
<Window.DataContext>
<local:ExampleData/>
</Window.DataContext>
<Grid>
<StackPanel >
<DataGrid x:Name="myGrid" IsReadOnly="True" CanUserAddRows="False" SelectionMode="Single" CanUserDeleteRows="False"
ItemsSource="{Binding ItemsViewCollection}" RowDetailsVisibilityMode="Collapsed"
SelectedItem="{Binding SelectedItemRow, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel HorizontalAlignment="Stretch">
<ToggleButton x:Name="btnEditItem" Content="Edit" Width="50" Height="20" Margin="0 0 3 0"
Command="{Binding RelativeSource={RelativeSource AncestorType=DataGrid}, Path=DataContext.UpdateItemCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=DataContext}"/>
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn x:Name="gr" Binding="{Binding Group}" Header="Gr" Width="30" />
<DataGridTextColumn x:Name="one" Binding="{Binding Col_1}" Header="h1" Width="30" />
<DataGridTextColumn x:Name="two" Binding="{Binding Col_2}" Header="h2" Width="30" />
<DataGridTextColumn x:Name="tree" Binding="{Binding Col_3}" Header="h3" Width="30" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
</Window>
Code Behind
namespace TotalRows
{
public class ItemClass
{
public int Group { get; set; }
public string Title { get; set; }
public int Col_1 { get; set; }
public int Col_2 { get; set; }
public int Col_3 { get; set; }
}
public class ExampleData
{
private bool _IsReadMode;
public bool IsReadMode
{
get { return _IsReadMode; }
set
{
_IsReadMode = value;
OnPropertyChanged(nameof(IsReadMode));
}
}
private ItemClass _selectedItem = null;
public ItemClass SelectedItemRow
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged(nameof(SelectedItemRow));
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<ItemClass> _items;
public ObservableCollection<ItemClass> Items
{
get
{
return _items;
}
set
{
if (_items != value)
{
_items = value;
OnPropertyChanged(nameof(Items));
}
}
}
private ICollectionView _itemsViewCollection;
public ICollectionView ItemsViewCollection
{
get
{
return _itemsViewCollection;
}
set
{
if (_itemsViewCollection != value)
{
_itemsViewCollection = value;
OnPropertyChanged(nameof(ItemsViewCollection));
}
}
}
public ICommand UpdateItemCommand { get; private set; }
public ExampleData()
{
IsReadMode = true;
UpdateItemCommand = new ViewModelCommand(param => updateItemCommand(param));
Items = new ObservableCollection<ItemClass>()
{
new ItemClass() {Group=1, Title="Item1", Col_1=100, Col_2=150, Col_3=250},
new ItemClass() {Group=2, Title="Item1", Col_1=50, Col_2=2, Col_3=200},
new ItemClass() {Group=2, Title="Item2", Col_1=50, Col_2=100, Col_3=40},
new ItemClass() {Group=3, Title="Item1", Col_1=60, Col_2=25, Col_3=230},
new ItemClass() {Group=3, Title="Item2", Col_1=30, Col_2=25, Col_3=0},
new ItemClass() {Group=3, Title="Item3", Col_1=9, Col_2=100, Col_3=20},
new ItemClass() {Group=4, Title="Item1", Col_1=46, Col_2=32, Col_3=30},
};
ItemsViewCollection = CollectionViewSource.GetDefaultView(Items);
ItemsViewCollection.GroupDescriptions.Add(new PropertyGroupDescription("Group"));
}
private void updateItemCommand(object param)
{
IsReadMode = !IsReadMode;
}
}
}

Do you realise f2 switches the current row into edit mode?
CommandManager.RegisterClassInputBinding(ownerType, new InputBinding(BeginEditCommand, new KeyGesture(Key.F2)));
CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(BeginEditCommand, new ExecutedRoutedEventHandler(OnExecutedBeginEdit), new CanExecuteRoutedEventHandler(OnCanExecuteBeginEdit)));
You could bind your edit button to a command in the ExampleData viewmodel and pass a reference to the specific ItemClass as a command parameter.
Use relativesource binding to get to that command.
ExampleData owns that collection so you can set properties on that instance and stash a reference or index to the last one they edited set the flag back. Or iterate through the whole collection.
Seems you know how to write a command but I recommend the community mvvm toolkit and relaycommand.
Your binding would be something like
<Button Command="{Binding DataContext.EditThisOneCommand
, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}">
The command parameter passes the row to an Icommand so will be a parameter passed to your command.
A similar command I happen to have.
private RelayCommand<Thing> _colourCommand;
public RelayCommand<Thing> ColourCommand
{
get
{
return _colourCommand
?? (_colourCommand = new RelayCommand<Thing>(
_thing =>
{
_thing.Row = Items.IndexOf(_thing);
},
_thing => CanUserClick));
}
}
You would of course have EditThisOneCommand
You then have to tell the UI to issue a BeginEditCommand.
And then you need to tell the UI to issue a CommitEditCommand when you finish.
CommandManager.RegisterClassCommandBinding(ownerType, new CommandBinding(CommitEditCommand, new ExecutedRoutedEventHandler(OnExecutedCommitEdit), new CanExecuteRoutedEventHandler(OnCanExecuteCommitEdit)));
These commands are source from the datagrid.
You could instead just bind those datagrid commands to buttons and not have this flag.
A datagridrow has a property IsEditing. You might be able to bind that onewaytosource to your flag. You'd set that binding via a style.
Not sure why you'd want to, but you could take a look at that.
https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.datagridrow.isediting?view=windowsdesktop-7.0

Related

Data Binding INotifyPropertyChanged Not Working as Expected

I am learning about the data binding especially with DataGrid. In my code here, I have a DataGrid and a Labelwhich shows the first cell value of DataGrid. Output of XAML is like . Considering the image below, The Label content next to The First Cell Value is: Monkey which I think i have got from the DataGrid first cell. Now what I wanted was to update left of The First Cell Value is: when I change the value in my DataGrid first cell. But I am unable to achieve it.
Bellow is my Code and the XAML File
CODE
namespace DataGridExampleSelfTry
{
public class MainWindowVM:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
private string _FirstCell;
public string FirstCell
{
get{ return _FirstCell; }
set
{
_FirstCell = value;
PropertyChanged(this,new PropertyChangedEventArgs(nameof(FirstCell)));
}
}
public string SecondCell { get; set; }
private ObservableCollection<animies> _animelistforbinding;
public ObservableCollection<animies> animelistforbinding
{ get
{
return _animelistforbinding;
}
set
{
_animelistforbinding = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(animelistforbinding)));
}
}
ObservableCollection<animies> addinganime = new ObservableCollection<animies>();
public MainWindowVM()
{
addinganime.Add(new animies("Monkey", "D Luffy"));
animelistforbinding = addinganime;
FirstCell = animelistforbinding[0].FirstName;
SecondCell = animelistforbinding[0].LastName;
}
}
public class animies:INotifyPropertyChanged
{
private string _FirstName;
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
public string FirstName
{
get { return _FirstName; }
set
{
_FirstName = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(FirstName)));
}
}
public string LastName { get; set; }
public animies(string dFirstName, string dLastName)
{
FirstName = dFirstName;
LastName = dLastName;
}
}
}
XAML
<Window x:Class="DataGridExampleSelfTry.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DataGridExampleSelfTry"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="450">
<Window.DataContext>
<local:MainWindowVM/>
</Window.DataContext>
<StackPanel>
<DataGrid x:Name="XAML_DataGrid"
AutoGenerateColumns="False" CanUserAddRows="False"
ItemsSource="{Binding animelistforbinding}" Margin="5"
CanUserSortColumns="False" HorizontalGridLinesBrush="Gray"
VerticalGridLinesBrush="Gray" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding FirstName, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Header="First name" Width="*" IsReadOnly="False"/>
<DataGridTextColumn Binding="{Binding LastName}" Header="Last Name" Width="*" IsReadOnly="False"/>
</DataGrid.Columns>
</DataGrid>
<StackPanel Orientation="Horizontal">
<Label Content="The First Cell Value is : "/>
<Label Content="{ Binding FirstCell}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="The Second Cell Value is : "/>
<Label Content="{ Binding SecondCell}"/>
</StackPanel>
<Button Content="Button" Margin="50"/>
</StackPanel>
</Window>
Thank you for your Help.
Either bind to directly to the same property that the first column of the DataGrid binds to:
<Label Content="{Binding animelistforbinding[0].FirstName}"/>
...or set the FirstCell property whenever the FirstName property of the first item in animelistforbinding is set. You can do this by handling the PropertyChanged event for the first item in your view model:
public MainWindowVM()
{
addinganime.Add(new animies("Monkey", "D Luffy"));
animelistforbinding = addinganime;
FirstCell = animelistforbinding[0].FirstName;
SecondCell = animelistforbinding[0].LastName;
animelistforbinding[0].PropertyChanged += (s, e) => FirstCell = animelistforbinding[0].FirstName;
}

WPF combobox with bound selected value and static items not recognizing selection on init

I need to have a combobox with two values. The first should have a custom name, while the second should use the underlying bound object's properties. Both items are values on the VM, and I'm able to bind all of it successfully.
XAML
<Window x:Class="StaticComboBox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:StaticComboBox"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:StaticUIVm}"
Title="MainWindow"
Height="450"
Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<ComboBox Grid.Row="1"
SelectedValuePath="Tag"
SelectedValue="{Binding SelectedValue, Mode=TwoWay}">
<ComboBox.Items>
<ComboBoxItem Content="Custom Display Text 111"
Tag="{Binding FirstValue}" />
<ComboBoxItem Content="{Binding SecondValue.Item2}"
Tag="{Binding SecondValue}" />
</ComboBox.Items>
</ComboBox>
</Grid>
</Window>
XAML.cs
using System.Windows;
namespace StaticComboBox
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new StaticUIVm();
}
}
}
StaticUIVm.cs
using StaticComboBox.Annotations;
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace StaticComboBox
{
public class StaticUIVm : INotifyPropertyChanged
{
public Tuple<long, string> FirstValue { get; set; }
public Tuple<long, string> SecondValue { get; set; }
private Tuple<long, string> _selectedValue;
public Tuple<long, string> SelectedValue
{
get { return _selectedValue; }
set
{
_selectedValue = value;
OnPropertyChanged();
}
}
public StaticUIVm()
{
FirstValue = new Tuple<long, string>(1, "Some Static Value");
SecondValue = new Tuple<long, string>(2, "Some Other Static Value");
SelectedValue = FirstValue;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
My problem is that despite the bindings working correctly for the items and displaying and when I as a user select a value, the combobox isn't reflecting the correct selection when initializing the VM class. Meaning, it doesn't select FirstValue. This doesn't make sense to me as the reference should be exactly the same, and I've confirmed that the value is in fact changing on the VM during initialization. I've definitely initialized values in the constructor and had them respected and displayed on load, so I'm a little confused as to where I'm going wrong here.
EDIT
I've accepted mm8's answer, but had to make a few additional tweaks to the XAML to get it to behave as needed. I needed to be able to trigger the custom text based on the ID value of the items, which was set at run time. Because of this a simple DataTrigger would not work so I had to use a MultiBinding. The MultiBinding broke the display when an item was selected (as described in ComboBox.ItemTemplate not displaying selection properly) so I had to set IsEditable to false. The full combobox is below.
<ComboBox Grid.Row="2"
Grid.Column="1"
IsEditable="False"
ItemsSource="{Binding ItemSource}"
SelectedItem="{Binding SelectedValue}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text"
Value="{Binding Name}" />
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource LongEqualToLongMultiBindingDisplayConverter}">
<Binding Path="Id" />
<Binding Path="DataContext.FirstValue.Id" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MyControl}}" />
</MultiBinding>
</DataTrigger.Binding>
<Setter Property="Text"
Value="Custom Display Text 111" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This XAML in combination with the suggestions from mm8's answer (setting up a collection which is initialized at runtime from the two provided values) did the trick.
Why don't you simply expose a collection of selectable items from your view model? This is how to solve this using MVVM:
public class StaticUIVm : INotifyPropertyChanged
{
public Tuple<long, string> FirstValue { get; set; }
public Tuple<long, string> SecondValue { get; set; }
private Tuple<long, string> _selectedValue;
public Tuple<long, string> SelectedValue
{
get { return _selectedValue; }
set
{
_selectedValue = value;
OnPropertyChanged();
}
}
public IEnumerable<Tuple<long, string>> Values { get; }
public StaticUIVm()
{
FirstValue = new Tuple<long, string>(1, "Some Static Value");
SecondValue = new Tuple<long, string>(2, "Some Other Static Value");
Values = new Tuple<long, string>[2] { FirstValue, SecondValue };
SelectedValue = SecondValue;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<ComboBox x:Name="cmb" Grid.Row="1" ItemsSource="{Binding Values}"
SelectedItem="{Binding SelectedValue}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="{Binding Item2}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Item1}" Value="1">
<Setter Property="Text" Value="Custom Display Text 111" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You might even remove the FirstValue and SecondValue properties. The custom text is defined in the view but the actual options to choose from is defined in the view model.
MAJOR EDITS:
So when I first added an answer, I was trying to use what you already had instead of demonstrating how I would do it. WPF is both flexible and constrictive in certain ways and I have often found myself working around problems. Your question is actually quite simple when done using a different approach. Many of my programs have ComboBox controls and although I normally populate with a collection, a similar principle can be applied by using a helper data class over a Tuple. This will add significantly more flexibility and be more robust.
I added the property SelectedIndex and bound it to an int in your datacontext class. I also changed SelectedValue to SelectedItem as it it far superior in this use case.
<ComboBox Grid.Row="1"
SelectedValuePath="Tag"
SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
SelectedIndex="{Binding SelectedIndex, Mode=TwoWay}">
<ComboBox.Items>
<ComboBoxItem Content="{Binding FirstValue.display}"
Tag="{Binding FirstValue}" />
<ComboBoxItem Content="{Binding SecondValue.display}"
Tag="{Binding SecondValue}" />
</ComboBox.Items>
</ComboBox>
Datacontext Class, Data Class, and Extension Method:
So, I moved your property changed event over to a separate class. I recommend doing this as it makes it reusable. It is especially handy for the Data Class. Now in the constructor we set the selected item AND the selected index.
public class StaticUIVm : PropertyChangeHelper
{
private ComboBoxDataType _FirstValue;
public ComboBoxDataType FirstValue
{
get { return _FirstValue; }
set
{
_FirstValue = value;
OnPropertyChanged();
}
}
private ComboBoxDataType _SecondValue { get; set; }
public ComboBoxDataType SecondValue
{
get { return _SecondValue; }
set
{
_SecondValue = value;
OnPropertyChanged();
}
}
private ComboBoxDataType _SelectedItem;
public ComboBoxDataType SelectedItem
{
get { return _SelectedItem; }
set
{
_SelectedItem = value;
OnPropertyChanged();
}
}
private int _SelectedIndex;
public int SelectedIndex
{
get { return _SelectedIndex; }
set
{
_SelectedIndex = value;
OnPropertyChanged();
}
}
public StaticUIVm(string dynamicName)
{
FirstValue = new ComboBoxDataType() { id = 1, data = "Some Static Value", display = "Custom Display Text 111", };
SecondValue = new ComboBoxDataType() { id = 2, data = dynamicName, display = dynamicName, };
SelectedItem = FirstValue;
SelectedIndex = 0;
}
}
public class ComboBoxDataType : PropertyChangeHelper
{
private long _id { get; set; }
public long id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged();
}
}
private string _data { get; set; }
public string data
{
get { return _data; }
set
{
_data = value;
OnPropertyChanged();
}
}
private string _display { get; set; }
public string display
{
get { return _display; }
set
{
_display = value;
OnPropertyChanged();
}
}
}
public class PropertyChangeHelper : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The reason for all of this you might ask...well this adds for flexibility instead of "hacking" things or adding in extra complexity in the XAML with a data trigger. You are working purely off of logic using an easy to manipulate data class.

execute a function in a viewmodel and real time refresh of the view

I have a small problem with my MVVM application.
I have a function in a viewmodel which modify a collection. This collection is bind to the view to show a datagrid. When an user click on a button, the function modify the collection but it can take few minutes and the view is not refresh.
My question is how can I execute this function to have the view refreshed in real time ?
In another program I have used the dispatcher but it was in the code behind of the view without binding.
Thanks
Edit :
Model :
public class Composants : INotifyPropertyChanged
{
private string _nom;
public string Nom
{
get { return _nom; }
set { _nom = value; OnPropertyChanged("Nom"); }
}
}
ViewModel :
public class PageSynchroViewModel : INotifyPropertyChanged
{
public void SynchroniserComposants()
{
foreach (var comp in _selectedVersion.ListeComposants)
{
comp.Nom = "";
}
}
View (I don't put all the code):
<Page x:Class="Centre_de_synchronisation.Vues.PageSynchro"
[...]
xmlns:app="clr-namespace:Centre_de_synchronisation.Classes" mc:Ignorable="d"
d:DesignHeight="531" d:DesignWidth="778"
Title="PageSynchro" Background="{x:Null}">
<Canvas>
[...]
<DataGrid Name="GridComposants" Style="{StaticResource DatagridStyle}" ItemsSource="{Binding ListeComposants}" AutoGenerateColumns="False" Canvas.Left="12" Canvas.Top="201" Height="285" Width="754" >
<DataGrid.Columns>
<DataGridTextColumn
Header="Nom"
Binding="{Binding Nom}"
Width="150"
IsReadOnly="True"/>
[...]
</DataGrid>
<Button Name="BoutonSynchro" Style="{StaticResource MessageBoxButtonStyle}" Content="Synchroniser" Height="27" Width="107" Command="{Binding BoutonSynchro}" CommandParameter="GridComposants" Visibility="{Binding Etat, Converter={StaticResource VisibilityConverter}}"/>
</Canvas>
Try using an ObservableCollection<T> instead of the collection you are using now.
This should cause the View to be updated whenever an Item is added or removed from the collection.
Just remember when interacted with the ObservableCollection to Invoke the Dispatcher otherwise you will get Thread Access Exceptions
Here is the code I did to test this.
XAML
<Window.Resources>
<loc:MyViewModel x:Key="ViewModel" />
</Window.Resources>
<Canvas DataContext="{StaticResource ViewModel}">
<DataGrid ItemsSource="{Binding Collection}"
Width="150"
Height="200">
<DataGrid.Columns>
<DataGridTextColumn Header="Nom"
Binding="{Binding Nom}"
Width="150"
IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
<Button Command="{Binding DoStuffCommand}"
Canvas.Bottom="0"
Canvas.Right="0">Stuff</Button>
</Canvas>
ViewModel
public class MyViewModel
{
public ObservableCollection<MyModel> Collection { get; set; }
public ICommand DoStuffCommand { get; set; }
public MyViewModel()
{
this.Collection = new ObservableCollection<MyModel>();
for (int i = 0; i < 10; i++)
{
Collection.Add(new MyModel { Nom = i.ToString() });
}
this.DoStuffCommand = new RelayCommand(DoStuff);
}
private void DoStuff()
{
foreach (var item in Collection)
{
item.Nom = item.Nom + ".";
}
}
}
Model
public class MyModel : INotifyPropertyChanged
{
private string nom;
public string Nom
{
get { return nom; }
set
{
nom = value;
RaiseChanged("Nom");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaiseChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
It updated the Nom in the view.

ListBox SelectedItem Binding Broken

I am using WPF having a strange issue with RadListBox SelectedItem databinding, trying to figure out but no luck. Following is my scenario
I am using Telerik Controls (RadListBox, and RadButton)
RadButton is placed inside a ItemsControl, RadListBox and ItemsControl are bind to same ItemsSource.
I am using PRISM and MVVM.
What I want is when I click on button, the same item is selected from RadListBox automatically, (This part working fine).
Problem: As soon as I click on any item of RadListBox and then click back on any button the item selection stops working.
Edit: I tried the same thing with standard WPF ListBox by adding attached behavior for selection changed event and attached property of Command and CommandParameter, it works fine, so it looks like an issue with Telerik RadListBox ?
Now let me come to code.
ViewModel Class
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public DelegateCommand<object> StudentSelected { get; set; }
public DelegateCommand<object> ButtonPressed { get; set; }
private void OnStudentSelected(object par)
{
//Debugger.Break();
if (handled == false)
{
Student std = par as Student;
if (std != null)
{
SelectedStudent = std;
}
}
handled = false;
}
private void OnButtonPressed(object par)
{
//Debugger.Break();
handled = true;
String std = par as String;
if (std != null)
{
foreach (Student st in _students)
{
if (st.Name.Equals(std))
{
SelectedStudent = st;
break;
}
}
}
}
private Student _selectedstudent;
private bool handled = false;
public MainViewModel()
{
StudentSelected = new DelegateCommand<object>(OnStudentSelected);
ButtonPressed = new DelegateCommand<object>(OnButtonPressed);
}
public Student SelectedStudent
{
get
{
return _selectedstudent;
}
set
{
_selectedstudent = value;
OnPropertyChanged("SelectedStudent");
}
}
private ObservableCollection<Student> _students;
public ObservableCollection<Student> Students
{
get
{
return _students;
}
set
{
_students = value;
OnPropertyChanged("Students");
}
}
}
public class Student
{
public String Name { get; set; }
public String School { get; set; }
}
MainView XAML
<telerik:RadListBox Grid.Column="0" Grid.Row="0" ItemsSource="{Binding Students}" Command="{Binding StudentSelected}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=SelectedItem}" SelectedItem="{Binding SelectedStudent, Converter={StaticResource DebugConverter}}">
<!-- The above debug converter is just for testing binding, as long as I keep on clicking button the Converter is being called, but the moment I click on RadListBoxItem the Converter is not called anymore, even when I click back on buttons -->
<telerik:RadListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</telerik:RadListBox.ItemTemplate>
</telerik:RadListBox>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding SelectedStudent.Name}"></Label>
<StackPanel Grid.Column="1" Grid.Row="1" Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding Students}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<telerik:RadButton Width="100" Height="70" Content="{Binding Name}" Command="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}, Path=DataContext.ButtonPressed}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=Content}">
</telerik:RadButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
and finally populating the ViewModel and setting the Datacontext
MainViewModel mvm = new MainViewModel();
ObservableCollection<Student> students = new ObservableCollection<Student>();
students.Add(new Student { Name = "Student 1", School = "Student 1 School" });
students.Add(new Student { Name = "Student 2", School = "Student 2 School" });
students.Add(new Student { Name = "Student 3", School = "Student 3 School" });
mvm.Students = students;
//Bind datacontext
this.DataContext = mvm;
Please give your suggestions and share you expertise from WPF Jargon?
Finally I figured out the issue, I just need to replace the RadListBox SelectedItem binding to TwoWay
<telerik:RadListBox Grid.Column="0" Grid.Row="0" ItemsSource="{Binding Students}" Command="{Binding StudentSelected}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=Self}, Path=SelectedItem}" SelectedItem="{Binding SelectedStudent, Mode,TwoWay, Converter={StaticResource DebugConverter}}">

ItemsControl that contains bound ComboBox in ItemTemplate

I've just stuck in a problem to bind collection in ItemsControl with ItemTeplate that contains bounded ComboBox.
In my scenario I need to "generate" form that includes textbox and combobox for each item in collection and let user to update items. I could use DataGrid for that but I'd like to see all rows in edit mode, so I use ItemsControl with custom ItemTemplate.
It's ok to edit textboxes but when you try to change any ComboBox, all other ComboBoxes in other rows will change too.
Is it a bug or feature?
Thanks, Ondrej
Window.xaml
<Window x:Class="ComboInItemsControlSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="480" Width="640">
<Window.Resources>
<CollectionViewSource x:Key="cvsComboSource"
Source="{Binding Path=AvailableItemTypes}" />
<DataTemplate x:Key="ItemTemplate">
<Border BorderBrush="Black" BorderThickness="0.5" Margin="2">
<Grid Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="20" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding Path=ItemValue}" />
<ComboBox Grid.Column="2"
SelectedValue="{Binding Path=ItemType}"
ItemsSource="{Binding Source={StaticResource cvsComboSource}}"
DisplayMemberPath="Name"
SelectedValuePath="Value" />
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Path=SampleItems}"
ItemTemplate="{StaticResource ItemTemplate}"
Margin="10" />
</Grid>
Window.xaml.cs
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel
{
public ViewModel()
{
SampleItems = new List<SampleItem> {
new SampleItem { ItemValue = "Value 1" },
new SampleItem { ItemValue = "Value 2" },
new SampleItem { ItemValue = "Value 3" }
};
AvailableItemTypes = new List<SampleItemType> {
new SampleItemType { Name = "Type 1", Value = 1 },
new SampleItemType { Name = "Type 2", Value = 2 },
new SampleItemType { Name = "Type 3", Value = 3 },
new SampleItemType { Name = "Type 4", Value = 4 }
};
}
public IList<SampleItem> SampleItems { get; private set; }
public IList<SampleItemType> AvailableItemTypes { get; private set; }
}
public class SampleItem : ObservableObject
{
private string _itemValue;
private int _itemType;
public string ItemValue
{
get { return _itemValue; }
set { _itemValue = value; RaisePropertyChanged("ItemValue"); }
}
public int ItemType
{
get { return _itemType; }
set { _itemType = value; RaisePropertyChanged("ItemType"); }
}
}
public class SampleItemType : ObservableObject
{
private string _name;
private int _value;
public string Name
{
get { return _name; }
set { _name = value; RaisePropertyChanged("Name"); }
}
public int Value
{
get { return _value; }
set { _value = value; RaisePropertyChanged("Value"); }
}
}
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName) {
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Picture
here you can see the result on picture
I believe it's because you're binding to a CollectionViewSource, which tracks the current item. Try binding directly to your list instead, which won't track the current item
<ComboBox Grid.Column="2"
SelectedValue="{Binding Path=ItemType}"
DisplayMemberPath="Name"
SelectedValuePath="Value"
ItemsSource="{Binding RelativeSource={
RelativeSource AncestorType={x:Type ItemsControl}},
Path=DataContext.AvailableItemTypes}" />
While you have a combobox in each row, it doesnt see these comboboxes as being seperate. i.e. They are all using the same collection, and the same selectedValue, so when a value changes in one box, it changes in all of them.
The best way to fix this is to add the SampleItemType collection as a property on your SampleItem model and to then bind the combo box to that property.

Resources