Initial Focus on selected ListViewItem - c#

I have a simple dialog with MVVM, just a ListView and two Buttons (Ok, Cancel) and a ViewModel responsible for binding the ItemsSource and SelectedItem of the ListView.
View:
...
<StackPanel>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ListView ItemsSource="{Binding Projects}" SelectedItem="{Binding Project}" SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
<StackPanel Orientation="Horizontal" FlowDirection="RightToLeft">
<Button Content="Cancel" IsCancel="True"/>
<Button Content="Ok" Command="{Binding OkCommand}" CommandParameter="{Binding ElementName=Dlg}"/>
</StackPanel>
</StackPanel>
...
Viewmodel:
public class SelectProjectViewModel : ViewModelBase, IModalDialogViewModel
{
private readonly ProjectList _projectList;
public ProjectList Projects => _projectList;
public Project Project
{
get { return GetValue(() => Project); }
set { SetValue(() => Project, value);}
}
private RelayCommand _okCommand;
public RelayCommand OkCommand => _okCommand ?? (_okCommand = new RelayCommand(OkCommandCall));
public SelectProjectViewModel(ProjectList projectList, Project currentProject)
{
_projectList = projectList;
this.Project = currentProject;
}
private void OkCommandCall(object window)
{
DialogResult = true;
WindowCloseBehaviour.SetClose(window as DependencyObject, true);
}
public bool? DialogResult
{
get { return GetValue(() => DialogResult); }
private set { SetValue(() => DialogResult, value); }
}
}
I tried to achieve focusing the first Element like described here, with the accepted solution and the equivalent behaviour class. But this always sets the focus on the Cancel-Button, while I want my ListView to be focused when opening the dialog, so that the selected element is really highlighted, not just in the "inactive highlight" color. And no, I don't want to workaround with changing the colors.

Using simple FocusedElement on a parent with SelectedIndex on ListView itself works for me:
<StackPanel FocusManager.FocusedElement="{Binding ElementName=myListView}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ListView Name="myListView" ItemsSource="{Binding Projects}" SelectedItem="{Binding Project}" SelectionMode="Single"
SelectedIndex="0">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
...
</StackPanel>
</StackPanel>

Related

How to insert items inside listbox withing another listbox on button click

I have a Listbox which is bound to a DataTemplate that has another Listbox on it.
On DataTemplate there is a button that I want to use for adding items to DataTemplate ListBox, but I can't find a solution to do this.
Here is my listbox:
<Button Width="200" Content="Add Question" x:Name="btnAddQuestion" Click="btnAddQuestion_Click"/>
<StackPanel Orientation="Horizontal">
<ListBox Margin="5" x:Name="lvQuestions" ItemTemplate="{StaticResource TemplateQuestionTitle}">
</ListBox>
</StackPanel>
And this is DataTemplate:
<DataTemplate x:Key="TemplateQuestionTitle">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBox materialDesign:HintAssist.Hint="Enter question" MinWidth="200" Style="{StaticResource MaterialDesignFloatingHintTextBox}"/>
<Button Content="+" Command="{Binding Source={x:Reference ThisPage},Path=DataContext.Command}" />
</StackPanel>
<ListBox ItemsSource="{Binding MyItems}" MinHeight="50">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox>
</TextBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
This is code behind on my page:
public partial class UIBuilder:Window
{
private CommandVm _commandVm;
public UIBuilder()
{
InitializeComponent();
_commandVm = new CommandVm();
DataContext = _commandVm;
}
private void btnAddQuestion_Click(object sender, RoutedEventArgs e)
{
lvQuestions.Items.Add(null);
}
}
I have implemented this code on my ViewModel in order to add items to datatemplate ListBox:
public class CommandVm
{
public ObservableCollection<TextBox> MyItems { get; set; }
public CommandVm()
{
MyItems = new ObservableCollection<TextBox>();
Command = new RelayCommand<TextBox>(Execute);
}
private void Execute(TextBox textBox)
{
MyItems .Add(textBox);
}
public ICommand Command { get; set; }
}
I use to catch the Execute() function on button "+" click command, but my code doesn't add any ListBox item.
MyItems is a property of the parent view model which means that you should bind to it like this:
<ListBox ItemsSource="{Binding DataContext.MyItems,
RelativeSource={RelativeSource AncestorType=Window}}" MinHeight="50">
This also means that you are using one single collection of items for all questions. Besides this obvious design flaw, a view model should not contain any TextBox elements. This basically breaks what the MVVM pattern is all about.
What you should do to make this example MVVM compliant is to create a Question class that has a collection of items, e.g.:
public class Question
{
public Question()
{
AddAnswerCommand = new RelayCommand<object>(Execute);
}
private void Execute(object obj)
{
Items.Add(new Answer());
}
public ObservableCollection<Answer> Items { get; }
= new ObservableCollection<Answer>();
public ICommand AddAnswerCommand { get; }
}
public class Answer { }
The window's view model should then have a collection of questions:
public class CommandVm
{
public CommandVm()
{
AddQuestionCommand = new RelayCommand<object>(Execute);
}
public ObservableCollection<Question> Questions { get; }
= new ObservableCollection<Question>();
public ICommand AddQuestionCommand { get; }
private void Execute(object obj)
{
Questions.Add(new Question());
}
}
The view and the bindings could then be defined like this:
<Window.Resources>
<DataTemplate x:Key="TemplateQuestionTitle">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBox MinWidth="200" />
<Button Content="+" Command="{Binding AddAnswerCommand}" />
</StackPanel>
<ListBox ItemsSource="{Binding Items}" MinHeight="50">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<Button Width="200" Content="Add Question" Command="{Binding AddQuestionCommand}"/>
<ListBox Margin="5"
ItemsSource="{Binding Questions}"
ItemTemplate="{StaticResource TemplateQuestionTitle}" />
</StackPanel>
This setup lets you add individual elements to each separate question.

WPF listbox hierarchial item template

I have a list box that is bound to hierarchial collection. The data has list menu items. There are two types of menus. First one is grouping menu, which will have child items. Second one is menu items which can be selected and this one does not have any children.
I have a property names HasChildren to distinguish between different type of menu. I want to apply different type of template for both types of menu. I want to show buttons for selectable(second) menus and tree view for menu with chidlren.
<ListBox ItemsSource="{Binding UserMenus}">
<ListBox.ItemTemplate>
<DataTemplate>
<TreeViewItem ItemsSource="{Binding Children}" Header="{Binding Name}">
<TreeViewItem.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<StackPanel>
<Button Width="250" Content="{Binding Name}" ToolTip="{Binding Name}" Style="{StaticResource MaterialDesignRaisedButton}"
Command="{Binding DataContext.TemplateCommand, RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding Id}"/>
</StackPanel>
</HierarchicalDataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I tried this approach but it is not working for top level menu items, it is displaying tree view item for both types of menus.
My hierarchial data class.
public class MenuModel : ObservableObject, IMenuModel
{
string _name, _description, _id;
public MenuModel()
{
Children = new ObservableCollection<IMenuModel>();
}
public string Name { get => _name; set => SetProperty(ref _name, value); }
public string Description { get => _description; set => SetProperty(ref _description, value); }
public string Id { get => _id; set => SetProperty(ref _id, value); }
public bool HasChildren
{
get
{
bool returnValue = false;
if (Children != null && Children.Count > 0)
{
returnValue = true;
}
return returnValue;
}
}
public ObservableCollection<IMenuModel> Children { get; set; }
}
Help me to apply template properly to achieve desired result.
You can try this:
Xaml
<ListBox ItemsSource="{Binding UserMenus}">
<ListBox.Resources>
<HierarchicalDataTemplate x:Key="DataTemplate2" ItemsSource="{Binding Children}">
<StackPanel>
<Button Width="250" Content="{Binding Name}" ToolTip="{Binding Name}" Style="{StaticResource MaterialDesignRaisedButton}"
Command="{Binding DataContext.TemplateCommand, RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding Id}"/>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate x:Key="DataTemplate1">
<TreeViewItem ItemsSource="{Binding Children}" Header="{Binding Name}"/>
</DataTemplate>
</ListBox.Resources>
</ListBox>
DataTemplateSelector
public sealed class MenuModelSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is MenuModel menuModel)
{
var resourceKey = menuModel.HasChildren ? "DataTemplate2" : "DataTemplate1";
if (container is FrameworkElement element && element.FindResource(resourceKey) is DataTemplate dataTemplate)
{
return dataTemplate;
}
}
return base.SelectTemplate(item, container);
}
}

How to trigger checked/unchecked events or Command of Checkbox in UWP using MVVM

I am new to UWP technology as well as new in MVVM too. I have multiple checkboxes on my page. See below:
A list of the checkboxes is inside the ListView and the ListView is inside the usercontrol. Data binding is done.
<ListView
x:Name="listMenu"
MinHeight="60"
FontSize="14"
Foreground="White"
IsItemClickEnabled="True"
ItemsSource="{x:Bind Items, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<ListViewItem
Margin="0"
VerticalAlignment="Center"
Style="{StaticResource SettingsChangeLanguageListStyle}">
<StackPanel Orientation="Vertical">
<CheckBox
x:Name="termsOfServiceCheckBox"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Command="{Binding AreaOfExpertiseCommand}"
Content="{Binding TopicName}"
FontFamily="{StaticResource osRegular}"
FontSize="14"
Foreground="#636E7F"
IsChecked="{Binding IsSelected, Mode=TwoWay}"
Style="{StaticResource CheckBoxCustomStyle}" />
</StackPanel>
</ListViewItem>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Now I want to trigger Checked/Uncheck event or Command to get my current checked or unchecked CheckBox using the MVVM pattern.
I have created a Dependency Property for Command which is not executing on checking/unchecking the checkbox.
public ICommand AreaOfExpertiseCommand
{
get { return (ICommand)GetValue(AreaOfExpertiseCommandProperty); }
set { SetValue(AreaOfExpertiseCommandProperty, value); }
}
public static readonly DependencyProperty AreaOfExpertiseCommandProperty = DependencyProperty.Register(nameof(AreaOfExpertiseCommand), typeof(ICommand), typeof(AreaOfExpertiseUserControl), new PropertyMetadata(null));
ViewModel.cs :
public ICommand AreaOfExpertiseCommand => new RelayCommand(UpdateAreaOfExpertiseAsync);
private void UpdateAreaOfExpertiseAsync()
{
}
Any help will be appreciated..

ListVIew of ItemsControl: Get the ListViewItem index after selecting one its item from ItemsControl

I have a ListView. Inside of this ListView there is ItemsControl and inside it there is second ItemsControl. Inside of the second ItemsControl there are TextBoxes.
ListView -> ItemsControl -> ItemsControl -> TextBox
Is there any chance that I would be able to get index of ListViewItem, which specific TextBox belongs to after clicking on this TextBox?
For example
I select a ListViewItem on index 0 but then I click on TextBox which belong to ListViewItem on index 2. In that case I would like to change value of SelectedGroupIndex from 0 to 2.
"Hello" strings are just for testing.
Thank you very much.
ViewModel
public class MainWindowViewModel
{
public ObservableCollection<ObservableCollection<ObservableCollection<ListViewString>>> AllTexts { get; set; }
public int SelectedGroupIndex { get; set; }
public ICommand AddGroup { get; private set; }
public ICommand AddColumn { get; private set; }
public ICommand TextBoxSelected { get; private set; }
public MainWindowViewModel()
{
this.AllTexts = new ObservableCollection<ObservableCollection<ObservableCollection<ListViewString>>>();
this.SelectedGroupIndex = -1;
this.AddGroup = new Command(this.AddGroupCommandHandler);
this.AddColumn = new Command(this.AddColumnCommandHandler);
this.TextBoxSelected = new Command(this.TextBoxSelectedCommandHandler);
}
private void AddGroupCommandHandler()
{
var tempColumn = new ObservableCollection<ListViewString>() {
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello") };
var tempGroup = new ObservableCollection<ObservableCollection<ListViewString>>();
tempGroup.Add(tempColumn);
this.AllTexts.Add(new ObservableCollection<ObservableCollection<ListViewString>>(tempGroup));
}
private void AddColumnCommandHandler()
{
if (this.SelectedGroupIndex >= 0 && this.SelectedGroupIndex < this.AllTexts.Count)
{
var tempColumn = new ObservableCollection<ListViewString>() {
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello"),
this.GetListViewString("Hello") };
this.AllTexts[this.SelectedGroupIndex].Add(tempColumn);
}
}
private void TextBoxSelectedCommandHandler()
{
// TODO: Change SelectedItem of ListView
// this.SelectedGroupIndex = ...;
}
private ListViewString GetListViewString(string text)
{
return new ListViewString { Value = text };
}
private string GetTextFromListViewString(ListViewString listViewString)
{
return listViewString.Value;
}
}
/// <summary>
/// Class used to show user Text in ListView.
/// Using this class fixes the issue that ObservableCollection didn't update
/// after user changed values of TextBoxes in GUI.
/// </summary>
public class ListViewString : DependencyObject
{
public string Value
{
get
{
return (string)GetValue(ValueProperty);
}
set
{
SetValue(ValueProperty, value);
}
}
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(string), typeof(ListViewString), new PropertyMetadata(string.Empty));
}
View:
<Window.Resources>
<ResourceDictionary>
<local:MainWindowViewModel x:Key="vm" />
</ResourceDictionary>
</Window.Resources>
<Grid Margin="10,10,10,10" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="300" />
<RowDefinition />
</Grid.RowDefinitions>
<ListView Grid.Row="0"
ItemsSource="{Binding AllTexts, Source={StaticResource vm}, Mode=TwoWay}"
Background="Blue"
SelectedIndex="{Binding SelectedGroupIndex, Source={StaticResource vm}}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value}"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"
Width="100" Height="40">
<TextBox.InputBindings>
<MouseBinding Gesture="LeftClick"
Command="{Binding TextBoxSelected, Source={StaticResource vm}}" />
</TextBox.InputBindings>
</TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Grid.Row="1" Orientation="Horizontal" Margin="0,20,0,0">
<Button Content="Add Group" Width="120" Height="30"
Command="{Binding AddGroup, Source={StaticResource vm}}" />
<Button Content="Add Column" Margin="20,0,0,0" Width="120" Height="30"
Command="{Binding AddColumn, Source={StaticResource vm}}" />
<TextBlock Width="120" Height="30" FontSize="20" Margin="20,0,0,0"
Text="{Binding SelectedGroupIndex, Source={StaticResource vm}}" />
</StackPanel>
</Grid>
Actually what you need is to pass the DataContext of your TextBox to the command as parameter, so use CommandParameter for it and implement your command with parameter:
<TextBox.InputBindings>
<MouseBinding Gesture="LeftClick"
Command="{Binding TextBoxSelected, Source={StaticResource vm}}"
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource Mode=Self}}"/>
</TextBox.InputBindings>
So you will have an item from your items source collection as command parameter and can find the index of it.

Bindig List to ListView in XAML not working?

I have read all the similar posts but did not found a solution for my problem.
I have the following List:
private List<Character> _Characters;
public List<Character> Characters
{
get { return _Characters; }
set { Set(ref _Characters, value);}
}
As I debugged the code, all the items (around 500) are added to the list properly.
My XAML looks like this:
<StackPanel Orientation="Horizontal">
<TextBlock Text="Characters: " FontWeight="Bold" VerticalAlignment="Top" />
<ScrollViewer VerticalScrollBarVisibility="Visible">
<ItemsControl ItemsSource="{Binding Characters}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=name}" IsTextSelectionEnabled="True" Margin="12" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</StackPanel>
However, when I run the program, the names are not displayed at all.
EDIT: Datacontext is set in the XAML:
<Page.DataContext>
<vm:MyViewModel />
</Page.DataContext>
Try to use ObservableCollection instead of List
private ObservableCollection<Character> _Characters;
public ObservableCollection<Character> Characters
{
get { return _Characters; }
set { Set(ref _Characters, value);}
}
also give a look at https://github.com/Fody/PropertyChanged

Categories

Resources