Good day.
Have a DataGrid (operationGrid) RowDetailsTemplate, which contains the DataGrid (sumOperationGrid)
<DataGrid AutoGenerateColumns="False" CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="False" IsReadOnly="True" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=UserControl}, Path=OperCountUnits}" Name="operationGrid" RowDetailsVisibilityMode="VisibleWhenSelected" SelectionMode="Single" VerticalContentAlignment="Bottom" Grid.ColumnSpan="2" Margin="6,60,7,6" LoadingRowDetails="operationGrid_LoadingRowDetails" SelectionChanged="operationGrid_SelectionChanged">
<DataGrid.Columns>
<DataGridTextColumn x:Name="Id" Binding="{Binding Path=Id}" Header="{Binding NumberOfOperation, Source={StaticResource resources}}" IsReadOnly="True" />
<DataGridTextColumn x:Name="StartedAt" Binding="{Binding Path=StartedAt, Converter={StaticResource ConvertCultureDate}}" Header="{Binding StartedAt, Source={StaticResource resources}}" IsReadOnly="True" />
<DataGridTextColumn x:Name="FinisedAt" Binding="{Binding Path=FinishedAt, Converter={StaticResource ConvertCultureDate}}" Header="{Binding FinishedAt, Source={StaticResource resources}}" IsReadOnly="True" />
<DataGridTextColumn x:Name="CountMode" Binding="{Binding Path=Description}" Header="{Binding CountMode, Source={StaticResource resources}}" IsReadOnly="True" />
<DataGridTextColumn x:Name="CurrencyCode" Header="{Binding CurrcencyCode, Source={StaticResource resources}}" IsReadOnly="True" />
<DataGridTextColumn x:Name="UnitId" Binding="{Binding Path=UnitId}" Header="UnitId" IsReadOnly="True" Visibility="Collapsed" />
<DataGridTextColumn x:Name="CountModeId" Binding="{Binding Path=CountModeId}" Header="CountModeId" IsReadOnly="True" Visibility="Collapsed" />
<DataGridTextColumn x:Name="HotListCount" Binding="{Binding Path=HotListCount}" Header="Фальш" IsReadOnly="True" Visibility="Collapsed" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<DataGrid AutoGenerateColumns="False" IsReadOnly="True" ItemsSource="{Binding CurrencySum}" Name="sumOperationGrid" SelectionChanged="sumOperationGrid_SelectionChanged" SelectionMode="Single">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding csId}" Visibility="Collapsed" />
<DataGridTextColumn Binding="{Binding CurrId}" Visibility="Collapsed" />
<DataGridTextColumn Binding="{Binding Curr}" Header="{Binding Currency, Source={StaticResource resources}}" />
<DataGridTextColumn Binding="{Binding TotalSum, Converter={StaticResource replaceNull}}" Header="{Binding Sum, Source={StaticResource resources}}" />
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
The request is for the WCF service and it turns out the answer is everything is good
If you submit data through a delegate, then the answer is in the datagrid (sumOperationGrid) come and displays.
private delegate void CurrencySumByCurrentItemInvoke(DataGrid sumGrid,List<CurrencySum> sum );
private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
DataGrid s2 = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
client.BeginGetCurrencySumByCurrentItem((int)_con.Id,
new AsyncCallback(
delegate(IAsyncResult result)
{
List<CurrencySum> sum = client.EndGetCurrencySumByCurrentItem(result).ToList<CurrencySum>();
if (sum != null)
{
s2.Dispatcher.BeginInvoke(new CurrencySumByCurrentItemInvoke(GetCurrencySumByCurrentItemInvoke), new object[2] {s2, sum});
}
}
), null);
}
void GetCurrencySumByCurrentItemInvoke(DataGrid sumGrid, List<CurrencySum> sum)
{
sumGrid.ItemsSource = sum;
}
Tried so.
private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
DataGrid s2 = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
client.BeginGetCurrencySumByCurrentitem((int)_con.Id, new AsyncCallback (GetCurrencySumByCurrentItemCallBack), null);
}
private delegate void CurrencySumByCurrentItemInvoke(DataGrid sumGrid,List<CurrencySum> sum );
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
DataGridRow opGrRow = (DataGridRow)(this.operationGrid.ItemContainergenerator.containerfromitem (this.operationGrid.Items.Currentitem));
ContentPresenter cpOpGrRow = FindVisualChild<ContentPresenter>(opGrRow);
DataTemplate dtOpGrRow = cpOpGrRow.ContentTemplate;
DataGrid s2 = (DataGrid)dtOpGrRow.FindName("sumOperationGrid", cpOpGrRow);
}
void GetCurrencySumByCurrentItemInvoke(DataGrid sumGrid, List<CurrencySum> sum)
{
sumGrid.ItemsSource = sum;
}
private childItem FindVisualChild<childItem>(DependencyObject obj)
where childItem : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is childItem)
return (childItem)child;
else
{
childItem childOfChild = FindVisualChild<childItem>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
and so
private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
DataGrid s2 = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
client.BeginGetCurrencySumByCurrentitem((int)_con.Id, new AsyncCallback (GetCurrencySumByCurrentItemCallBack), s2);
}
private delegate void CurrencySumByCurrentItemInvoke(DataGrid sumGrid,List<CurrencySum> sum );
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
DataGrid s2 = result.AsyncState as DataGrid
List<CurrencySum> sum = client.EndGetCurrencySumByCurrentitem(result).ToList<CurrencySum>();
if (sum != null)
{
s2.Dispatcher.BeginInvoke(new CurrencySumByCurrentItemInvoke(GetCurrencySumByCurrentItemInvoke), new object[2] {s2, sum});
}
}
void GetCurrencySumByCurrentItemInvoke(DataGrid sumGrid, List<CurrencySum> sum)
{
sumGrid.ItemsSource = sum;
}
In both cases, it all boils down to the error
"The calling thread cannot access this object because the owner of this object is another thread."
The question of how to remove a delegate from client.BeginGetCurrencySumByCurrentitem and forward data table s2 in a separate function?
How do I get back into your flow and load data into a table sumOperationGrid ?
If I understood messy code I cant understand why you trying to pass through data grid everywhere. Only try to access grid and set ItemsSource at the end of operation only by passing sum. Here is pseudocode of callback function:
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
if(sum != null)
{
this.Invoke(new Action(()=>
{
//get data grid
//set ItemSource
})
}
}
Or if you do need to have data grid to Invoke try to do something like this:
DataGrid myDataGrid;
private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
myDataGrid = e.DetailsElement.FindName("sumOperationGrid") as DataGrid;
//do stuff without passing grid
}
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
List<CurrencySum> sum = client.EndGetCurrencySumByCurrentitem(result).ToList<CurrencySum>();
if (sum != null)
{
myDataGrid.Dispatcher.BeginInvoke(new Action(()=>
{
myDataGrid.ItemSource = sum;
});
}
Yea. This a good idea. Thank a lot!
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
if(sum != null)
{
this.Invoke(new Action(()=>
{
//get data grid
//set ItemSource
})
}
}
I changed my code
private void operationGrid_LoadingRowDetails(object sender, DataGridRowDetailsEventArgs e)
{
OperCountUnit _con = (OperCountUnit)e.Row.DataContext;
client.BeginGetCurrencySumByCurrentItem((int)_con.Id, new AsyncCallback(GetCurrencySumByCurrentItemCallBack), null);
}
private delegate void CurrencySumByCurrentItemInvoke(List<CurrencySum> sum);
private void GetCurrencySumByCurrentItemCallBack(IAsyncResult result)
{
List<CurrencySum> sum = client.EndGetCurrencySumByCurrentItem(result).ToList<CurrencySum>();
if (sum != null)
{
Dispatcher.Invoke(new CurrencySumByCurrentItemInvoke(GetCurrencySumByCurrentItemInvoke), new object[1] { sum });
}
}
void GetCurrencySumByCurrentItemInvoke(List<CurrencySum> sum)
{
DataGridRow opGrRow = (DataGridRow)(this.operationGrid.ItemContainerGenerator.ContainerFromItem(this.operationGrid.SelectedItem));
DataGridDetailsPresenter cpOpGrRow = FindVisualChild<DataGridDetailsPresenter>(opGrRow);
DataTemplate dtOpGrRow = cpOpGrRow.ContentTemplate;
DataGrid s2 = (DataGrid)dtOpGrRow.FindName("sumOperationGrid", cpOpGrRow);
s2.ItemsSource = sum;
}
Related
I have the following WPF DataGrid with GroupStyle. I need to now which rows are expanded when I have expanded/collapsed event.
I add:
Expanded="Expander_Process" Collapsed="Expander_Process"
but in the event function Expander_Process when I try to get the row
var row = DataGridRow.GetRowContainingElement(expander);
if (row == null)
then the row is null. So my question is: how can I know which rows are expanded in the datagrid?
<DataGrid x:Name="gvOptionChain" AutoGenerateColumns="False" FontWeight="Bold" Background="#FF262626" Foreground="White" Width="1509"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Auto"
Margin="53,120,89,4.333" HorizontalContentAlignment="Right" SelectionChanged="gvOptionChain_SelectionChanged" VirtualizingPanel.IsVirtualizingWhenGrouping="True"
VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.ScrollUnit ="Item" VirtualizingPanel.VirtualizationMode="Recycling" EnableRowVirtualization="True" EnableColumnVirtualization = "True"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" SelectionUnit="Cell" SelectedItem="{Binding SelectedItem, Mode=OneWay}" >
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="False" Foreground="#FFEEEEEE" Expanded="Expander_Process" Collapsed="Expander_Process" >
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock FontWeight="Bold" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type GroupItem}}, Converter={StaticResource ResourceKey=groupToTitleConverter}}" />
</StackPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
private void Expander_Process(object sender, RoutedEventArgs e)
{
if (sender is Expander expander)
{
var row = DataGridRow.GetRowContainingElement(expander);
if (row == null)
{
}
}
}
on the expander event - get the rows that expanded throu
CollectionViewGroup Items1 = ((CollectionViewGroup)(expander.DataContext));
and update a flag about the IsExpanded status of the row . this event happens many times for each expand - so I add timer to process the rows only once
private System.Timers.Timer ExpandTimer;
private bool bIsTimerOn = false;
private void Expander_Process(object sender, RoutedEventArgs e)
{
if (sender is Expander expander)
{
CollectionViewGroup Items1 = ((CollectionViewGroup)(expander.DataContext));
for(int i = 0; i < Items1.Items.Count; i++)
{
OptScrtyData ScrtyData1 = (OptScrtyData)Items1.Items[i];
ScrtyData1.IsExpanded = expander.IsExpanded;
}
if (bIsTimerOn == false)
{
ExpandTimer.Start();
bIsTimerOn = true;
}
}
}
a better answer - activate the timer only if there is new expand status :
private void Expander_Process(object sender, RoutedEventArgs e)
{
if (sender is Expander expander)
{
CollectionViewGroup Items1 = ((CollectionViewGroup)(expander.DataContext));
bool bNewStatus = false;
for(int i = 0; i < Items1.Items.Count; i++)
{
OptScrtyData ScrtyData1 = (OptScrtyData)Items1.Items[i];
if (bNewStatus == false)
{
bNewStatus = ScrtyData1.IsExpanded != expander.IsExpanded;
}
ScrtyData1.IsExpanded = expander.IsExpanded;
}
if (bNewStatus == true)
{
ExpandTimer.Start();
}
}
}
I have a Datagrid in wpf as below:
<DataGrid Grid.Row="1" Grid.Column="1" x:Name="GridItems" HorizontalAlignment="Stretch" Margin="1" Background="Transparent" VerticalAlignment="Stretch"
AutoGenerateColumns="False" GridLinesVisibility="All" HeadersVisibility="Column" SelectionMode="Single" SelectionUnit="Cell"
VerticalContentAlignment="Center" SelectedIndex="-1" CanUserAddRows="False" ItemsSource="{Binding Transaction.TransactionItems}"
VerticalGridLinesBrush="#ccc" HorizontalGridLinesBrush="#ccc" AlternationCount="2" AlternatingRowBackground="#F2F2F2"
BorderThickness="0" RowHeaderWidth="0" CellStyle="{StaticResource CellStyleWithPadding}"
CellEditEnding="GridItems_CellEditEnding_1" PreviewKeyDown="GridItems_PreviewKeyDown_1" MouseDoubleClick="GridItems_MouseDoubleClick" BeginningEdit="GridItems_BeginningEdit">
<DataGrid.Columns>
<DataGridTextColumn x:Name="Id" Visibility="Collapsed" Binding="{Binding Id}"></DataGridTextColumn>
<DataGridTextColumn x:Name="Code" Header="Code" Width="50" Binding="{Binding Code,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></DataGridTextColumn>
<DataGridTextColumn x:Name="Name" Header="Name" Width="*" Binding="{Binding Name}" IsReadOnly="True"></DataGridTextColumn>
<DataGridTextColumn x:Name="Unit" Header="Unit" Width="40" Binding="{Binding SelectedUnit.Name}" IsReadOnly="True" Visibility="Collapsed"></DataGridTextColumn>
<DataGridTextColumn x:Name="SalePrice" Header="Sale Price" IsReadOnly="True" Width="70" Binding="{Binding SalePrice,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" ElementStyle="{StaticResource RightCell}"></DataGridTextColumn>
<DataGridTextColumn x:Name="Quantity" Header="Quantity" Width="70" Binding="{Binding Quantity,Mode=TwoWay,UpdateSourceTrigger=LostFocus}" ElementStyle="{StaticResource RightCell}"></DataGridTextColumn>
<DataGridTextColumn x:Name="Amount" Header="Amount" Width="80" Binding="{Binding Amount}" ElementStyle="{StaticResource RightCell}" IsReadOnly="True"></DataGridTextColumn>
<DataGridTemplateColumn Width="60" Header="Remove">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="BtnDelete" Click="BtnDelete_Click_1" Background="Transparent" BorderThickness="0">
<Image Height="25" Source="/Images/delete.png"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
In this grid only code and quantity is editable and i had used a code to set focus directly to quantity from code and then from quantity to next row code column. Set Focus code is as below:
public static void SelectCellByIndex(this DataGrid dataGrid, int rowIndex, int columnIndex)
{
if (!dataGrid.SelectionUnit.Equals(DataGridSelectionUnit.Cell))
throw new ArgumentException("The SelectionUnit of the DataGrid must be set to Cell.");
if (rowIndex < 0 || rowIndex > (dataGrid.Items.Count - 1))
return;
if (columnIndex < 0 || columnIndex > (dataGrid.Columns.Count - 1))
return;
dataGrid.SelectedCells.Clear();
object item = dataGrid.Items[rowIndex]; //=Product X
DataGridRow row = dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) as DataGridRow;
if (row == null)
{
dataGrid.ScrollIntoView(item);
row = dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) as DataGridRow;
}
if (row != null)
{
DataGridCell cell = GetCell(dataGrid, row, columnIndex);
if (cell != null)
{
DataGridCellInfo dataGridCellInfo = new DataGridCellInfo(cell);
dataGrid.CurrentCell = dataGridCellInfo;
if (!dataGrid.SelectedCells.Contains(dataGridCellInfo))
dataGrid.SelectedCells.Add(dataGridCellInfo);
cell.Focus();
//TextBox ele = ((ContentPresenter)(cell.Column.GetCellContent(row))).Content as TextBox;
//ele.Focus();
//dataGrid.BeginEdit();
}
}
}
public static DataGridCell GetCell(DataGrid dataGrid, DataGridRow rowContainer, int column)
{
if (rowContainer != null)
{
DataGridCellsPresenter presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
if (presenter == null)
{
/* if the row has been virtualized away, call its ApplyTemplate() method
* to build its visual tree in order for the DataGridCellsPresenter
* and the DataGridCells to be created */
rowContainer.ApplyTemplate();
presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
}
if (presenter != null)
{
DataGridCell cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
if (cell == null)
{
/* bring the column into view
* in case it has been virtualized away */
dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]);
cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
}
return cell;
}
}
return null;
}
public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
return (T)child;
else
{
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
Now this code is working fine except 1 issue that when i set focus to next column then its not going in edit mode when i press any key.I had also used DataGrid.BeginEdit for this but when i use that then CellEditEnding is not getting fired for that column/cell.
Can anyone help me there?
I have a Datagrid in wpf as below:
<DataGrid Grid.Row="1" Grid.Column="1" x:Name="GridItems" HorizontalAlignment="Stretch" Margin="1" Background="Transparent" VerticalAlignment="Stretch"
AutoGenerateColumns="False" GridLinesVisibility="All" HeadersVisibility="Column" SelectionMode="Single" SelectionUnit="Cell"
VerticalContentAlignment="Center" SelectedIndex="-1" CanUserAddRows="False" ItemsSource="{Binding Transaction.TransactionItems}"
VerticalGridLinesBrush="#ccc" HorizontalGridLinesBrush="#ccc" AlternationCount="2" AlternatingRowBackground="#F2F2F2"
BorderThickness="0" RowHeaderWidth="0" CellStyle="{StaticResource CellStyleWithPadding}"
CellEditEnding="GridItems_CellEditEnding_1" PreviewKeyDown="GridItems_PreviewKeyDown_1" MouseDoubleClick="GridItems_MouseDoubleClick" BeginningEdit="GridItems_BeginningEdit">
<DataGrid.Columns>
<DataGridTextColumn x:Name="Id" Visibility="Collapsed" Binding="{Binding Id}"></DataGridTextColumn>
<DataGridTextColumn x:Name="Code" Header="Code" Width="50" Binding="{Binding Code,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></DataGridTextColumn>
<DataGridTextColumn x:Name="Name" Header="Name" Width="*" Binding="{Binding Name}" IsReadOnly="True"></DataGridTextColumn>
<DataGridTextColumn x:Name="Unit" Header="Unit" Width="40" Binding="{Binding SelectedUnit.Name}" IsReadOnly="True" Visibility="Collapsed"></DataGridTextColumn>
<DataGridTextColumn x:Name="SalePrice" Header="Sale Price" IsReadOnly="True" Width="70" Binding="{Binding SalePrice,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" ElementStyle="{StaticResource RightCell}"></DataGridTextColumn>
<DataGridTextColumn x:Name="Quantity" Header="Quantity" Width="70" Binding="{Binding Quantity,Mode=TwoWay,UpdateSourceTrigger=LostFocus}" ElementStyle="{StaticResource RightCell}"></DataGridTextColumn>
<DataGridTextColumn x:Name="Amount" Header="Amount" Width="80" Binding="{Binding Amount}" ElementStyle="{StaticResource RightCell}" IsReadOnly="True"></DataGridTextColumn>
<DataGridTemplateColumn Width="60" Header="Remove">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="BtnDelete" Click="BtnDelete_Click_1" Background="Transparent" BorderThickness="0">
<Image Height="25" Source="/Images/delete.png"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
In this grid only code and quantity is editable and i had used a code to set focus directly to quantity from code and then from quantity to next row code column. Set Focus code is as below:
public static void SelectCellByIndex(this DataGrid dataGrid, int rowIndex, int columnIndex)
{
if (!dataGrid.SelectionUnit.Equals(DataGridSelectionUnit.Cell))
throw new ArgumentException("The SelectionUnit of the DataGrid must be set to Cell.");
if (rowIndex < 0 || rowIndex > (dataGrid.Items.Count - 1))
return;
if (columnIndex < 0 || columnIndex > (dataGrid.Columns.Count - 1))
return;
dataGrid.SelectedCells.Clear();
object item = dataGrid.Items[rowIndex]; //=Product X
DataGridRow row = dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) as DataGridRow;
if (row == null)
{
dataGrid.ScrollIntoView(item);
row = dataGrid.ItemContainerGenerator.ContainerFromIndex(rowIndex) as DataGridRow;
}
if (row != null)
{
DataGridCell cell = GetCell(dataGrid, row, columnIndex);
if (cell != null)
{
DataGridCellInfo dataGridCellInfo = new DataGridCellInfo(cell);
dataGrid.CurrentCell = dataGridCellInfo;
if (!dataGrid.SelectedCells.Contains(dataGridCellInfo))
dataGrid.SelectedCells.Add(dataGridCellInfo);
cell.Focus();
//TextBox ele = ((ContentPresenter)(cell.Column.GetCellContent(row))).Content as TextBox;
//ele.Focus();
//dataGrid.BeginEdit();
}
}
}
public static DataGridCell GetCell(DataGrid dataGrid, DataGridRow rowContainer, int column)
{
if (rowContainer != null)
{
DataGridCellsPresenter presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
if (presenter == null)
{
/* if the row has been virtualized away, call its ApplyTemplate() method
* to build its visual tree in order for the DataGridCellsPresenter
* and the DataGridCells to be created */
rowContainer.ApplyTemplate();
presenter = FindVisualChild<DataGridCellsPresenter>(rowContainer);
}
if (presenter != null)
{
DataGridCell cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
if (cell == null)
{
/* bring the column into view
* in case it has been virtualized away */
dataGrid.ScrollIntoView(rowContainer, dataGrid.Columns[column]);
cell = presenter.ItemContainerGenerator.ContainerFromIndex(column) as DataGridCell;
}
return cell;
}
}
return null;
}
public static T FindVisualChild<T>(DependencyObject obj) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(obj, i);
if (child != null && child is T)
return (T)child;
else
{
T childOfChild = FindVisualChild<T>(child);
if (childOfChild != null)
return childOfChild;
}
}
return null;
}
Now this code is working fine except 1 issue that when i set focus to next column then its not going in edit mode when i press any key.I had also used DataGrid.BeginEdit for this but when i use that then CellEditEnding is not getting fired for that column/cell.
Can anyone help me there?
I'm trying to sort a ListView by it's headers.
I'm following this MSDN example, with the alternation given here - where this line works if I were to use direct binding:
GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
string sortString = ((Binding)headerClicked.Column.DisplayMemberBinding).Path.Path
But problem is I'm not binding the columns directly using DisplayMemberBinding="{Binding PVNum}" but rather I am using CellTemplate:
<ListView.Resources>
<DataTemplate x:Key="NumberTemplate">
<TextBlock Text="{Binding PVNum}" TextAlignment="Center" />
</DataTemplate>
</ListView.Resources>
<ListView.View>
<GridView AllowsColumnReorder="False">
<GridView.Columns>
<GridViewColumn Header=" " CellTemplate="{StaticResource NumberTemplate}"/>
</GridView.Columns>
</GridView>
</ListView.View>
So my question is - how do I get this "PVNum" string in the code behind?
I did try this, though s is null - so I guess I'm off:
var t = headerClicked.Column.CellTemplate.LoadContent() as TextBlock;
var s = t.GetBindingExpression(TextBox.TextProperty);
Any suggestions?
It should be TextBlock.Text property:
var t = headerClicked.Column.CellTemplate.LoadContent() as TextBlock;
var s = t.GetBindingExpression(TextBlock.TextProperty);
string sourcePropertyName = s.ParentBinding.Path.Path;
A possible solution is to define an attached property for GridViewColumn:
public class GridViewColumnAttachedProperties
{
public static readonly DependencyProperty SortPropertyNameProperty = DependencyProperty.RegisterAttached(
"SortPropertyName", typeof(string), typeof(GridViewColumnAttachedProperties), new PropertyMetadata(default(string)));
public static void SetSortPropertyName(DependencyObject element, string value)
{
element.SetValue(SortPropertyNameProperty, value);
}
public static string GetSortPropertyName(DependencyObject element)
{
return (string) element.GetValue(SortPropertyNameProperty);
}
}
In XAML you set the attached properties to the Path used in the Binding inside the templates. Based on the example from the MSDN site:
<ListView x:Name='lv' Height="150" HorizontalAlignment="Center" VerticalAlignment="Center" GridViewColumnHeader.Click="GridViewColumnHeaderClickedHandler">
<ListView.Resources>
<DataTemplate x:Key="YearTemplate">
<TextBlock Text="{Binding Year}" TextAlignment="Center" />
</DataTemplate>
<DataTemplate x:Key="MonthTemplate">
<TextBlock Text="{Binding Month}" TextAlignment="Center" />
</DataTemplate>
<DataTemplate x:Key="DayTemplate">
<TextBlock Text="{Binding Day}" TextAlignment="Center" />
</DataTemplate>
</ListView.Resources>
<ListView.ItemsSource>
<collections:ArrayList>
<system:DateTime>1993/1/1 12:22:02</system:DateTime>
<system:DateTime>1993/1/2 13:2:01</system:DateTime>
<system:DateTime>1997/1/3 2:1:6</system:DateTime>
<system:DateTime>1997/1/4 13:6:55</system:DateTime>
<system:DateTime>1999/2/1 12:22:02</system:DateTime>
<system:DateTime>1998/2/2 13:2:01</system:DateTime>
<system:DateTime>2000/2/3 2:1:6</system:DateTime>
<system:DateTime>2002/2/4 13:6:55</system:DateTime>
<system:DateTime>2001/3/1 12:22:02</system:DateTime>
<system:DateTime>2006/3/2 13:2:01</system:DateTime>
<system:DateTime>2004/3/3 2:1:6</system:DateTime>
<system:DateTime>2004/3/4 13:6:55</system:DateTime>
</collections:ArrayList>
</ListView.ItemsSource>
<ListView.View>
<GridView>
<GridViewColumn CellTemplate="{StaticResource YearTemplate}" local:GridViewColumnAttachedProperties.SortPropertyName="Year" />
<GridViewColumn CellTemplate="{StaticResource MonthTemplate}" local:GridViewColumnAttachedProperties.SortPropertyName="Month" />
<GridViewColumn CellTemplate="{StaticResource DayTemplate}" local:GridViewColumnAttachedProperties.SortPropertyName="Day" />
</GridView>
</ListView.View>
</ListView>
And in the Click event handler you can just retrieve the value of the attached property with string bindingName = headerClicked.Column.GetValue(GridViewColumnAttachedProperties.SortPropertyNameProperty) as string;. Based on the MSDN example:
GridViewColumnHeader _lastHeaderClicked;
ListSortDirection _lastDirection = ListSortDirection.Ascending;
private void GridViewColumnHeaderClickedHandler(object sender, RoutedEventArgs e)
{
GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;
if (headerClicked != null)
{
if (headerClicked.Role != GridViewColumnHeaderRole.Padding)
{
ListSortDirection direction;
if (!ReferenceEquals(headerClicked, _lastHeaderClicked))
{
direction = ListSortDirection.Ascending;
}
else
{
if (_lastDirection == ListSortDirection.Ascending)
{
direction = ListSortDirection.Descending;
}
else
{
direction = ListSortDirection.Ascending;
}
}
string bindingName = headerClicked.Column.GetValue(GridViewColumnAttachedProperties.SortPropertyNameProperty) as string;
Sort(bindingName, direction);
if (direction == ListSortDirection.Ascending)
{
headerClicked.Column.HeaderTemplate = Resources["HeaderTemplateArrowUp"] as DataTemplate;
}
else
{
headerClicked.Column.HeaderTemplate = Resources["HeaderTemplateArrowDown"] as DataTemplate;
}
// Remove arrow from previously sorted header
if (_lastHeaderClicked != null && !ReferenceEquals(_lastHeaderClicked, headerClicked))
{
_lastHeaderClicked.Column.HeaderTemplate = null;
}
_lastHeaderClicked = headerClicked;
_lastDirection = direction;
}
}
}
private void Sort(string sortBy, ListSortDirection direction)
{
ICollectionView dataView = CollectionViewSource.GetDefaultView(lv.ItemsSource);
dataView.SortDescriptions.Clear();
SortDescription sd = new SortDescription(sortBy, direction);
dataView.SortDescriptions.Add(sd);
dataView.Refresh();
}
I have a RadTreeView (treeview) and a RadGridView (gridview). The user should be able to move elements from the gridview to the treeview and change the effect of the drag&drop action by using the shift or control key (like in Windows Explorer).
In the GiveFeedback event the effect is set according to the pressed key and also the visualization shows the expected behavior. But in the Drop event (on the treeview) and in the DragDropCompleted event (on the gridview) the effect is set to All, therefore I'm not able to decide if I have to remove the Element from the current list.
Am I missing something?
XAML:
<telerik:RadTreeView AllowDrop="True"
ItemsSource="{Binding Folders}"
SelectedItem="{Binding SelectedFolder,
Mode=TwoWay}"
SelectionMode="Single">
<telerik:RadTreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:Folder}" ItemsSource="{Binding Children}">
<Grid VerticalAlignment="Center">
<TextBlock VerticalAlignment="Center"
Text="{Binding Name}"
local:TreeItemDropBehavior.IsEnabled="True" />
</Grid>
</HierarchicalDataTemplate>
</telerik:RadTreeView.Resources>
</telerik:RadTreeView>
<telerik:RadGridView Grid.Column="1"
AllowDrop="True"
AutoGenerateColumns="False"
CanUserFreezeColumns="False"
CanUserReorderColumns="False"
CanUserResizeColumns="False"
FontSize="12"
IsFilteringAllowed="False"
IsReadOnly="True"
ItemsSource="{Binding SelectedFolder.Elements}"
RowHeight="32"
RowIndicatorVisibility="Collapsed"
SelectionMode="Multiple"
ShowGroupPanel="False"
local:GridViewDragDropBehavior.IsEnabled="True"
telerik:DragDropManager.AllowCapturedDrag="True"
telerik:DragDropManager.AllowDrag="True">
<telerik:RadGridView.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<DataTemplate x:Key="DraggedItemTemplate">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Dragging: " />
<TextBlock FontWeight="Bold" Text="{Binding CurrentDraggedItems.Count}" />
<TextBlock Text=" Element(s)" />
</StackPanel>
</DataTemplate>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</telerik:RadGridView.Resources>
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn Width="100"
DataMemberBinding="{Binding Name}"
Header="Name" />
<telerik:GridViewDataColumn Width="250"
DataMemberBinding="{Binding Description}"
Header="Description" />
</telerik:RadGridView.Columns>
</telerik:RadGridView>
GridViewDragDropBehavior:
public class GridViewDragDropBehavior
{
public RadGridView AssociatedControl { get; set; }
private void SubscribeToDragDropEvents()
{
DragDropManager.AddDragInitializeHandler(AssociatedControl, OnDragInitialize);
DragDropManager.AddGiveFeedbackHandler(AssociatedControl, OnGiveFeedback);
DragDropManager.AddDragDropCompletedHandler(AssociatedControl, OnDragDropCompleted);
DragDropManager.AddDragOverHandler(AssociatedControl, OnDragOver);
}
private void UnsubscribeFromDragDropEvents()
{
DragDropManager.RemoveDragInitializeHandler(AssociatedControl, OnDragInitialize);
DragDropManager.RemoveGiveFeedbackHandler(AssociatedControl, OnGiveFeedback);
DragDropManager.RemoveDragDropCompletedHandler(AssociatedControl, OnDragDropCompleted);
DragDropManager.RemoveDragOverHandler(AssociatedControl, OnDragOver);
}
private void OnDragInitialize(object sender, DragInitializeEventArgs e)
{
DropIndicationDetails details = new DropIndicationDetails();
var gridView = sender as RadGridView;
details.DragSource = gridView.ItemsSource;
var items = gridView.SelectedItems;
details.CurrentDraggedItems = items;
IDragPayload dragPayload = DragDropPayloadManager.GeneratePayload(null);
dragPayload.SetData("DraggedData", items);
dragPayload.SetData("DropDetails", details);
e.Data = dragPayload;
e.DragVisual = new DragVisual { Content = details, ContentTemplate = AssociatedControl.Resources["DraggedItemTemplate"] as DataTemplate };
e.DragVisualOffset = new Point(e.RelativeStartPoint.X + 10, e.RelativeStartPoint.Y);
e.AllowedEffects = DragDropEffects.All;
}
private void OnGiveFeedback(object sender, GiveFeedbackEventArgs e)
{
Debug.WriteLine("GridViewDragDropBehavior.OnGiveFeedback {0}", e.Effects);
e.SetCursor(Cursors.Arrow);
e.Handled = true;
}
private void OnDragDropCompleted(object sender, DragDropCompletedEventArgs e)
{
Debug.WriteLine("GridViewDragDropBehavior.OnDragDropCompleted: {0}", e.Effects);
var data = DragDropPayloadManager.GetDataFromObject(e.Data, "DraggedData") as IList;
var details = DragDropPayloadManager.GetDataFromObject(e.Data, "DropDetails");
Debug.WriteLine(e.Effects);
// Remove Element from source list if drag drop effect is move
/*if (e.Effects == DragDropEffects.Move)
{
var collection = (details as DropIndicationDetails).DragSource as IList;
foreach(var element in data)
{
collection.Remove(element);
}
}*/
}
private void OnDragOver(object sender, Telerik.Windows.DragDrop.DragEventArgs e)
{
Debug.WriteLine("GridViewDragDropBehavior.OnDragOver: {0}", e.Effects);
e.Effects = DragDropEffects.None;
e.Handled = true;
}
}
TreeItemDropBehavior.cs:
public class TreeItemDropBehavior
{
protected virtual void Initialize()
{
DragDropManager.AddGiveFeedbackHandler(AssociatedObject, OnGiveFeedback);
DragDropManager.AddDropHandler(AssociatedObject, OnDrop);
}
protected virtual void CleanUp()
{
DragDropManager.RemoveGiveFeedbackHandler(AssociatedObject, OnGiveFeedback);
DragDropManager.RemoveDropHandler(AssociatedObject, OnDrop);
}
private void OnGiveFeedback(object sender, Telerik.Windows.DragDrop.GiveFeedbackEventArgs e)
{
Debug.WriteLine("TreeItemDropBehavior.OnGiveFeedback {0}", e.Effects);
e.SetCursor(Cursors.Arrow);
e.Handled = true;
}
private void OnDrop(object sender, Telerik.Windows.DragDrop.DragEventArgs e)
{
Debug.WriteLine("TreeItemDropBehavior.OnDrop: {0}", e.Effects);
if (e.Effects != DragDropEffects.None)
{
var destinationItem = (e.OriginalSource as FrameworkElement).ParentOfType<RadTreeViewItem>();
var data = DragDropPayloadManager.GetDataFromObject(e.Data, "DraggedData") as IList;
var details = DragDropPayloadManager.GetDataFromObject(e.Data, "DropDetails") as DropIndicationDetails;
if (destinationItem != null)
{
foreach (var element in data)
{
(destinationItem.DataContext as Folder).Elements.Add(element as Element);
}
e.Handled = true;
}
}
}
}