WPF DataGrid RowHeader MouseEnter MouseEventHandler - wpf

I have datagrid and it's loaded event I would like to create event handler for Mouse Enter and Mouse Leave.
I was able to do it for Datagrid Column header but I dont know how to do it for Row.
Here is code:
for (int i = 0; i < grid.Columns.Count; i++)
{
DataGridColumnHeader columnHeader = DataGridHelper.GetColumnHeader(this, i);
if (columnHeader != null)
{
columnHeader.MouseEnter += new MouseEventHandler(ColumnHeader_MouseEnter);
columnHeader.MouseLeave += new MouseEventHandler(ColumnHeader_MouseLeave);
}
}
Pls Help me.
Thanks
Dee

I would use a Style
<Style TargetType="{x:Type DataGridRowHeader}">
<EventSetter Event="MouseEnter" Handler="MyMouseEnterHandler"/>
<EventSetter Event="MouseLeave" Handler="MyMouseLeaveHandler"/>
</Style>
You should be able to do the same for the column headers and get rid of the code behind.

Related

DataGrid: Adding DataGridCellInfo-instances to the DataGrid.SelectedCells-collection is very slow

In a WPF DataGrid, I have tried to select all cells within a given column with the following code
for (int i = 0; i < MyDataGrid.Items.Count; i++)
{
MyDataGrid.SelectedCells.Add(new DataGridCellInfo(MyDataGrid.Items[i], column));
}
This piece of code runs extremely slow, though.
MyDataGrid.Items is of type ItemCollection<MyDataStructure> and holds about 70,000 items.
MyDataGrid.SelectedCells is of type IList<DataGridCellInfo>.
It takes the loop roughly 30 seconds in all.
Can someone explain why it takes so long?
Also, is it possible to exchange this for a LINQ query?
Accessing SelectedCells/SelectedRows/SelectedColumns is inefficient anyway when it comes to big sets of data. So you can't change it to work much better. Instead I'm suggesting you to use style and DataTrigger. To apply this solution you'll have to extend MyDataStructure and add IsSelected property. Then mimic selection by applying specific style:
<Style x:Key="dataGridSelectableCellStyle" TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsItemSelected}" Value="True">
<Setter Property="Background" Value="Gey"/>
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
Property in MyDataStructure:
private bool isItemSelected;
public bool IsItemSelected
{
get { return isItemSelected; }
set
{
isItemSelected = value;
this.OnPropertyChanged("IsItemSelected");
}
}
And finally looping through rows:
foreach(var item in MyDataGrid.ItemsSource.Cast<MyDataStructure>())
{
item.IsItemSelected = true;
}

Navigate listboxitems using arrow Keys

My usercontrol has Listbox with Images as ListboxItems, here i'm facing an issue when i navigate listbox items (Images) using "Arrow Keys",i could'n navigate the items that is present in Next row, say for example ,List box contains rows of images*("I have used WrapPanel")*, if i navigate an images using Right arrow key i cant able to move to next row,
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="KeyboardNavigation.DirectionalNavigation" Value="Cycle" />
<Setter Property="IsTabStop" Value="True" />
</Style>
</ListBox.ItemContainerStyle>
Based on this answer which almost worked, but not quite.
Put a KeyDown event on your ListBox and use its ItemsCollection to select the next or previous element when you see a Right or Left keypress.
That moves the selection, but not the keyboard focus (dotted line), so you must also call MoveFocus on the element that has Keyboard focus.
private void ListBox_KeyDown( object sender, KeyEventArgs e )
{
var list = sender as ListBox;
switch( e.Key )
{
case Key.Right:
if( !list.Items.MoveCurrentToNext() ) list.Items.MoveCurrentToLast();
break;
case Key.Left:
if( !list.Items.MoveCurrentToPrevious() ) list.Items.MoveCurrentToFirst();
break;
}
e.Handled = true;
if( list.SelectedItem != null )
{
(Keyboard.FocusedElement as UIElement).MoveFocus( new TraversalRequest( FocusNavigationDirection.Next ) );
}
}
Finally, make sure you have IsSynchronizedWithCurrentItem="True" on your ListBox.
This will give you the wrap-around behaviour that you want.

Set datagrid row color based on the value in the datagrid

Datagrid dgColor contains two fields:
columnA shows names like a,b,c.
columnB is a hidden field which contains color code values like (#ffffff, #ff1211, #1111, #1222).
Fill datagrid row color based on the values in columnB.
Give this a try but I did not test it with data
<DataGrid Name="dataGrid1" Margin="12,12,0,0">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="{Binding Path=colorCol}" />
</Style>
</DataGrid.RowStyle>
</DataGrid>
Since you already got the color you need for each row you can easily set the colors of each row in the LoadingRow event. I can't test the code where I am now so you probably have to tweak this some, but you can try something like this:
private void dataGrid_LoadingRow(object sender, System.Windows.Controls.DataGridRowEventArgs e)
{
MyObject myObject = e.Row.Item as MyObject;
if (myObject != null)
{
byte r = byte.Parse(myObject.Color.Substring(1, 2), NumberStyles.HexNumber);
byte g = byte.Parse(myObject.Color.Substring(3, 2), NumberStyles.HexNumber);
byte b = byte.Parse(myObject.Color.Substring(5, 2), NumberStyles.HexNumber);
e.Row.Background = new SolidColorBrush(Color.FromRgb(r,g,b));
}
}
EDIT:
Try the edited code instead. It should do the job.

DatagridTemplate column content only visible when row selected and cell clicked

Hi I have a Datagrid that is bound to an ObservableCollection of custom AutoCAD layer objects. 3 of the columns are DataGridTextColumns and work correctly. However I also have a DataGridTemplateColumn that contains a StackPanel containing a label and a Rectangle. I am using the label to display the ACI or RGB value of the layer depending how it is set and displaying the colour in the rectangle. The rectangle has a mouse down event that launches a colour picker dialog so the user can select a new colour for the layer. This functionality works. What doesn't work is that the contents of the cell (the label and rectangle) are only shown in a row that is selected and the cell clicked on whereas they need to be visible at all times.
I have tried using a Grid inside the DataTemplate and using the Grid's FocusManager.Focused element to give the Rectangle Focus but this hasn't changed the behaviour.
<t:DataGrid x:Name="layersGrid" ItemsSource="{Binding Layers}"
SelectedItem="{Binding SelectedLayer, Mode=TwoWay}" SelectionMode="Single">
<t:DataGridTemplateColumn Visibility="Visible">
<t:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid FocusManager.FocusedElement="{Binding ElementName=swatch}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Colour.ColourProperty}"/>
<Rectangle Name="swatch" Fill="{Binding Colour, Converter={StaticResource colourConverter}}"
MouseLeftButtonDown="swatch_MouseLeftButtonDown"/>
</StackPanel>
</Grid>
</DataTemplate>
</t:DataGridTemplateColumn.CellEditingTemplate>
</t:DataGridTemplateColumn>
</t:DataGrid.Columns>
</t:DataGrid>
Additionally, once you change the colour of the layer in the model view, the rectangle hasn't updated until another row is selected and then the changed one is selected again.
private void swatch_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
Colour col = LaunchColourPickerCode();
((LayersModel)this.Resources[MODEL]).SelectedLayer.Colour = col;
}
The problem with them not displaying has been fixed by using CellTemplate instead of a CellEditingTemplate
I adapted surfen's answer on this page to solve the selection problem
How to perform Single click checkbox selection in WPF DataGrid?
Replacing his method with this:
private static void GridColumnFastEdit(DataGridCell cell, RoutedEventArgs e)
{
if (cell == null || cell.IsEditing || cell.IsReadOnly)
return;
DataGrid dataGrid = FindVisualParent<DataGrid>(cell);
if (dataGrid == null)
return;
if (!cell.IsFocused)
{
cell.Focus();
}
DataGridRow row = FindVisualParent<DataGridRow>(cell);
if (row != null && !row.IsSelected)
{
row.IsSelected = true;
}
}
and adding an event on the swatch to obtain the cell it is in
private void swatch_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
DataGridCell cell = null;
while (cell == null)
{
cell = sender as DataGridCell;
if (((FrameworkElement)sender).Parent != null)
sender = ((FrameworkElement)sender).Parent;
else
sender = ((FrameworkElement)sender).TemplatedParent;
}
GridColumnFastEdit(cell, e);
}
Also thanks to kmatyaszek

Update UI when adding to observable collection

iv'e got 2 itemscontrols bound to 2 observable collections
my ItemsControls :
<ItemsControl Grid.Column="4" Name="Pipe19" ItemsSource="{Binding Path=Pipes[19].Checkers}" Style="{StaticResource ItemsControlStyle}" ItemsPanel="{StaticResource TopPipePanelTemplate}" />
<ItemsControl Grid.Column="5" Name="Pipe18" ItemsSource="{Binding Path=Pipes[18].Checkers}" Style="{StaticResource ItemsControlStyle}" ItemsPanel="{StaticResource TopPipePanelTemplate}" />
their definitions through style :
<Style TargetType="{x:Type ItemsControl}" x:Key="ItemsControlStyle">
<Setter Property="ItemTemplate" Value="{StaticResource PipeDataItem}"></Setter>
<Setter Property="AllowDrop" Value="True"></Setter>
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ItemsControl_MouseLeftButtonDown"></EventSetter>
<EventSetter Event="Drop" Handler="ItemsControl_Drop"></EventSetter>
</Style>
both are considered drop targets , they contain ellipse items which i need to drag from one to the other . the problem is the Add part of the operation doesn't get rendered to the UI .
Visual Helper :
when drag drop as ellipse from the right one to the left :
this is the code which grabs the ellipse and saves the source control
private void ItemsControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ItemsControl control = (ItemsControl)sender;
UIElement element = control.InputHitTest(e.GetPosition(control)) as UIElement;
if (element != null)
{
Ellipse ellipse = element as Ellipse;
DragSource = control;
DragDrop.DoDragDrop(ellipse, ellipse, DragDropEffects.Copy);
}
}
this is the code where the ellipse is dropped onto it's target :
private void ItemsControl_Drop(object sender, DragEventArgs e)
{
ItemsControl target = (ItemsControl)sender;
Ellipse ellipse = (Ellipse)e.Data.GetData(typeof(Ellipse));
((ObservableCollection<Checker>)DragSource.ItemsSource).Remove(ellipse.DataContext as Checker);
((ObservableCollection<Checker>)target.ItemsSource).Add(ellipse.DataContext as Checker);
}
the Checkers collections are described as follows : (Take notice that they are the itemscontrols ItemsSource :
public class Pipe
{
private ObservableCollection<Checker> checkers;
public ObservableCollection<Checker> Checkers
{
get
{
if (checkers == null)
checkers = new ObservableCollection<Checker>();
return checkers;
}
}
}
after the ItemsControl_Drop event the result is that only the remove updated the UI but the add on the target add not (i would expect that a new item would appear on the left one which i called Add on it's itemsource :
Another Visual Aid :
any ideas ?
Maybe the problem is that your adding to the ItemsSource of the ItemsControl instead of directly to the Pipes[#].Checkers.
I notice that the remove method is a remove from DragSource.ItemsSource.
You probably have a DragTargetControl which ItemsSource you should add to, which will update the Binding.
In the way your doing it you might be undoing the Binding if you cast the Binded ItemsSource to an ObservableCollection.
I think I know what is going on. I bet Pipes[] is not an ObservableCollection. Try it with Pipe1 and Pipe2. Remove from Pipe1 and add to Pipe2.
it turns out the ui element some how loses it's reference if you remove it
from one observable collection before adding it to another ,
so i added the ellipse to the target and then removed it from the source .
private void ItemsControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ItemsControl control = (ItemsControl)sender;
UIElement element = control.InputHitTest(e.GetPosition(control)) as UIElement;
if (element != null && (element is Ellipse))
{
Ellipse ellipse = element as Ellipse;
DragSource = control;
DragDrop.DoDragDrop(ellipse, ellipse, DragDropEffects.Move);
}
}
private void ItemsControl_Drop(object sender, DragEventArgs e)
{
ItemsControl target = (ItemsControl)sender;
Ellipse ellipse = (Ellipse)e.Data.GetData(typeof(Ellipse));
ObservableCollection<Checker> collection = target.ItemsSource as ObservableCollection<Checker>;
Checker checker = ellipse.DataContext as Checker;
collection.Add(checker);
((ObservableCollection<Checker>)DragSource.ItemsSource).Remove(ellipse.DataContext as Checker);
}

Resources