Reputation: 11178
I'm using a WinUI3 ListView like this to load a list of files:
<ListView ItemTemplate="{StaticResource Template2}"
ItemsSource="{x:Bind Files,Mode=OneWay}"
IsItemClickEnabled="True" x:Name="List2" SelectionMode="Multiple" />
<DataTemplate x:Key="Template2" x:DataType="local:FileItem">
<Grid>
.... TextBlocks that bind to properties in FileItem
</Grid>
</DataTemplate>
Now, how do I configure my DataTemplate in order for some (or all) of the items to be selected by default?
Is there a special value in the data template I should use?
Upvotes: 1
Views: 656
Reputation: 24
You can always add the objects to the lists SelectedItems Collection.
Name your list x:Name=ListName
Then apply logic after loading your view to add certain objects of that list to the SelectedItems.
<ListView
x:Name="ListName"
Grid.Row="1"
IsItemClickEnabled="True"
ItemTemplate="{StaticResource Template2}"
ItemsSource="{x:Bind ViewModel.Files, Mode=OneWay}"
SelectionMode="Multiple" />
Then do
public ClassName()
{
this.InitializeComponent();
// apply your own logic to add if condition is met
// this would select all values
foreach (var x in ListName.Items)
{
ListName.SelectedItems.Add(x);
}
}
Upvotes: -1
Reputation: 139187
Here is another answer of course still based on ListViewItem but slightly different, I believe a bit simpler to implement across different WinUI3 supported languages.
The Xaml:
<ListView ItemsSource="{x:Bind MyItems}" SelectionMode="Multiple">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:MyItem">
<ListViewItem IsSelected="{x:Bind IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBlock Text="{x:Bind Name}" />
</ListViewItem>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And if you have a data class like this in C#
public class MyItem : INotifyPropertyChanged
{
private bool _isSelected;
public event PropertyChangedEventHandler PropertyChanged;
public string Name { get; set; }
public bool IsSelected
{
get => _isSelected;
set
{
if (_isSelected == value)
return;
_isSelected = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsSelected)));
}
}
}
Or this in C++ (with WinRT), some code ommited for brevity the full project is here https://github.com/smourier/WinUI3Cpp
[bindable]
[default_interface]
runtimeclass FileSystemItem : Microsoft.UI.Xaml.Data.INotifyPropertyChanged
{
String Name { get; };
Boolean IsSelected;
}
struct FileSystemItem : FileSystemItemT<FileSystemItem>
{
hstring Name() { return _name; }
bool IsSelected() const { return _isSelected; }
void IsSelected(bool const& selected){
if (selected == _isSelected)
return;
_isSelected = selected;
RaisePropertyChanged(L"IsSelected");
}
event_token PropertyChanged(PropertyChangedEventHandler const& handler) { return _propertyChanged.add(handler); }
void PropertyChanged(event_token token) { _propertyChanged.remove(token); }
private:
event<PropertyChangedEventHandler> _propertyChanged;
hstring _name;
bool _isSelected;
void RaisePropertyChanged(hstring propertyName)
{
_propertyChanged(*this, PropertyChangedEventArgs(propertyName));
}
};
}
Then the trick is to select initial items in a deferred way, so like this in C#
public sealed partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyItems.Add(new MyItem { Name = "Bob" });
MyItems.Add(new MyItem { Name = "Alice" });
MyItems.Add(new MyItem { Name = "Carl" });
MyItems.Add(new MyItem { Name = "Donald" });
// select items at initialization
DispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
MyItems[1].IsSelected = true; // select items 1 & 3
MyItems[3].IsSelected = true;
});
}
public ObservableCollection<MyItem> MyItems { get; } = [];
}
And for example like this in C++
auto source{ single_threaded_observable_vector<TreeViewSample::FileSystemItem>() };
for (auto const& child : someSource())
{
source.Append(child);
}
myListView().ItemsSource(source);
// select items at initialization
DispatcherQueue().TryEnqueue(
Microsoft::UI::Dispatching::DispatcherQueuePriority::Low,
[source]()
{
for (auto const& child : source)
{
child.IsSelected(child.Name().c_str()[0] == 'T'); // select all child with name that starts with 'T'
}
});
Upvotes: 2
Reputation: 13666
You can use the ListViewItem
in your ItemTemplate
.
Let's say your FileItem
looks like this:
public partial class FileItem : ObservableObject
{
[ObservableProperty]
private string _name = string.Empty;
[ObservableProperty]
private bool _isSelected;
}
and your ViewModel:
public partial class MainPageViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<FileItem> _files = new()
{
new FileItem { Name = "File1" },
new FileItem { Name = "File2", IsSelected = true, },
new FileItem { Name = "File3"},
};
}
and in XAML:
<Page.Resources>
<DataTemplate
x:Key="Template2"
x:DataType="local:FileItem">
<ListViewItem IsSelected="{x:Bind IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<TextBlock Text="{x:Bind Name, Mode=OneWay}" />
</ListViewItem>
</DataTemplate>
</Page.Resources>
<ListView
Grid.Row="1"
IsItemClickEnabled="True"
ItemTemplate="{StaticResource Template2}"
ItemsSource="{x:Bind ViewModel.Files, Mode=OneWay}"
SelectionMode="Multiple" />
But unfortunately, this won't work because the ListViewItem
resets its IsSelected
property to false
when it's loaded. I'm not sure if it's a bug or it's by design.
As a workaround, you can do the binding after each ListViewItem
is loaded:
<Page.Resources>
<DataTemplate
x:Key="Template2"
x:DataType="local:FileItem">
<ListViewItem Loaded="ListViewItem_Loaded">
<TextBlock Text="{x:Bind Name, Mode=OneWay}" />
</ListViewItem>
</DataTemplate>
</Page.Resources>
then in code-behind:
private void ListViewItem_Loaded(object sender, RoutedEventArgs e)
{
if (sender is not ListViewItem listViewItem)
{
return;
}
listViewItem.SetBinding(
ListViewItem.IsSelectedProperty,
new Binding
{
Path = new PropertyPath("IsSelected"),
Mode = BindingMode.TwoWay,
Source = listViewItem.DataContext,
UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
});
}
BTW, I'm using the CommunityToolkit.Mvvm NuGet package for the ViewModels but I hope this gives you an idea how to fix your issue.
Upvotes: 4