Reputation: 13206
I am currently having an issue with my code where the focus on a particular item row will not hold. I created the int focusReference
to keep track of the currently focused row, but it appears to reset to 0 after every couple of seconds, i.e. once the user selects a row, in a couple of seconds, it "forgets" the users selection. I've included an example below:
As you can see from my code below, I have a timer doing something every few seconds. I've already been told that refreshing my list every few seconds is leading to the loss in focus every few seconds.
How would I programmatically set focus on an item, to retain that focus a user has? I've tried to implement a FocusItem
method but it doesn't seem to be working. This is a very important aspect of my program, and I need it to work properly otherwise other features (like the right click context menu I am implementing), will also not work:
public MainWindow()
{
InitializeComponent();
int focusReference = 0;
PlotListView.SelectionChanged += (s, ee) => { PlotListView_SelectionChanged(s, ee, focusReference); };
var dbObject = new DbConnect();
dbObject.OpenConnection();
RefreshPlotTimer(filterReference, focusReference);
}
public void PlotListView_SelectionChanged(object sender, SelectionChangedEventArgs e, int focusReference)
{
if (PlotListView.SelectedItems.Count == 0) return;
var selectedItem = (DbConnect.PlotList)PlotListView.SelectedItems[0];
focusReference = Convert.ToInt32(selectedItem.PlotId);
FocusItem(focusReference);
}
private void FocusItem(int focusReference)
{
if (PlotListView.SelectedItems.Count != 0)
{
DbConnect.PlotList plotList =
PlotListView.Items.OfType<DbConnect.PlotList>()
.FirstOrDefault(p => Convert.ToInt32(p.PlotId) == focusReference);
if (plotList != null)
{
//get visual container
var container = PlotListView.ItemContainerGenerator.ContainerFromItem(plotList) as ListViewItem;
if (container != null)
{
container.IsSelected = true;
container.Focus();
}
}
}
}
public void RefreshPlotTimer(int filterReference, int focusReference)
{
var refreshTimer = new Timer();
refreshTimer.Elapsed += (sender, e) => RefreshPlot(sender, e, filterReference, focusReference);
refreshTimer.Interval = 2500;
refreshTimer.Enabled = true;
}
public void RefreshPlot(object source, ElapsedEventArgs e, int filterReference, int focusReference)
{
var dbObject = new DbConnect();
dbObject.OpenConnection();
dbObject.RefreshPlot();
Dispatcher.Invoke(() =>
{
FocusItem(focusReference);
if (!string.IsNullOrWhiteSpace(FilterTextBox.Text) &&
(!Regex.IsMatch(FilterTextBox.Text, "[^0-9]")))
{
filterReference = Convert.ToInt32(FilterTextBox.Text);
}
});
ResetPlot(filterReference);
}
public void ResetPlot(int filterReference)
{
var dbObject = new DbConnect();
dbObject.OpenConnection();
List<DbConnect.PlotList> plotList = dbObject.SelectPlotLists(filterReference);
Dispatcher.BeginInvoke(
new ThreadStart(() => PlotListView.ItemsSource = plotList));
int jobSum = 0;
int bidSum = 0;
foreach (DbConnect.PlotList item in PlotListView.Items)
{
jobSum += Convert.ToInt32(item.Jobs);
bidSum += Convert.ToInt32(item.Bids);
}
Dispatcher.BeginInvoke(
new ThreadStart(() => JobBidRatioTextBlock.Text = jobSum + " jobs - " + bidSum + " bids"));
}
UPDATE 2
I reverted back to the code I was using initially, could it be that the FocusItem method is not retaining the listview item focus?
private void FocusItem(int focusReference)
{
Dispatcher.Invoke(() =>
{
if (PlotListView.SelectedItems.Count != 0)
{
DbConnect.PlotList plotList =
PlotListView.Items.OfType<DbConnect.PlotList>()
.FirstOrDefault(p => Convert.ToInt32(p.PlotId) == focusReference);
if (plotList != null)
{
//get visual container
var container = PlotListView.ItemContainerGenerator.ContainerFromItem(plotList) as ListViewItem;
if (container != null)
{
container.IsSelected = true;
container.Focus();
}
}
}
}
}
I'm calling it at the right places and the index is definitely the right one, according to what is written in the console.
PlotListView_SelectionChanged 4
Before refresh 4
After refresh 4
PlotListView_SelectionChanged 7
Before refresh 7
After refresh 7
Before refresh 7
After refresh 7
Both in the refresh plot method...
public void RefreshPlot(object source, ElapsedEventArgs e)
{
var dbObject = new DbConnect();
dbObject.OpenConnection();
dbObject.RefreshPlot();
Console.WriteLine("\rBefore refresh " + focusReference);
Dispatcher.Invoke(() =>
{
if (!string.IsNullOrWhiteSpace(FilterTextBox.Text) &&
(!Regex.IsMatch(FilterTextBox.Text, "[^0-9]")))
{
filterReference = Convert.ToInt32(FilterTextBox.Text);
}
});
ResetPlot(filterReference);
Console.WriteLine("After refresh " + focusReference);
FocusItem(focusReference);
}
And the reset plot method...
public void ResetPlot(int filterReference)
{
// Establish MySQL connection
var dbObject = new DbConnect();
dbObject.OpenConnection();
// Fill plot list view
List<DbConnect.PlotList> plotList = dbObject.SelectPlotLists(filterReference);
Dispatcher.BeginInvoke(
new ThreadStart(() => PlotListView.ItemsSource = plotList));
int jobSum = 0;
int bidSum = 0;
foreach (DbConnect.PlotList item in PlotListView.Items)
{
jobSum += Convert.ToInt32(item.Jobs);
bidSum += Convert.ToInt32(item.Bids);
}
FocusItem(focusReference);
// Determine job/bid list ratio
Dispatcher.BeginInvoke(
new ThreadStart(() => JobBidRatioTextBlock.Text = jobSum + " jobs - " + bidSum + " bids"));
}
Upvotes: 4
Views: 1080
Reputation: 18675
Before doing the refresh you need to remember the SelectedItem
and set it agian after the refresh. That's the theory.
In case of objects you'll need to find which one is now the same as the one before (because it's not the same instance anymore) so you'll have to search for it in the new results list and assign it to the SelectedItem
.
The easiest way would be to just re-set the SelectedIndex
but because the list probably changes it would also sooner or later re-select the wrong item.
// Gets current selection.
public DbConnect.PlotList SelectedPlotList
{
get
{
return PlotListView.SelectedItem as DbConnect.PlotList;
}
}
public void ResetPlot(int filterReference)
{
// Get current plot number;
int? plotNumber = SelectedPlotList == null ? (int?)null : SelectedPlotList.PlotNumber;
var dbObject = new DbConnect();
dbObject.OpenConnection();
List<DbConnect.PlotList> plotList = dbObject.SelectPlotLists(filterReference);
// Find the plot list in the new list.
DbConnect.PlotList selectPlotList =
plotNumber.HasValue
? plotList.Where(x => x.PlotNumber == plotNumber.Value).FirstOrDefault();
: null;
Dispatcher.BeginInvoke(new ThreadStart(() => PlotListView.ItemsSource = plotList));
// Re-select plot list if found in the new list.
if (selectPlotList != null)
{
PlotListView.SelectedItem = selectPlotList;
}
int jobSum = 0;
int bidSum = 0;
foreach (DbConnect.PlotList item in PlotListView.Items)
{
jobSum += Convert.ToInt32(item.Jobs);
bidSum += Convert.ToInt32(item.Bids);
}
Dispatcher.BeginInvoke(
new ThreadStart(() => JobBidRatioTextBlock.Text = jobSum + " jobs - " + bidSum + " bids"));
}
EDIT:
I've just tested a quick & dirty demo and it works. There must be a bug somewhere else.
This generates 7 items with unique ids and re-selects the last selected item if it is drawn again:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
List<ListViewItem> items = new List<ListViewItem>();
Random rnd = new Random(DateTime.Now.Millisecond);
HashSet<int> ids = new HashSet<int>();
for (int i = 0; i < 7; i++)
{
int id = 0;
do
{
id = rnd.Next(0, 10);
} while (ids.Contains(id));
ids.Add(id);
items.Add(new ListViewItem() { Id = id, Name = "Item-" + i });
}
int? selectedId = listView1.SelectedItem != null ? (listView1.SelectedItem as ListViewItem).Id : (int?)null;
listView1.ItemsSource = items;
if (selectedId.HasValue)
{
listView1.SelectedItem = items.Where(x => x.Id == selectedId).FirstOrDefault();
listView1.Focus();
}
}
}
class ListViewItem
{
public int Id { get; set; }
public string Name { get; set; }
}
Upvotes: 2