I have a grid with a collection of "ItemPresupusto". I need to add a NumericUpDown (by mahApps) to be able to modify the "Cantidad" property of each "ItemPresupuesto" and every time I modify that property, I need to update data in the UI. I've tried everything, but I can not do it. Im use MVVM Light Some help. Thank you!
.XAML
<DataGrid IsReadOnly="True"
SelectionUnit="FullRow"
AutoGenerateColumns="False"
GridLinesVisibility="Horizontal"
ItemsSource="{Binding Articulos}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Cantidad" MinWidth="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<mahApps:NumericUpDown Minimum="1"
IsTabStop="False"
Value="{Binding Cantidad, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
ViewModel
public class PresupuestosViewModel : ViewModelBase
{
public IEnumerable<ItemPresupuesto> Articulos => new ObservableCollection<ItemPresupuesto>(Presupuesto.Items);
}
Class
public class ItemPresupuesto: EntidadBase
{
public decimal Cantidad { get; set; }
}
public class Presupuesto : EntidadBase
{
public virtual List<ItemPresupuesto> Items { get; }
}
The ItemPresupuesto class should implement the INotifyPropertyChanged and interface and raise change notifications for the source property that is bound to the control that you want to refresh whenever the Cantidad or Prico properties are set:
public class ItemPresupuesto : INotifyPropertyChanged
{
private decimal _cantidad;
public decimal Cantidad
{
get { return _cantidad; }
set { _cantidad = value; NotifyPropertyChanged(); NotifyPropertyChanged(nameof(Total)); }
}
private decimal _prico = 1;
public decimal Prico
{
get { return _prico; }
set { _prico = value; NotifyPropertyChanged(); NotifyPropertyChanged(nameof(Total)); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public decimal Total => _prico * _cantidad;
}
Related
I have just started learning WPF yesterday and my goal is to create window with simple grid with hotel booking information. For now there are just room number, number of guests, dates and "Action" columns. In the "Actions" column there is "Save" button. It should be able to save updates or create new booking when clicked in new row. The problem is when I click "Save" button SaveBooking method is not invoked. I'm also not sure how to properly bind to CurrentBooking object. As I am new to WPF I tried to figure it out from few tutorials. Here's what I've created.
XAML:
<Window x:Class="HotelApp.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:HotelApp"
mc:Ignorable="d"
Title="MainWindow" Height="800" Width="1000">
<Grid>
<TabControl>
<TabItem Header="Bookings">
<DataGrid AutoGenerateColumns = "False" ItemsSource="{Binding Bookings}">
<DataGrid.Columns>
<DataGridTextColumn Header = "Room" Binding = "{Binding Room, Mode=TwoWay}" />
<DataGridTextColumn Header = "Floor" Binding = "{Binding NumOfGuests, Mode=TwoWay}" />
<DataGridTextColumn Header = "From" Binding = "{Binding From, Mode=TwoWay}"/>
<DataGridTextColumn Header = "To" Binding = "{Binding To, Mode=TwoWay}"/>
<DataGridTemplateColumn Header = "Actions">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Save" Command="{Binding DataContext.SaveBookingCommand }" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</TabItem>
<TabItem Header="Guests" />
</TabControl>
</Grid>
</Window>
MODEL:
public class BookingModel : ObservableObject
{
private int _room;
public int Room
{
get => _room;
set
{
if (value != _room)
{
_room = value;
OnPropertyChanged("Room");
}
}
}
private int _numOfGuests;
public int NumOfGuests
{
get => _numOfGuests;
set
{
_numOfGuests = value;
OnPropertyChanged("NumOfGuests");
}
}
private DateTime _from;
public DateTime From
{
get => _from;
set
{
_from = value;
OnPropertyChanged("From");
}
}
private DateTime _to;
public DateTime To
{
get => _to;
set
{
_to = value;
OnPropertyChanged("To");
}
}
}
VIEWMODEL:
public class MainWindowVM : ObservableObject
{
private readonly IBookingService _bookingService;
private ICommand _saveBookingCommand;
public ICommand SaveBookingCommand
{
get
{
if (_saveBookingCommand == null)
{
_saveBookingCommand = new RelayCommand(
param => SaveBooking(),
param => (CurrentBooking != null)
);
}
return _saveBookingCommand;
}
}
private ObservableCollection<BookingModel> _Bookings { get; set; }
private BookingModel _currentBookng;
public BookingModel CurrentBooking
{
get { return _currentBookng; }
set
{
if (value != _currentBookng)
{
_currentBookng = value;
OnPropertyChanged("CurrentBooking");
}
}
}
public ObservableCollection<BookingModel> Bookings
{
get { return _Bookings; }
set { _Bookings = value; }
}
public MainWindowVM(IBookingService bookingService)
{
_bookingService = bookingService;
BrowseBookings();
}
public void BrowseBookings()
{
var bookings = _bookingService.Browse().Select(x => new BookingModel { Room = x.Room.RoomId, NumOfGuests = x.NumOfGuests, From = x.From, To = x.To });
Bookings = new ObservableCollection<BookingModel>(bookings);
}
private void SaveBooking()
{
// send CurrentBooking to service
}
}
RelayCommand:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
return _canExecute == null ? true : _canExecute(parameters);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
_execute(parameters);
}
#endregion // ICommand Members
}
Your command is in the datacontext of the entire datagrid MainWindowVM.
Your button's datacontext is that of the row - a BookingModel.
You need some relativesource on that binding.
In principle that looks like this:
{Binding DataContext.ParentVMProperty,
RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
And your type, in this case, will be DataGrid.
You can also bind selecteditem on the datagrid and when they click the button ensure that is selected using the datagrid properties for selection.
Or
You can have a commandparameter on the command which is
CommandParameter="{Binding .}"
Relaycommand usually comes in two flavours one being RelayCommand
Maybe I missed it but I don't see that in your implementation. I'd suggest you go grab the source code for MVVM Light and paste into your solution for a more complete implementation. Or just add the nuget package if you're not using .net core. You want the commandwpf namespace version of relaycommand.
You left out a lot of code, so I don't know which nuget package you used for your ObservableObject. Anywho, I faked the ObservableObject and got the binding working. The main problem was that you were trying to bind SaveBookingCommand at the BookingModel level, when in your code you have it written in the MainWindowVM level.
You can easily fix this by parenting your MainWindowVM in your BookingModel, and change your binding to be Command={Binding Parent.SaveBookingCommand}.
Here's some pointers to the edits that I made:
MainWindow.xaml.cs:
<DataTemplate>
<Button Content="Save" Command="{Binding Parent.SaveBookingCommand}" />
</DataTemplate>
BookingModel.cs:
public class BookingModel : ObservableObject
{
public MainWindowVM Parent { get; private set; }
public BookingModel()
{
this.Parent = null;
}
public BookingModel(MainWindowVM parent)
{
this.Parent = parent;
}
// ... you know the rest
MainWindowVM.cs:
public MainWindowVM : ObservableObject
{
public void BrowseBookings()
{
// NOTICE that I added 'this' as the parameter argument to connect MainWindowVM to the BookingModel.
var bookings = _bookingService.Browse().Select(x => new BookingModel(this) { Room = x.Room, NumOfGuests = x.NumOfGuests, From = x.From, To = x.To });
Bookings = new ObservableCollection<BookingModel>(bookings);
CurrentBooking = Bookings.First();
}
// ... you know the rest
I'm making a ListView filled with List of objects, which properties are shown and editable in a ListView. I need to get object when its properties are being updated. How can I do this?
I tried creating an object of class and bind it to SelectedItem in ListView. The problem is that, obviously, the SelectedItem is set after clicking the row of ListItem, but not the children of that row. I need to get the updated object from the row of my ListView each time after any ComboBox or TextBox values are changed.
To handle all the things with INotifyPropertyChanged I'm using PropertyChanged.Fody. Could it help me to solve this problem easier?
View
Appearance of the ListView
<ListView
Margin="10"
Grid.Row="1"
Grid.ColumnSpan="2"
ItemsSource="{Binding TimesheetEntries}"
SelectedItem="{Binding SelectedEntry, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="30" Margin="3">
<TextBlock
Text="{Binding Date, StringFormat=dd-MM-yyyy}"
VerticalAlignment="Center"
Width="Auto"
Margin="10"/>
<ComboBox
SelectedValuePath="Key" DisplayMemberPath="Value"
ItemsSource="{Binding EmploymentTypesDictionary, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValue="{Binding SelectedEmployment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Width="270"/>
<TextBox
Text="{Binding Hours, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="10,0,0,0"
Width="70"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel
public List<TimesheetEntryEntity> TimesheetEntries
{
get { return _TimesheetEntries; }
set { _TimesheetEntries = value; }
}
public TimesheetEntryEntity SelectedEntry
{
get { return _SelectedEntry; }
set { _SelectedEntry = value; }
}
...
private List<TimesheetEntryEntity> _TimesheetEntries { get; set; }
private TimesheetEntryEntity _SelectedEntry;
private TimesheetModel timesheetModel;
public TimesheetViewModel()
{
this.Timesheets = TimesheetUnitModel.GetAllTimesheetsForUnit((int)Application.Current.Properties["UnitID"]);
this._StartDate = DateTime.Now;
_TimesheetEntries = new List<TimesheetEntryEntity>();
}
public KeyValuePair<int, string> SelectedWorker
{
get { return _SelectedWorker; }
set
{
_SelectedWorker = value;
_TimesheetEntries =
timesheetModel.GetTimesheetList(_SelectedWorker.Key, SelectedTimesheet.Key, StartDate.Date);
}
}
TimesheetEntryEntity
public DateTime Date { get; set; }
public Dictionary<EmploymentTypes, string> EmploymentTypesDictionary { get; set; }
public EmploymentTypes SelectedEmployment {
get { return _SelectedEmployment; }
set
{
_SelectedEmployment = value;
CheckHoursAvaliability();
}
}
public bool HoursAvaliable { get; set; }
public decimal Hours
{
get;
set;
}
private EmploymentTypes _SelectedEmployment;
public TimesheetEntryEntity()
{
FillEmploymentTypes();
}
public void FillEmploymentTypes()
{
//Some code here
}
I tried to follow the answer from Get Object properties of selected list item question, but there were only textblocks, so the row gets selected anyway, but i have ComboBox and TextBox, who get their own focus.
You can implement INotifyPropertyChanged in your TimesheetEntryEntity i.e.
public abstract class TimesheetEntryEntity: INotifyPropertyChanged
{
public event EventHandler Changed;
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void OnChange()
{
EventHandler handler = Changed;
handler?.Invoke(this, EventArgs.Empty);
}
private DateTime date;
public DateTime Date
{
get => date;
set
{
if (date == value)
{
return;
}
//Do something with unchanged property
date = value;
RaisePropertyChanged();
OnChange();
//Do something with changed property
}
}
in your ViewModel before adding new item to list:
timesheet.Changed+=ItemChanged;
and
private void ItemChanged(object sender, EventArgs e)
{
var item=sender as TimesheetEntryEntity;
//do something
}
I have a datagrid with three columns as shown below:
Datagrid is populated via an observable collection.
View model is defined as:
public class PlanningResult : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public double A { get; set; }
public double B { get; set; }
public double D { get; set; }
}
ObservableCollection<PlanningResult> populatePatternData = new ObservableCollection<PlanningResult>();
public ObservableCollection<PlanningResult> PopulatePatternData
{
get { return populatePatternData; }
set
{
populatePatternData = value;
base.OnPropertyChanged("StringList");
}
}
In the datagrid, user is allowed to modify cell values of column A. I am able to achieve that from UI but how do I reflect the cell value change in column D when cell value in A changes?
Here is the XAML:
<DataGrid x:Name="PrintReport" ItemsSource="{Binding PopulatePatternData}" AutoGenerateColumns="False">
<DataGridTextColumn Header="A" Binding="{Binding A,StringFormat={}{0:0.00}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="False" />
<DataGridTextColumn Header="B" Binding="{Binding B,StringFormat={}{0:0.00}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True" />
<DataGridTextColumn Header="D" Binding="{Binding D,StringFormat={}{0:0.00}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsReadOnly="True" />
</DataGrid>
Raise the PropertyChanged event for the D property in the setters of the A and B properties:
public class PlanningResult : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private double _a;
public double A
{
get { return _a; }
set { _a = value; NotifyPropertyChanged(); NotifyPropertyChanged("D"); }
}
private double _b;
public double B
{
get { return _b; }
set { _b = value; NotifyPropertyChanged(); NotifyPropertyChanged("D"); }
}
public double D { get { return _a + _b; } }
}
Also note that the Mode of the read-only properties should be OneWay which is the default value:
<DataGridTextColumn Header="D" Binding="{Binding D,StringFormat={}{0:0.00}}" IsReadOnly="True" />
I have a generic pattern for this situation:
you define the formula component with normal properties as usual, and in the dependent property - in your case D = A + B you'll write only the getter.
Then you take each component of the formula - in this case A and B - and you go to their setters and simply add the OnPropertyChanged of the formula - D - after the OnPropertyChangedof its components - A and B.
That's because the formula changes if and only if its components do: that explains where the notifications must be sent from.
I'm binding a DataGrid to a List, which has a boolen property "IsSelected".
public partial class MainWindow : Window
{
List<BaselineEntity> _blRecs;
public MainWindow()
{
InitializeComponent();
_blRecs = new List<BaselineEntity>();
_blRecs.Add(new BaselineEntity{EntityId = "Yada_Yada_1", IsSelected = false});
_blRecs.Add(new BaselineEntity{EntityId = "Yada_Yada_2", IsSelected = false});
_blRecs.Add(new BaselineEntity{EntityId = "Yada_Yada_2", IsSelected = false});
ChangedBlRecsGridView.ItemsSource = _blRecs;
}
}
Where the class BaselineEntity is defined as:
internal class BaselineEntity
{
public string EntityId {get; set;}
public bool IsSelected { get; set; }
}
The XAML is this
<DataGrid Name="ChangedBlRecsGridView"
ItemsSource="{Binding _blRecs, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Entity Id" Binding="{Binding EntityId}"></DataGridTextColumn>
<DataGridTemplateColumn Width="30">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.Header>
<CheckBox Name="chkSelectAll" Checked="chkSelectAll_Checked" Unchecked="chkSelectAll_Unchecked"></CheckBox>
</DataGridTemplateColumn.Header>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Where the event handlers for the checked and Unchecked events of the Select-All checkbox are like this:
private void chkSelectAll_Checked(object sender, RoutedEventArgs e)
{
foreach (BaselineEntity enty in ChangedBlRecsGridView.ItemsSource)
{
enty.IsSelected = true;
}
}
private void chkSelectAll_Unchecked(object sender, RoutedEventArgs e)
{
foreach (BaselineEntity enty in ChangedBlRecsGridView.ItemsSource)
{
enty.IsSelected = false;
}
}
I'm familiar with MVVM and I know I haven't stuck to any best practices (like implementing a viewModel), cuz I'm just getting started with WPF.
The Check and Uncheck event handlers set and reset the IsSelected Property, but the view is NOT getting updated about this change. It looks like the intended 2-way binding isn't happening somehow.
My guess is that I should have implemented the BaselineEntity class with some interface to push notifications to the view (This is what I'd expect from my somewhat working knowledge on knockout js, but couldn't find what it is after spending hours).
I don't know whatelse I'm missing here.. I just could NOT find what could be the solution.
Any help/guidance is appreciated..
Thank you!
Refer the below model code with INotifyPropertyChanged
internal class BaselineEntity:INotifyPropertyChanged
{
public string EntityId { get; set; }
private bool isSelected;
public bool IsSelected
{
get { return isSelected; }
set { isSelected = value; OnPropertyChanged("IsSelected"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
I'm binding DataGridCoulmun's DisplayIndex to my ViewModel. Since DataGridColumns doesn't belong to DataGrid's visual or lgical tree, i had to do some some tricks to achieve that binding, but it works.
My problem is: When DataContext is changed (if we have more ViewModels), DataGridColumns shoud get new DisplayIndexes. Unfortunately the behaviour is strange and after the change, the column order is more or less random.
Do you have any idea how to handle this problem or at least what is the cause?
Here is a example:
Before initializing the datagrid I set the DataContext to the new instance of the ViewModel and it works as it should. After that i reorder the columns and it still works and the changes are propagated to the ViewModel correctly. Finally I click the button, which set the DataContext to the new instance of the ViewModel, so the columns after click should be ordered as at the beginning.
Here is a XAML code:
<Window x:Class="TestDataGridBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestDataGridBinding"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn
Header="A"
Binding="{Binding A}"
DisplayIndex="{Binding Path=Data.ADisplayIndex, FallbackValue=0, Mode=TwoWay, Source={StaticResource proxy}}"/>
<DataGridTextColumn
Header="B"
Binding="{Binding B}"
DisplayIndex="{Binding Path=Data.BDisplayIndex, FallbackValue=1, Mode=TwoWay, Source={StaticResource proxy}}"/>
<DataGridTextColumn
Header="C"
Binding="{Binding C}"
DisplayIndex="{Binding Path=Data.CDisplayIndex, FallbackValue=2, Mode=TwoWay, Source={StaticResource proxy}}"/>
<DataGridTextColumn
Header="D"
Binding="{Binding D}"
DisplayIndex="{Binding Path=Data.DDisplayIndex, FallbackValue=3, Mode=TwoWay, Source={StaticResource proxy}}"/>
</DataGrid.Columns>
</DataGrid>
<Button Click="Button_Click" Width="70" Height="30" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="10" Content="Click me"/>
</Grid>
</Window>
Here is a Code-behind
public partial class MainWindow : Window
{
ViewModel _model;
public MainWindow()
{
_model = new ViewModel();
_model.Items.Add(new Item("x","y","z","zz"));
DataContext = _model;
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_model = new ViewModel();
_model.Items.Add(new Item("xx", "y", "zz", "zzz"));
DataContext = _model;
}
}
[Serializable]
public class ViewModel : INotifyPropertyChanged
{
#region notifications
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
#endregion
#region private and default values
private int _a = 3;
private int _b = 2;
private int _c = 1;
private int _d = 0;
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
#endregion
#region public
public int ADisplayIndex { get { return _a; } set { _a = value; NotifyPropertyChanged("ADisplayIndex"); } }
public int BDisplayIndex { get { return _b; } set { _b = value; NotifyPropertyChanged("BDisplayIndex"); } }
public int CDisplayIndex { get { return _c; } set { _c = value; NotifyPropertyChanged("CDisplayIndex"); } }
public int DDisplayIndex { get { return _d; } set { _d = value; NotifyPropertyChanged("DDisplayIndex"); } }
public ObservableCollection<Item> Items
{ get { return _items; } set { _items = value; NotifyPropertyChanged("Items"); } }
#endregion
}
public class Item
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
public string D { get; set; }
public Item(string a, string b, string c, string d)
{
A = a; B = b; C = c; D = d;
}
}
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
Trick with proxy was found here:
http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
The DataGrid automatically renumbers the DisplayIndex of all the other columns when you adjust the first.
E.g. if you set your second column to DisplayIndex = 0, it will give the first column DisplayIndex = 1.
See UpdateDisplayIndexForChangedColumn() in https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/DataGridColumnCollection.cs
This is going to play havoc with your bindings unless you change them in ascending order.
You'd need some kind of custom NotifyPropertyChanged logic which sets all of the new desired index values on your model first, without raising NotifyPropertyChanged, then raises NotifyPropertyChanged events in ascending numerical order.
eg.
_aDisplayIndex = 2;
_bDisplayIndex = 1;
_cDisplayIndex = 0;
_dDisplayIndex = 3;
NotifyPropertyChanged("CDisplayIndex");
NotifyPropertyChanged("BDisplayIndex");
NotifyPropertyChanged("ADisplayIndex");
NotifyPropertyChanged("DDisplayIndex");
Also watch out for two-way binding, in this case you might want to re-write the values of _*DisplayIndex between each NotifyPropertyChanged call.