Я использую WinUI3 ListView, подобный этому, для загрузки списка файлов:
<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>
Теперь, как мне настроить DataTemplate, чтобы некоторые (или все) элементы были выбраны по умолчанию?
Есть ли в шаблоне данных особое значение, которое мне следует использовать?





Вы можете использовать ListViewItem в своем ItemTemplate.
Допустим, ваш FileItem выглядит так:
public partial class FileItem : ObservableObject
{
[ObservableProperty]
private string _name = string.Empty;
[ObservableProperty]
private bool _isSelected;
}
и ваша 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"},
};
}
и в 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" />
Но, к сожалению, это не сработает, потому что ListViewItem сбрасывает свое свойство IsSelected на false при загрузке. Я не уверен, это ошибка или так задумано.
В качестве обходного пути вы можете выполнить привязку после загрузки каждого ListViewItem:
<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>
затем в коде:
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,
});
}
Кстати, я использую пакет CommunityToolkit.Mvvm NuGet для ViewModels, но надеюсь, что это даст вам представление о том, как решить вашу проблему.
Вот еще один ответ, конечно, все еще основанный на ListViewItem, но немного другой, я считаю, что его немного проще реализовать на разных языках, поддерживаемых WinUI3.
Ксамл:
<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>
И если у вас есть такой класс данных в 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)));
}
}
}
Или это на C++ (с WinRT), часть кода опущена для краткости, полный проект здесь 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));
}
};
}
Тогда трюк заключается в том, чтобы выбрать начальные элементы отложенным способом, как это в 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; } = [];
}
И, например, вот так в 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'
}
});
Вы всегда можете добавить объекты в списки SelectedItems Collection.
Назовите свой список x:Name=ListName
Затем примените логику после загрузки представления, чтобы добавить определенные объекты из этого списка в SelectedItems.
<ListView
x:Name = "ListName"
Grid.Row = "1"
IsItemClickEnabled = "True"
ItemTemplate = "{StaticResource Template2}"
ItemsSource = "{x:Bind ViewModel.Files, Mode=OneWay}"
SelectionMode = "Multiple" />
Тогда делай
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);
}
}
Проголосовал за, потому что это привело меня к <ListViewItem Tapped = "ListViewItem_Tapped">, который спас мой день,