I'm calculating the logarithmic slopes between the points (that the user has inserted):
For that I'm using two DataGrid; first displays the coordinates, and the second displays the slopes. I'm using a pair of DataGrid because I need to update the curve distinguishing what has been changed, the coordinates or the slope.
I would like to display better the values of the slopes: rather that being displayed in the same line as the points, they should be displayed in between the points, as shown in the second figure.
I don't know a way of doing this in WPF, maybe setting a greater value to Height of the header of the second DataGrid, so all rows will be displaced a little (approx 5px), but I can't find a way of doing that.
Could someone guide me to achieve this?
The DataGrid is defined in a pretty standard way:
<DataGrid x:Name="gridSlope" ItemsSource="{Binding Points, Mode=TwoWay}"
AutoGenerateColumns="False" Width="100"
Background="#19B0C4DE" BorderThickness="1"
BorderBrush="#19D3D3D3" CanUserResizeColumns="False"
CanUserResizeRows="False" CanUserSortColumns="False"
ClipboardCopyMode="IncludeHeader"
CellEditEnding="s1GridPendiente_CellEditEnding">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=Slope, StringFormat='{}{0:0.00}',
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
Header="dB/oct"/>
</DataGrid.Columns>
</DataGrid>
Here's a screenshot:
To achieve that, you can add this
<DataGrid.ColumnHeaderStyle>
<Style TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Margin" Value="0 0 0 10"></Setter>
</Style>
</DataGrid.ColumnHeaderStyle>
to your <DataGrid x:Name="gridSlope" ... before the <DataGrid.Columns>.
You can play with the margin there, or do what you want :) (and remove the bold if you don't like it ... I was just fooling around with it ...)
The following isn't perfect but it's close to what you are after. The problem with this idea is that the DataGridRow clips its content so you can't move the 3rd column as far down as would be ideal.
<DataGrid ItemsSource="{Binding Data}" AutoGenerateColumns="False" GridLinesVisibility="None">
<DataGrid.Columns>
<DataGridTextColumn Header="A" Binding="{Binding A}"/>
<DataGridTextColumn Header="B" Binding="{Binding B}"/>
<DataGridTemplateColumn Header="C" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=C}" Margin="0,5,0,-2"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
public class DataClass
{
public int A { get; set; }
public int B { get; set; }
public String C { get; set; }
public DataClass(int a, int b, String c)
{
A = a;
B = b;
C = c;
}
}
public List<DataClass> Data { get; set; }
public MainWindow()
{
Data = new List<DataClass>() { new DataClass(1, 2, "3"), new DataClass(2, 3, "4"), new DataClass(3, 4, "5"), new DataClass(4, 5, "") };
DataContext = this;
InitializeComponent();
}
Related
When I modify values via DataContext to set data for CheckBox, why does it return original value whem scrolling down or scrolling up? Is there any way to recover?
When I scroll down or up I expect it to not return the checkboxes to their original values.
XAML Code
<DataGrid x:Name="list_account_instagram"
Background="#DDDDDD"
CanUserResizeColumns="False"
CanUserSortColumns="False"
CanUserAddRows="False">
<DataGrid.Columns>
<materialDesign:DataGridTextColumn Width="0.4*"
Binding="{Binding STT}"
Header="#" />
<DataGridCheckBoxColumn Width="0.35*" Binding="{Binding CHECK, Mode=OneTime}">
<DataGridCheckBoxColumn.Header>
<Border Background="Transparent">
<CheckBox Name="checkallins" Click="Checkallins_Click"/>
</Border>
</DataGridCheckBoxColumn.Header>
</DataGridCheckBoxColumn>
<materialDesign:DataGridTextColumn Binding="{Binding UID}" Width="*"
Header="UID" />
<materialDesign:DataGridTextColumn Binding="{Binding PASSWORD}" Width="*"
Header="Password"/>
<materialDesign:DataGridTextColumn Binding="{Binding COOKIE}" Width="*"
Header="Cookie"/>
<materialDesign:DataGridTextColumn Binding="{Binding TEN}" Width="*"
Header="Tên"/>
<materialDesign:DataGridTextColumn Binding="{Binding AVATAR}" Width="*"
Header="Avatar"/>
<materialDesign:DataGridTextColumn Binding="{Binding TT}" Width="*"
Header="Tình Trạng"/>
<materialDesign:DataGridTextColumn Binding="{Binding MESSAGE}" Width="*"
Header="Trạng Thái"/>
</DataGrid.Columns>
</DataGrid>
C#
private void Checkallins_Click(object sender, RoutedEventArgs e)
{
if (checkallins.IsChecked.Value)
{
if (list_account_instagram.Items.Count > 0)
{
for (int i = 0; i < list_account_instagram.Items.Count; i++)
{
GetCell(list_account_instagram, i, 1).DataContext = new { CHECK = true };
}
}
}
else
{
if (list_account_instagram.Items.Count > 0)
{
for (int i = 0; i < list_account_instagram.Items.Count; i++)
{
GetCell(list_account_instagram, i, 1).DataContext = null;
GetCell(list_account_instagram, i, 1).DataContext = new { CHECK = false };
}
}
}
}
To better describe the problematic behavior, there is video: https://files.fm/u/2xxgqrqra#/view/p4jw246e3
Issue is related to using OneTime binding. This will cause that data are only loaded during initialization, but after any change (either in UI or from the code), it won't affect the collection itself.
This is causing the problem when you see different things in the "UI" and different things in the "code-behind". If you would debug actual values (when pressing header of DataGrid), you would be surprised where those values would come from.
Fix is pretty simple, you must update the Binding mode. Instead of this:
Binding="{Binding CHECK, Mode=OneTime}">
You should use:
Binding="{Binding CHECK}">
<!-- or this -->
Binding="{Binding CHECK, Mode=TwoWay}">
EDIT: from comments of OP it looks that the problem is also setting the bool value for the items in the DataGrid. You should not be assigning new objects there, instead you should be working with the DataContext of the DataGrid. You can achieve this by:
var isChecked = checkallins.IsChecked.Value;
foreach(var item in list_account_instagram.Items)
{
// Put instead of `MyClass` actual class you are using (prob. `InstagramAccount`)
var myItem = item as MyClass;
if(item != null)
{
myItem.CHECK = isChecked;
}
}
I was not able to use the answers from other similar questions. I feel I'm close to a solution, but struggle on one or two points.
I need a ComboBoxColumn in my DataGrid.
the ComboBoxColumn shows defaultly the place where an item is stocked right now.
I should be able to change the place with the ComboBox.
Here is the DataGrid with the ComboBox included:
<DataGrid x:Name="goederenZoekenDataGrid"
AutoGenerateColumns="False"
EnableRowVirtualization="True"
ItemsSource="{Binding}"
Margin="10,315,10,120"
RowDetailsVisibilityMode="VisibleWhenSelected"
RowEditEnding="goederenZoekenDataGrid_RowEditEnding">
<DataGrid.Columns>
<DataGridTextColumn x:Name="lotnummerLeverancierColumn"
Binding="{Binding LotnummerLeverancier}"
Header="Lotnr Leverancier" Width="auto"/>
<DataGridTextColumn x:Name="grondstofomschrijvingColumn"
Binding="{Binding Grondstofomschrijving}"
Header="Grondstof" Width="auto"/>
<DataGridTextColumn x:Name="leveranciersNaamColumn"
Binding="{Binding LeveranciersNaam}"
Header="Leverancier" Width="auto"/>
<DataGridTemplateColumn x:Name="datumLeveringColumn"
Header="Datum Levering" Width="auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DatePicker SelectedDate="{Binding DatumLevering, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn x:Name="hoeveelheidGeleverdColumn"
Binding="{Binding HoeveelheidGeleverd}"
Header="Hoeveelheid IN" Width="auto"/>
<DataGridTextColumn x:Name="eenheidColumn"
Binding="{Binding Eenheid}"
Header="Eenheid" Width="auto"/>
<DataGridTextColumn x:Name="lotnummerINIDColumn"
Binding="{Binding LotnummerINID}"
Header="Lotnummer INID" Width="auto"/>
<DataGridTemplateColumn x:Name="stockINOmschrijvingColumn"
Header="Stockplaats IN" Width="auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="cmbStockINGrid"
Text="{Binding StockINOmschrijving, Mode=Twoway, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
IsEditable="True" Height="20"
SelectionChanged="cmbStockINGrid_SelectionChanged"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn x:Name="HoeveelheidStockINColumn"
Binding="{Binding HoeveelheidStockIN}"
Header="Hoeveelheid Stock" Width="auto"/>
</DataGrid.Columns>
</DataGrid>
This gives me already the result of the stock place. Good sign. But to fill the ComboBox up with the rest of all the places I must have made a mistake since no result is returned. My biggest struggle here is the fact that the ComboBox name used in the DataGridTemplateColumn.CellTemplate couldn't be used in the code behind.
private void cmbStockINGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox combostockIN = (ComboBox)sender;
stockPlaatsINViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("stockPlaatsINViewSource")));
var manager = new LotnummersDBManager();
using (var conLotnrs = manager.Getconnection())
using (var comstockin = conLotnrs.CreateCommand())
{
comstockin.CommandType = CommandType.Text;
comstockin.CommandText = "SELECT StockINOmschrijving FROM StockPlaatsIN order by StockINOmschrijving asc";
conLotnrs.Open();
using (var rdrstockin = comstockin.ExecuteReader())
{
while (rdrstockin.Read())
{
combostockIN.Items.Add(rdrstockin[0]);
}//while
} //using rdrStockPlaatsIN
}//using Conlotnrs and ComStockPlaatsIN
}// cmbStockINGrid_SelectionChanged
Result is in Can anyone help me out to fill the ComboBox with the selection from sql?
My StockPlaatsINViewsource comes from the following class:
namespace AdoGemeenschap
{
public class StockPlaatsIN : LotnummerIN
{
private Int32 stockplaatsinidValue;
private String stockinValue;
private decimal HoeveelstockinValue;
public Int32 StockPlaatsINID
{
get { return stockplaatsinidValue; }
set { stockplaatsinidValue = value; }
}
public String StockINOmschrijving
{
get { return stockinValue; }
set { stockinValue = value; }
}
public decimal HoeveelheidStockIN
{
get { return HoeveelstockinValue; }
set { HoeveelstockinValue = value; }
}
public StockPlaatsIN(Int32 stockinID, String stockbeschrijving, decimal hoeveelstockin, Int32 lotnrinid):base(lotnrinid)
{
this.StockPlaatsINID = stockinID;
this.StockINOmschrijving = stockbeschrijving;
this.StockINOmschrijving = stockbeschrijving;
this.HoeveelheidStockIN = hoeveelstockin;
}
public StockPlaatsIN(string stockbeschrijving)
{
this.StockINOmschrijving = stockbeschrijving;
}
}
PS: Other possibility that I tried was as follows but I don't even find how to use a ComboBox:
<DataGridComboBoxColumn x:Name="stockINOmschrijvingColumn"
Header="Stockplaats IN" Width="auto"
SelectedItemBinding="{Binding StockINOmschrijving, Mode=TwoWay}"
ItemsSource="{Binding Source={StaticResource stockPlaatsINViewSource}}"/>
I know this isn't necessarily specific to MahApps Metro DataGridCheckBoxColumns, but I figured that information may help someone answer my question.
I'm trying to do two things with my DataGridCheckBox Column in MahApps Metro
1. I would like to have two separate datagrids, and have the ability to return the value in the second column of whichever row is selected
For example, if I had a datagrid that looked like this:
When someone has the checkbox associated with 2 selected, and the checkbox associated with Red selected, I would like a messagebox to show "2 Red".
my .xaml
<DataGrid Name="numberGrid"
ItemsSource="{Binding Path=numberGrid}"
Grid.Row="0"
AutoGenerateColumns="False"
Width="300"
Height="auto">
<DataGrid.Columns>
<DataGridCheckBoxColumn ElementStyle="{DynamicResource MetroDataGridCheckBox}"
EditingElementStyle="{DynamicResource MetroDataGridCheckBox}"
Header="Select"
Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=IsSelected, Mode=OneWay}"
/>
<DataGridTextColumn Header="Numbers"
Binding="{Binding Number, Mode=OneWay}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
<DataGrid Name="colorGrid"
ItemsSource="{Binding Path=colorGrid}"
Grid.Row="0"
AutoGenerateColumns="False"
Width="300">
<DataGrid.Columns>
<DataGridCheckBoxColumn ElementStyle="{DynamicResource MetroDataGridCheckBox}"
EditingElementStyle="{DynamicResource MetroDataGridCheckBox}"
Header="Select"
Binding="{Binding RelativeSource={RelativeSource AncestorType=DataGridRow}, Path=IsSelected, Mode=OneWay}"
/>
<DataGridTextColumn Header="Colors"
Binding="{Binding Color, Mode=OneWay}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
my .cs with the pseudocode of what I think should happen commented out.
ObservableCollection<MahAppsNumbers> MAnumbers = new ObservableCollection<MahAppsNumbers>
{
new MahAppsNumbers{Number = "1"},
new MahAppsNumbers{Number = "2"},
new MahAppsNumbers{Number = "3"},
new MahAppsNumbers{Number = "4"}
};
public class MahAppsNumbers
{
public string Number { set; get; }
}
ObservableCollection<MahAppsAccents> MAcolors = new ObservableCollection<MahAppsColors>
{
new MahAppsColors{Color = "Red"},
new MahAppsColors{Color = "Orange"},
new MahAppsColors{Color = "Yellow"},
new MahAppsColors{Color = "Green"}
};
public class MahAppsColors
{
public string Color { set; get; }
}
public myWindow()
{
InitializeComponent();
numberGrid.ItemsSource = MAnumbers;
colorGrid.ItemsSource = MAcolors;
//Something like this maybe? I know this isn't a thing you can do
//string currentNumber = numberGrid.SelectedRow[1]; //to get the second column value
//string currentColor = colorGrid.SelectedRow[1];
//MessageBox.Show(currentNumber + currentColor);
}
2. For bonus points, why do I have an extra row at the end?
I've found TONS of solutions for this, that are all basically the same. But that doesn't solve my problem.
Thanks in advance.
Edit 1
Since I want this even to happen every time a box is selected, I'm trying something like this inside of myWindow()
this.numberGrid.SelectedCellsChanged += new SelectedCellsChangedEventHandler(numberGrid_SelectionChanged);
and adding a function that hopefully someone can point me in the right direction to fixing.
private void numberGrid_SelectionChanged(object sender, SelectedCellsChangedEventArgs e)
{
MessageBox.Show(numberGrid.SelectedItem.ToString());
}
Try Binding like this,
<DataGridCheckBoxColumn Binding="{Binding Path=IsSelected, Mode=TwoWay}" />
Now you have a Property IsSelected add this to your collection which binds your DataGrid. When you check your CheckBox IsSelected property Set will call and set it to True, now in your collection has the IsSelected True for which its checked.
I've got some nested controls that don't seem to be inheriting bindings correctly... or it could be something else entirely. I'm a bit new to WPF.
I've got an ObservableCollection of objects bound to the ListBox and at the top level this appears to be working since a row is added for each item in the collection as expected. The problem is that I need to place a DataGrid within the ListBox's ItemTemplate and...
All I get are the headers, which I specified manually. No data from the bound objects.
I've probably just done something dumb. Here's the code, slightly simplified:
XAML:
<ListBox x:Name="ReportListBox" Margin="10,10,10,10" Grid.Row="1" ItemsSource="{Binding Path=SummaryTotals, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Date, StringFormat=\{0:MM/dd/yyyy\}}" Header="Date"/>
<DataGridTextColumn Binding="{Binding GrossRevenue}" Header="Gross Revenue" />
<DataGridTextColumn Binding="{Binding OurRevenue}" Header="Our Revenue" />
<DataGridTextColumn Binding="{Binding Cost}" Header="Cost" />
<DataGridTextColumn Binding="{Binding Profit}" Header="Profit" />
<DataGridTextColumn Binding="{Binding Roi}" Header="ROI" />
<DataGridTextColumn Binding="{Binding Rpc}" Header="RPC" />
<DataGridTextColumn Binding="{Binding Clicks}" Header="Clicks" />
<DataGridTextColumn Binding="{Binding Conversions}" Header="Conversions" />
<DataGridTextColumn Binding="{Binding ConversionRate}" Header="Conversion Rate" />
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
C#:
public class SummaryTotalRecord
{
public DateTime Date { get; set; }
public decimal Cost { get { return Google.Cost + Ypa.PureLocal.Cost + Ypa.BuyerPricer.Cost; } }
public decimal GrossRevenue { get { return Google.GrossRevenue + Ypa.PureLocal.GrossRevenue + Ypa.BuyerPricer.GrossRevenue; } }
public decimal OurRevenue { get { return Google.OurRevenue + Ypa.PureLocal.OurRevenue + Ypa.BuyerPricer.OurRevenue; } }
public decimal Roi { get { return (Google.Roi + Ypa.PureLocal.Roi + Ypa.BuyerPricer.Roi)/3; } }
public decimal ConversionRate { get { return (Google.ConversionRate + Ypa.PureLocal.ConversionRate + Ypa.BuyerPricer.ConversionRate)/3; } }
public decimal Profit { get { return Google.Profit + Ypa.PureLocal.Profit + Ypa.BuyerPricer.Profit; } }
public decimal Rpc { get { return (Google.Rpc + Ypa.PureLocal.Rpc + Ypa.BuyerPricer.Rpc)/3; } }
public long Clicks { get { return Google.Clicks + Ypa.PureLocal.Clicks + Ypa.BuyerPricer.Clicks; } }
public long Conversions { get { return Google.Conversions + Ypa.PureLocal.Conversions + Ypa.BuyerPricer.Conversions; } }
// ... plus a bunch of other properties I haven't touched yet
}
public ObservableCollection<SummaryTotalRecord> SummaryTotals = new ObservableCollection<SummaryTotalRecord>();
I found others with similar problems and the accepted fix seems to be something like
RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBox}}
But neither that, nor x:Type ListItem seems to produce any difference in results.
Update: Trying a new method with similar results. I've changed my class to return a DataTable called TableData and modified the XAML like so:
<Expander IsExpanded="True" Header="{Binding Date, StringFormat=\{0:MM/dd/yyyy\}}">
<DataGrid DataContext="{Binding Path=TableData}" AutoGenerateColumns="True" />
</Expander>
Similar results:
The Expander has access to the Date property, but the DataGrid it contains doesn't have access to TableData?
I ended up adding a new property to the bound object:
public DataTable TableData
{
get
{
var table = new DataTable();
table.Columns.Add("Date", typeof (string));
table.Columns.Add("Gross Revenue", typeof(string));
table.Columns.Add("Our Revenue", typeof(string));
table.Columns.Add("Cost", typeof(string));
table.Columns.Add("Profit", typeof(string));
table.Columns.Add("ROI", typeof(string));
table.Columns.Add("RPC", typeof(string));
table.Columns.Add("Clicks", typeof(long));
table.Columns.Add("Conversions", typeof(long));
table.Columns.Add("Conversion Rate", typeof(string));
table.Rows.Add(
Date.ToString("MM/dd/yyyy"),
GrossRevenue.ToString("C"),
OurRevenue.ToString("C"),
Cost.ToString("C"),
Profit.ToString("C"),
Roi.ToString("P"),
Rpc.ToString("P"),
Clicks,
Conversions,
ConversionRate.ToString("P")
);
return table;
}
}
At which point the following XAML worked:
<DataGrid ItemsSource="{Binding Path=TableData}" AutoGenerateColumns="True"
CanUserAddRows="False" />
I still have no idea why the original code failed and this code works.
It makes perfect sense why the Grid was empty; it wasn't bound to any collection. If the source isn't specified, then there is none, and the grid will be empty. This isn't something that can be inferenced by the binding engine.
I just want to display row numbers in the left-most column of my DataGrid. Is there some attribute to do this?
Keep in mind, this isn't a primary key for my table. I don't want these row numbers to move with their rows when a column is sorted. I basically want a running count. It doesn't even need to have a header.
One way is to add them in the LoadingRow event for the DataGrid
<DataGrid Name="DataGrid" LoadingRow="DataGrid_LoadingRow" ...
void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.Header = (e.Row.GetIndex()).ToString();
}
When items are added or removed from the source list then the numbers can get out of sync for a while. For a fix to this, see the attached behavior here:
WPF 4 DataGrid: Getting the Row Number into the RowHeader
Useable like this
<DataGrid ItemsSource="{Binding ...}"
behaviors:DataGridBehavior.DisplayRowNumber="True">
Adding a short info about Fredrik Hedblad answer.
<DataGrid Name="DataGrid" LoadingRow="DataGrid_LoadingRow" ...
void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.Header = (e.Row.GetIndex()+1).ToString();
}
...If you want to start numbering from 1
If your data grid has its ItemsSource bound to a collection, bind the AlternationCount property of your data grid to either the the count property of your collection, or to the Items.Count property of your DataGrid as follows:
<DataGrid ItemsSource="{Binding MyObservableCollection}" AlternationCount="{Binding MyObservableCollection.Count}" />
Or:
<DataGrid ItemsSource="{Binding MyObservableCollection}" AlternationCount="{Binding Items.Count, RelativeSource={RelativeSource Self}" />
Either should work.
Then, assuming you're using a DataGridTextColumn for your leftmost column you do the following in your DataGrid.Columns definition:
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding AlternationIndex, RelativeSource={RelativeSource AncestorType=DataGridRow}}"
</DataGrid.Columns>
If you don't want to start at 0, you can add a converter to your binding to increment the index.
It's an old question, but I would like to share something. I had a similar problem, all I needed was a simple RowHeader numeration of rows and Fredrik Hedblad's answer was almost complete for my problem.
While this is great:
<DataGrid Name="DataGrid" LoadingRow="DataGrid_LoadingRow" ...
void DataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.Header = (e.Row.GetIndex()).ToString();
}
my headers messed up when removing and adding items. If you have buttons responsible for that just add dataGrid.Items.Refresh(); under the 'deleting' code as in my case:
private void removeButton_Click(object sender, RoutedEventArgs e)
{
// delete items
dataGrid.Items.Refresh();
}
That solved desyncronized numeration for me, because refreshing items calls DataGrig_LoadingRow again.
And just to add to the discussion on this... (I spent too much time finding this out!).
You'll need to set the EnableRowVirtualization to False on the datagrid to prevent errors in the row sequencing:
EnableRowVirtualization="False"
The EnableRowVirtualization property is set to true by default. When the EnableRowVirtualization property is set to true, the DataGrid does not instantiate a DataGridRow object for each data item in the bound data source. Instead, the DataGrid creates DataGridRow objects only when they are needed, and reuses them as much as it can. MSDN Reference here
Just another answer to provide almost copy&paste example (not to be encouraged) for new people or people in a rush, inspired by answers inside this post by #GrantA and #Johan Larsson ( + many other people who answered to the numerous posts on that subject)
You may not want to add the enumeration inside a column
You do not need to re-create your own Attached Property
<UserControl...
<Grid>
<DataGrid ItemsSource="{Binding MainData.ProjColl}"
AutoGenerateColumns="False"
AlternationCount="{ Binding MainData.ProjColl.Count}" >
<DataGrid.Columns>
<!--Columns given here for example-->
...
<DataGridTextColumn Header="Project Name"
Binding="{Binding CMProjectItemDirName}"
IsReadOnly="True"/>
...
<DataGridTextColumn Header="Sources Dir"
Binding="{Binding CMSourceDir.DirStr}"/>
...
</DataGrid.Columns>
<!--The problem of the day-->
<DataGrid.RowHeaderStyle>
<Style TargetType="{x:Type DataGridRowHeader}">
<Setter Property="Content"
Value="{Binding Path=(ItemsControl.AlternationIndex),
RelativeSource={RelativeSource AncestorType=DataGridRow}}"/>
</Style>
</DataGrid.RowHeaderStyle>
</DataGrid>
</Grid>
</UserControl>
Note the parenthesis () around (ItemsControl.AlternationIndex) as warned in Fredrik Hedblad answer in Check if Row as odd number
After some Tests withRowHeaderStyle, the repaired and extended sample from NGI:
<DataGrid EnableRowVirtualization="false" ItemsSource="{Binding ResultView}" AlternationCount="{Binding ResultView.Count}" RowHeaderWidth="10">
<DataGrid.RowHeaderStyle>
<Style TargetType="{x:Type DataGridRowHeader}">
<Setter Property="Content" Value="{Binding Path=AlternationIndex, RelativeSource={RelativeSource AncestorType=DataGridRow}}" />
</Style>
</DataGrid.RowHeaderStyle>
</DataGrid>
Using attached properties, full source here.
using System.Windows;
using System.Windows.Controls;
public static class Index
{
private static readonly DependencyPropertyKey OfPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
"Of",
typeof(int),
typeof(Index),
new PropertyMetadata(-1));
public static readonly DependencyProperty OfProperty = OfPropertyKey.DependencyProperty;
public static readonly DependencyProperty InProperty = DependencyProperty.RegisterAttached(
"In",
typeof(DataGrid),
typeof(Index),
new PropertyMetadata(default(DataGrid), OnInChanged));
public static void SetOf(this DataGridRow element, int value)
{
element.SetValue(OfPropertyKey, value);
}
public static int GetOf(this DataGridRow element)
{
return (int)element.GetValue(OfProperty);
}
public static void SetIn(this DataGridRow element, DataGrid value)
{
element.SetValue(InProperty, value);
}
public static DataGrid GetIn(this DataGridRow element)
{
return (DataGrid)element.GetValue(InProperty);
}
private static void OnInChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var row = (DataGridRow)d;
row.SetOf(row.GetIndex());
}
}
Xaml:
<DataGrid ItemsSource="{Binding Data}">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="dataGrid2D:Index.In"
Value="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" />
</Style>
</DataGrid.RowStyle>
<DataGrid.RowHeaderStyle>
<Style TargetType="{x:Type DataGridRowHeader}">
<Setter Property="Content"
Value="{Binding Path=(dataGrid2D:Index.Of),
RelativeSource={RelativeSource AncestorType={x:Type DataGridRow}}}" />
</Style>
</DataGrid.RowHeaderStyle>
</DataGrid>