Nhibernate How to Query Against an Ilist<String> Property

Set Item Focus in ListView WPF

There are two types of focus in WPF - Keyboard Focus and Logical Focus. This link can give you more information about focus in WPF.

You can either do this:

ListViewItem item = myListView.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
item.Focus();

It's also possible to call

Keyboard.Focus(item); 

If you also want to scroll the ListView to the item's position, add this:

myListView.ScrollIntoView(item);

IMPORTANT NOTE: For this to work, you will need to set VirtualizingStackPanel.IsVirtualizing="False" on your ListView, which may cause it to perform slower. The reason this attached property is required is that when the ListView is virtualized (which it is by default), the ListViewItems aren't created for items that aren't displayed on the screen, which will cause ContainerFromIndex() to return null.

WPF ListView Select and get Item Focus automatically

I finally figure it out. Below is my current working code.

In the Model I have just changed the flag IsSelected to IsCurrent to avoid confusion with ListViewItem built-in property but it might just be an implementation detail.

public class SmartDeviceModel : INotifyPropertyChanged
{
public bool IsCurrent;
[...]
}

The BindingList in ViewModel is mostly the same as in OP:

public class ScanDeviceViewModel : INotifyPropertyChanged
{
public BindingList<SmartDeviceModel> ReaderList { get; internal set; }
[...]
}

NB : BindingList seems to reduce OnNotifyPropertyChange need but other Type of List should work with a tiny bit of extra code. I also noticed BindingList might not be suited for huge list scenario.

The View is then using the above ViewModel as DataContext and therefore Binding ItemSource to the BindingList. The ListViewItem Style Setter is then using the IsCurrent Property from the Model.

 <ListView ItemsSource="{Binding ReaderList}"  
SelectionMode="Single"
SelectionChanged="OnListViewSelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsSelected" Value="{Binding IsCurrent}" />
</Style>
</ListView.ItemContainerStyle>
[...]

And finally this piece of View Code behind below is mainly to simulate the focus as per user input, otherwise the elemant get selected but not focused and might be outside the visible item scope :

private void OnListViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
ListView listView = e.Source as ListView;
if (listView.ItemContainerGenerator.ContainerFromItem(listView.SelectedItem) is FrameworkElement container)
{
container.Focus();
}
}

WPF - Set Keyboard Focus to ListView Item like Tab does

You could try to focus the first item in the ListView:

ListView.SelectedIndex = 0;
ListViewItem lvi = ListView.ItemContainerGenerator.ContainerFromIndex(0) as ListViewItem;
if (lvi != null)
lvi.Focus();

How to set focus on the first item of a ListView?

The problem is solved by calling UpdateLayout() method before trying to get a ListViewItem.

WPF: ListView Cannot Get Focus when Click in Blank Area

An easy workaround is to handle clicks:

<ListView PreviewMouseLeftButtonDown="ListView_PreviewMouseLeftButtonDown" ... />

to set focus

void ListView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) =>
(sender as ListBox)?.Focus();

Note: this will cause GotFocus to be called twice when clicking on normal items.

how to focus on the keyboard on a textbox in WPF listview after item.refresh

I don't went through your whole code but only those parts that are relevant to your question. To simplify things, I will use the attached property TabNavigation and the data binding feature. The binding will automatically update the control values so that the refreshing of the ListView and the focus handling becomes redundant.

Remark: To shorten the XAML code I will only show the relevant code portions.

Tab navigation

Set the attached property KeyboardNavigation.TabNavigation on the ListView to Continue so that the focus will loop through the list:

  <ListView x:Name="DetailsList"
KeyboardNavigation.TabNavigation="Continue"
...
</ListView>

You can control which elements can receive focus on Tab key pressed by setting the property IsTabStop on the elements. The default value is True. So set it to False on those elements you wish to exclude. It's useful to disable Tab focus on the ListViewItem itself so that the Tab key is only effective for the containing controls:

    <ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="IsTabStop"
Value="False" />
...
</Style>
</ListView.ItemContainerStyle>

If you wish to exclude the Button as well, so that you only jump between the TextBox elements, set the Button's IsTabStopproperty to False as well:

        <GridViewColumn Width="30">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Button x:Name="BT_DeleteDetail"
IsTabStop="False"
...
/>
...
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>

If you also wish to change the order the elements will receive focus on Tab key pressed, you can do it by setting the element's TabIndex property. A control with a lower tab index receives focus before a control with a higher index.

Binding the TextBox

To enable binding to a data model it is required that the data model (view model) implements INotifyPropertxChanged:

public class DetailItem : INotifyPropertyChanged
{
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public event PropertyChangedEventHandler PropertyChanged;

private string mark;
public string Mark
{
get => this.mark;
set
{
if (value == this.mark) return;
this.mark = value;
OnPropertyChanged();
}
}

private string reference;
public string Reference
{
get => this.reference;
set
{
if (value == this.reference) return;
this.reference = value;
OnPropertyChanged();
}
}

private string detail;
public string Detail
{
get => this.detail;
set
{
if (value == this.detail) return;
this.detail = value;
OnPropertyChanged();
}
}

private string explanation;
public string Explanation
{
get => this.explanation;
set
{
if (value == this.explanation) return;
this.explanation = value;
OnPropertyChanged();
}
}
}

In order access the view model from your DataTemplate set its DataType property to the type of the view model (DetailItem). Remember to always set the DataType of a DataTemplate. Then add the Binding to the TextBox. Note that the Mode (direction) of the binding is set to OneWayToSource. This limits the binding to set data from TextBoxto DetailItem only:

          <GridViewColumn.CellTemplate>
<DataTemplate DataType="viewModels:DetailItem">
<TextBox x:Name="RTB_Reference"
Text="{Binding Reference, Mode=OneWayToSource}"
...
</TextBox>
...
</DataTemplate>
</GridViewColumn.CellTemplate>

<GridViewColumn.CellTemplate>
<DataTemplate DataType="viewModels:DetailItem">
<TextBox x:Name="RTB_Detail"
Text="{Binding Detail, Mode=OneWayToSource}"
...
</TextBox>
...
</DataTemplate>
</GridViewColumn.CellTemplate>

The final step is to update the DetailItem.Explanation property. To do this, we move the UpdateExplanation() method into the view model DetailItem. Since we use binding we can now get rid of all the ListView refresh and focus logic inside, so the method becomes smaller. Note that since we moved the method into the DetailItem class, we can also remove the parameter of the method:

private void UpdateExplanation()
{
this.Explanation = GetExplanation(this.Reference, this.Detail);
}

The UpdateExplanation() method is invoked directly from the setter of the Reference and Detail property, so that everytime these properties are changed the Explanation value will be updated. With the updated Reference and Detail setter the final version of the DetailItem class will then look as followed:

public class DetailItem : INotifyPropertyChanged
{
private void UpdateExplanation()
{
this.Explanation = GetExplanation(this.Reference, this.Detail);
}

protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public event PropertyChangedEventHandler PropertyChanged;

private string mark;
public string Mark
{
get => this.mark;
set
{
if (value == this.mark) return;
this.mark = value;
OnPropertyChanged();
}
}

private string reference;
public string Reference
{
get => this.reference;
set
{
if (value == this.reference) return;
this.reference = value;
OnPropertyChanged();
UpdateExplanation();
}
}

private string detail;
public string Detail
{
get => this.detail;
set
{
if (value == this.detail) return;
this.detail = value;
OnPropertyChanged();
UpdateExplanation();
}
}

private string explanation;
public string Explanation
{
get => this.explanation;
set
{
if (value == this.explanation) return;
this.explanation = value;
OnPropertyChanged();
}
}
}

WPF - Set Focus after click on ListView

You can do this directly in xaml without behaviors. In the Style that you defined under ListView.ItemContainerStyle, add a Trigger. When the IsSelected property of the ListViewItem is true, apply focus to your txtPassword TextBox element. Like this:

<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FocusManager.FocusedElement" Value="{Binding ElementName=txtPassword}"/>
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>

Set initial keyboard focus in Controls.ListView

The trick seems to be to explicitly set the focus to the item container of the first item in the ListView. I found a good explanation here.

Short summary of that blog post:

Because the items aren't available directly after creating the ListView focusing has to happen after all items are generated in the background. So, attach to the StatusChanged event of the ItemContainerGenerator:

Persons.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;

And in the event handler, set the focus after everything has finished generating:

private void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if (Persons.ItemContainerGenerator.Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated)
{
int index = Persons.SelectedIndex;
if (index >= 0)
((ListViewItem)Persons.ItemContainerGenerator.ContainerFromIndex(index)).Focus();
}
}

This solution isn't as simple as I hoped it would be but it worked for me.

WPF Listbox focus from viewmodel

I ended up with the following code in control's codebehind:

public void FixListboxFocus()
{
if (lbFiles.SelectedItem != null)
{
lbFiles.ScrollIntoView(lbFiles.SelectedItem);
lbFiles.UpdateLayout();

var item = lbFiles.ItemContainerGenerator.ContainerFromItem(viewModel.SelectedFile);
if (item != null && item is ListBoxItem listBoxItem && !listBoxItem.IsFocused)
listBoxItem.Focus();
}
}

This method is available for calling from within viewModel, which calls it every time it sets the selection:

var file = files.FirstOrDefault(f => f.Path.Equals(subfolderName, StringComparison.OrdinalIgnoreCase));
if (file != null)
SelectedFile = file;
else
SelectedFile = files.FirstOrDefault();

access.FixListboxFocus();

The access is view passed to ViewModel via interface (to keep separation between presentation and logic). The relevant XAML part looks like following:

<ListBox x:Name="lbFiles" ItemsSource="{Binding Files}" SelectedItem="{Binding SelectedFile}" />


Related Topics



Leave a reply



Submit