WPF Datagrid Selection Issue - wpf

In my WPF application I have one datagrid and one textbox. In the textChanged event of the textbox, I put this:
myDatagrid.ItemsSource =
myListOfObjects.Where(item => item.Name.Contains(MyTextBox.Text)); //Filter
if (myDatagrid.Items.Count > 0) // If no itens, then do nothing
{
myDatagrid.SelectedIndex = 0; // If has at least one item, select the first
}
myDatagrid.Items.Refresh();
Note that I force the selection when the text changes, in the first row of the DataGrid.
But unfortunately, the color of the row does not change to blue, making it hard to see the selection.
I realy need this, because in the PreviewKeyDown event of the textbox I have this:
private void myTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Up)
{
if (!(myDataGrid.SelectedIndex <= 0))
{
myDataGrid.SelectedIndex--; // Go one position Up
}
}
if (e.Key == Key.Down)
{
if (!(myDataGrid.SelectedIndex == myDataGrid.Items.Count - 1))
{
myDataGrid.SelectedIndex++; // Go one position Down
}
}
}
So, when the textbox is focused and the user press the Up or the Down key, the selection does not appear to change.
Any idea of how I can make the selected item on the datagrid change it's color to blue?
Other thing: in my virtual machine, it works!! With the same code! How it's possible?
I think that is the aeroglass, but I change the theme to the Windows 7 Basic (same in the virtual machine) and still don't work.
Thanks, and sorry for my english.

Could you try using SelectedItem? you could always create a new property and bind to this and then set this item directly rather than using the selected index. Hopefully this would trigger any additional logic in the DataGrid control :)
//Declare property outside of method
public ObjectType SelectedItem { get; set; }
//Set datacontext on load
DataContext = this;
myDatagrid.ItemsSource = myListOfObjects.Where(item => item.Name.Contains(MyTextBox.Text)); //Filter
if (myDatagrid.Items.Count > 0) // If no itens, then do nothing
{
SelectedItem = myDatagrid.ItemSource[0]; // If has at least one item, select the first
}
myDatagrid.Items.Refresh();
Also don't forget to set your binding!
SelectedItem="{Binding SelectedItem}"
hope that helps!

Related

Reset comboxbox selection after a selection is made

I have a combobox that is created with data from a dataset
foreach(var item in ds.MiDESValues)
{
string comboboxtext = ds.MiDESValues.Rows[k][1].ToString();
sFactorCB.Items.Add(comboboxtext);
k++;
}
On a selectionchanged event it will populate a listbox with that selection
private void sFactors_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string add = sFactorCB.SelectedValue.ToString();
var svalue = ds.MiDESValues.Rows[0][2].ToString();
int Svalue = int.Parse(svalue);
SValue.Add(svalue);
SelectionListBox.Items.Add(add);
SelectionBox.Add(add);
// when a new item is added to Selection list box, select it and show it
// this will keep the last item highlighted and as the list grows beyond
// the view of the list box, the last item will always be shown
SelectionListBox.SelectedIndex = SelectionListBox.Items.Count - 1;
SelectionListBox.ScrollIntoView(SelectionListBox.SelectedItem);
}
That list box then used to populate a listbox used on the next page. If I navigate to the next page and then navigate back, the combobox is still showing the last selection I made therefor the listbox is being populated with that value.
I have tried setting the selectedindex of the combobox to sFactorCB.SelectedIndex = -1;, at the end of the sFactors_SelectionChanged event but i get System.NullReferenceException. How can I get the combobox to reset back to a non-selected item state? Thanks
Actually, you are doing it correctly. To clear the selection either set the SelectedIndex to -1 or the SelectedItem to null.
The problem is that once you do that your sFactors_SelectionChanged gets called again and since there is no current selection, the SelectedValue property is null thus causing the following to fail:
string add = sFactorCB.SelectedValue.ToString();
How to solve this sort of depends on your intended results when nothing is selected. The simplest thing to do would be to just check for nothing selected at the start of the handler and simply return.
private void sFactors_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sFactorCB.SelectedIndex == -1 || sFactorCB.SelectedValue == null)
return;
string add = sFactorCB.SelectedValue.ToString();
...

How to retain Focus of the first row after sorting and searching in the datagrid?

When my datagrid loads up I am able to get the focus of the first row by providing selected index=0 in the xaml but when I perform searching the focus gets lost so I want focus to be retain at the first row no matter I do sorting and searching on the datagrid.
Here is my code that searches particular thing in the datagrid.
private void TextBox_TextChanged(object sender, RoutedEventArgs e)
{
TextBox STB = (TextBox)sender;
this.SearchValue = STB.Text;
//ContentPresenter CP = (ContentPresenter)STB.TemplatedParent;
//DataGridColumnHeader DGCH = (DataGridColumnHeader)CP.TemplatedParent;
//DataGridColumn DGC = DGCH.Column;
//this.ColumnName = DGC.Header.ToString();
this.Datalist.Filter = this.CustomeFilter;
DataGrid dataGrid = this as DataGrid;
dataGrid.CurrentCell = new DataGridCellInfo(
dataGrid.Items[0], dataGrid.Columns[0]);
dataGrid.BeginEdit();
}
In above code I am trying to get the focus of the current cell but all in vain.
private bool CustomeFilter(object item)
{
SymbolData ltpObj = item as SymbolData;
//WpfApplication1.Model.LtpMessage ltpObj = item as WpfApplication1.Model.LtpMessage;
string values = (string)ltpObj.Symbol.ToString();
values = values.ToUpper();
//return values.StartsWith(this.SearchValue.ToString().ToUpper());
if (values.StartsWith(this.SearchValue.ToString().ToUpper()))
{ return true; }
else
return false;
}
You should read this:
How to programmatically select and focus a row or cell in a DataGrid in WPF: https://blog.magnusmontin.net/2013/11/08/how-to-programmatically-select-and-focus-a-row-or-cell-in-a-datagrid-in-wpf/
You can select and focus a row or cell of a DataGrid programmatically and get the same behaviour as when using the mouse by accessing the visual user interface elements of the DataGrid control and calling the UIElement.Focus() method on a particular DataGridCell object as described in the blog post above. There are code samples included.
You cannot simply set the SelectedItem or SelectedIndex property of the DataGrid to focus the row or cell though.

Windows Forms: Manually paint SelectedItem of ComboBox

I have a ComboBox (ToolStripCombobox, to be more precise) filled with items of type KeyValuePair<Int32, FontFamily>. I managed to have the Items beeing painted manually by using the DrawItem event. So every Item is painted with the FontFamily of the corresponding KeyValuePair. This works fine for the DropDownList, but when I select an Item out of the List and the list closes, the text in the ComboBox says something like "[21, [FontFamily: Name=Arial]]" which is most likely the result of SelectedItem.ToString().
Any ideas how to solve this problem?
here is the code of my custom DrawItem method:
private void fontComboBoxDrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
if ((e.State & DrawItemState.Focus) != 0)
{
e.DrawFocusRectangle();
}
Brush objBrush = null;
var itemToDraw = this.fontComboBox.Items[e.Index];
KeyValuePair<Int32, FontFamily> windowsFontItem = (KeyValuePair<Int32, FontFamily>)itemToDraw;
objBrush = new SolidBrush(e.ForeColor);
e.Graphics.DrawString(windowsFontItem.Value.Name, new Font(windowsFontItem.Value, e.Font.Size), objBrush, e.Bounds);
if (objBrush != null)
{
objBrush.Dispose();
}
objBrush = null;
}
Update:
It works as expected, when I set the DropDownStyle of the ComboBox to ComboBoxStyle.DropDownList
But I´d rather use ComboBoxStyle.DropDown, so you can edit the Text to search for Fonts.

Set the content of a new row Datagrid

I have a DataGrid showing some databases having quite some columns.
I would like that, when the user edit a new row, some values are set automatically.
With the windows form DataGrid that would be easy, since there's RowsAdded event handler.
But how could i handle this with the wpf DataGrid ??
Edit : my DataGrid is bound in Xaml to a public property which is an ITable. When user select a table in a ComboBox, the property is updated with corresponding table.
Yes there's autogenerating column, and the way the user can enter a new row is to edit the last blank row (default behaviour).
You can do this in the LoadingRow event. Try something like this:
private void myDataGrid_LoadingRow(object sender, System.Windows.Controls.DataGridRowEventArgs e)
{
MyObject myObject = e.Row.Item as MyObject;
if (myObject != null)
{
myObject.PropertyOne = "test";
myObject.PropertyTwo = 2;
}
}
Ok i think i got it.
When a DataTable is bound to a DataGrid, a CollectionView is created in order to see it. You can get it by using the (static/shared) CollectionView.GetDefaultView(ThePropertyThatIsBound) method.
Since it implements ICollectionChanged, you can add an event handler to the CollectionChangedEvent.
In the CollectionChanged event handler, if you have a new item (e.NewItems.Count>0) you must check it against System.Windows.Data.CollectionView.NewItemPlaceholder and if it is not a place holder, then it is a brand new item, for wich i can set all default values.
Assign a CollectionViewSource to your DataGrid then listen to the CollectionChanged event as following :
..
public CollectionViewSource ViewSource { get; set; }
..
this.ViewSource = new CollectionViewSource();
this.ViewSource.Source = new List<YourObjectType>();
this.ViewSource.View.CollectionChanged += View_CollectionChanged;
..
private void View_CollectionChanged(object sender,System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems.Count > 0)
{
YourObjectType myObject = e.NewItems[e.NewItems.Count-1] as YourObjectType;
if (myObject != null)
{
myObject.Property = TheValueYouWant;
..
}
}
}
..
<DataGrid ItemsSource="{Binding ViewSource.View}" ../>

WPF Datagrid -- programmatic selection of row seems to break multi-select (shift-click multiselect, specifically)

I have a WPF DataGrid control with a SelectionUnit of "FullRow" and SelectionMode of "Extended" that I'm programmatically selecting an item in (the first item, usually). The selection works, but for some reason any form of programmatic selection seems to break the shift-select multiselect ability.
If I single click another item in the DataGrid (so the item I just clicked is the only item selected), then shift-select will work. It only seems to break if I've programmatically selected the item. Additionally, control-click works to select multiple items in either case -- it seems to only be shift-select that is broken.
I've tried various forms of programmatically selecting the single item, from as simple as myGrid.SelectedIndex = 0, to using the DataGrid's ItemContainerGenerator to get an instance of the DataGridRow object and setting IsSelected = true on it, but to no avail.
To re-iterate -- programmatic selection of an item works, but it breaks shift-click selection.
Has anyone run into this before? I've tried setting focus on the DataGridRow instance that is programmatically selected, but it doesn't seem to help?
I succeeded to work around this problem using reflection:
var method = typeof(DataGrid).GetMethod("HandleSelectionForCellInput", BindingFlags.Instance | BindingFlags.NonPublic);
method.Invoke(MyDataGrid, new object[] { cellToSelect, false, false, false });
I struggled with this problem for multiple days and tried a lot of things that I found on the internet. In the end, I found the solution that works for me by studying the source code of the DataGrid.
In the DataGrid I noticed a member variable called _selectionAnchor and guessed that this must be the starting point for when a user expands the selection in the grid. My solution is to set this member to the first cell of the row that is selected. If a row is selected in code, than this fix makes sure that when expanding the selection it starts at the selected row.
Please note that I used the code from this issue to enable multiselect. Then, in file MainWindow.xaml.cs, I added this code:
private void ExampleDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (ExampleDataGrid.SelectedItems.Count > 0)
{
ExampleDataGrid.ScrollIntoView(ExampleDataGrid.SelectedItems[0]);
// Make sure that when the user starts to make an extended selection, it starts at this one
foreach (var cellInfo in ExampleDataGrid.SelectedCells)
{
if (cellInfo.Column.DisplayIndex == 0)
{
var cell = GetDataGridCell(cellInfo);
cell?.Focus();
var field = typeof(DataGrid).GetField("_selectionAnchor", BindingFlags.NonPublic | BindingFlags.Instance);
field?.SetValue(ExampleDataGrid, cellInfo);
break;
}
}
}
}
public DataGridCell GetDataGridCell(DataGridCellInfo cellInfo)
{
var cellContent = cellInfo.Column.GetCellContent(cellInfo.Item);
if (cellContent != null)
{
return (DataGridCell)cellContent.Parent;
}
return null;
}
In the xaml file:
<vm:CustomDataGrid x:Name="ExampleDataGrid" ItemsSource="{Binding ImportItems}"
SelectedItemsList="{Binding SelectedImportItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
AutoGenerateColumns="False" SelectionMode="Extended" IsReadOnly="True" CanUserAddRows="False"
SelectionChanged="ExampleDataGrid_SelectionChanged">
Remember there is a difference between focus and keyboard focus. When you select the item in code, check to see what control has Keyboard focus / regular focus. I'm guessing that the data grid loses this focus until you click on it with the mouse and then it regains the focus needed to use the ctrl function.
I ran into this issue in a WPF user control we were hosting inside a C++ application.
I just resolved exactly the same problem with the help of #ezolotko's snippet.
Because the grid is dynamically generating rows I needed to subscribe to ItemContainerGenerator.StatusChanged event and find the first cell in a row representing this element.
To find the cell I used DataGridHelper class and wrapped it all in an attached behaviour:
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using Speedwell.WPF.Helpers;
namespace Speedwell.WPF.Behaviors
{
public static class DataGridSingleRowSelected
{
public static readonly DependencyProperty IsSelectionFixEnabledProperty = DependencyProperty.RegisterAttached
(
"IsSelectionFixEnabled",
typeof(bool?),
typeof(DataGridSingleRowSelected),
new PropertyMetadata(null, IsSelectionFixEnabledChanged)
);
public static bool GetIsSelectionFixEnabled(DataGrid element)
{
return (bool)element.GetValue(IsSelectionFixEnabledProperty);
}
public static void SetIsSelectionFixEnabled(DataGrid element, bool value)
{
element.SetValue(IsSelectionFixEnabledProperty, value);
}
private static void IsSelectionFixEnabledChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var dataGrid = sender as DataGrid;
if(dataGrid != null)
{
if(args.OldValue == null)
{
dataGrid.ItemContainerGenerator.StatusChanged += (s, e) => ContainerStatusChanged(dataGrid, ((ItemContainerGenerator)s));
}
}
}
private static void ContainerStatusChanged(DataGrid dataGrid, ItemContainerGenerator generator)
{
if(generator != null && generator.Status == GeneratorStatus.ContainersGenerated && dataGrid.SelectedItems.Count == 1)
{
var row = (DataGridRow)dataGrid.ItemContainerGenerator.ContainerFromItem(dataGrid.SelectedItems[0]);
if(row != null)
{
var cell = dataGrid.GetCell(row, 0);
if(cell != null)
{
SelectCellMethod.Invoke(dataGrid, new object[] { cell, false, false, false });
}
}
}
}
private static readonly MethodInfo SelectCellMethod = typeof(DataGrid).GetMethod("HandleSelectionForCellInput", BindingFlags.Instance | BindingFlags.NonPublic);
}
}
As you can see the proper selection is only applied when there is a single (1) row selected and this is exactly what I need and it seems it also what #Jordan0Day requested.

Resources