При попытке связать IsSelectable свойство INPC с помощью установщика в представлении списка на основе этого ответа.
Указанный ответ предназначен для ComboBoxItem, который отлично работает, я пытаюсь провести рефакторинг для ListViewItem, который не работает.
Установка IsSelectable true или false для любого элемента в ViewModel.ItemsOC не работает, поскольку элемент представления списка по-прежнему доступен для выбора. Что мне не хватает?
<ListView
ItemsSource = "{x:Bind ViewModel.ItemsOC, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
SelectedItem = "{x:Bind ViewModel.SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectionChanged = "LV_Item_SelectionChanged">
<ListView.Resources>
<Style BasedOn = "{StaticResource DefaultListViewItemStyle}" TargetType = "ListViewItem">
<Setter Property = "cus_ctrls:LVI_BindingHelper.IsEnable" Value = "IsSelectable" />
</Style>
</ListView.Resources>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text = "{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public class LVI_BindingHelper
{
public static string GetIsEnable(DependencyObject obj)
{
return (string)obj.GetValue(IsEnableProperty);
}
public static void SetIsEnable(DependencyObject obj, string value)
{
obj.SetValue(IsEnableProperty, value);
}
// Using a DependencyProperty as the backing store for IsEnable. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsEnableProperty =
DependencyProperty.RegisterAttached("IsEnable", typeof(string), typeof(LVI_BindingHelper), new PropertyMetadata(null, GridBindingPathPropertyChanged));
private static void GridBindingPathPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var propertyPath = e.NewValue as string;
if (propertyPath != null)
{
var bindingProperty =
e.Property == IsEnableProperty
? ListViewItem.IsEnabledProperty
: null;
BindingOperations.SetBinding(
obj,
bindingProperty,
new Binding { Path = new PropertyPath(propertyPath) });
}
}
}
bool _isSelectable;
public bool IsSelectable
{
get => _isSelectable;
set => Set(ref _isSelectable, value);
}
@AndrewKeepCoding нет, я хочу, чтобы определенные уровни нельзя было выбрать при создании списка
Вы можете попробовать установить для параметра ItemListView.isEnabled значение false. Я предлагаю вам обратиться к теме: stackoverflow.com/a/34634601/11872808





Обычная привязка должна подойти. Проверьте этот пример кода:
Item.cs
public partial class Item : ObservableObject
{
[ObservableProperty]
private string name = string.Empty;
[ObservableProperty]
private bool isSelectable;
}
MainPageViewModel.cs
public partial class MainPageViewModel : ObservableObject
{
[ObservableProperty]
private ObservableCollection<Item> items = new();
[ObservableProperty]
private Item? selectedItem;
public MainPageViewModel()
{
for (int i = 0; i < 100; i++)
{
Items.Add(new Item
{
Name = $"Item {i}",
IsSelectable = i % 5 is not 0,
});
}
}
[RelayCommand]
private void MakeAllItemsSelectable()
{
foreach (Item item in Items)
{
item.IsSelectable = true;
}
}
}
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public MainPageViewModel ViewModel { get; } = new();
}
MainPage.xaml
<Grid RowDefinitions = "Auto,*">
<Button
Grid.Row = "0"
Command = "{x:Bind ViewModel.MakeAllItemsSelectableCommand}"
Content = "Click" />
<ListView
Grid.Row = "1"
ItemsSource = "{x:Bind ViewModel.Items, Mode=OneWay}"
SelectedItem = "{x:Bind ViewModel.SelectedItem, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate x:DataType = "local:Item">
<ListViewItem IsEnabled = "{x:Bind IsSelectable, Mode=OneWay}">
<TextBlock Text = "{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
</ListViewItem>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
ПРИМЕЧАНИЕ
Я использую пакет NuGet CommunitToolkit.Mvvm для шаблона MVVM.
ОБНОВЛЯТЬ
Поскольку кажется, что вы не можете коснуться ItemTemplate, я думаю, вам все равно придется использовать AttachedProperty. Я подтвердил, что приведенный ниже код работает:
ListViewItemIBindingHelper.cs
public class ListViewItemIBindingHelper
{
public static string GetIsEnabled(DependencyObject obj)
=> (string)obj.GetValue(IsEnabledProperty);
public static void SetIsEnabled(DependencyObject obj, string value)
=> obj.SetValue(IsEnabledProperty, value);
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(string),
typeof(ListViewItemIBindingHelper),
new PropertyMetadata(string.Empty, OnIsEnabledPropertyChanged));
private static void OnIsEnabledPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is not ListViewItem listViewItem ||
e.NewValue is not string bindingPath)
{
return;
}
listViewItem.RegisterPropertyChangedCallback(
ListViewItem.ContentProperty,
(_, _) =>
{
if (listViewItem.Content is not Item item)
{
return;
}
listViewItem.SetBinding(
ListViewItem.IsEnabledProperty,
new Binding
{
Source = item,
Path = new PropertyPath(bindingPath),
});
});
}
}
<ListView ItemsSource = "{x:Bind ViewModel.Items, Mode=OneWay}">
<ListView.ItemContainerStyle>
<Style TargetType = "ListViewItem">
<Setter Property = "local:ListViewItemIBindingHelper.IsEnabled" Value = "IsSelectable" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
Большое спасибо за этот ответ. К сожалению, ItemTemplate отключен где-то в файле ресурсов, который мне не разрешено изменять. Приложение переносится из WPF, поэтому возникает проблема с установщиком. Я ищу способ добавить несколько помощников и небольшой объем xaml, так как существует множество ListView для рефакторинга.
Ты имеешь в виду, что не можешь сменить Item класс? или вы действительно имеете в виду ListView.ItemTemplate?
ListView.ItemTemplate = StaticResource {lv_aux_qwop_1877} изначально опущено, чтобы сделать пример более читабельным, поскольку изначально я надеялся на помощь с помощником по привязке, но должен еще раз поблагодарить вас за ваш ответ, я уверен, что он поможет другим, к сожалению, не мне на этот раз.
Обновил свой ответ, используя AttachedProperty.
Спасибо, это работает. Ценю всю вашу помощь
Для каждого элемента (ViewModel) в ItemsSource ListViewItem создается следующим образом: (по крайней мере, если ListViewItem не является частью шаблона элемента)
Корневые элементы, созданные из ItemTemplate, получат свой DataContext из свойства Content ListViewItem, что сделает их и все дочерние данные привязываемыми.
Но сам ListViewItem не имеет набора DataContext, только Content. ComboBoxItem получают как DataContext, так и Content, установленные в Item (ViewModel), поэтому он работает для ComboBoxItem.
Хотя он не может связываться только с путем, он все же может связываться с использованием относительного источника, измените последнюю часть вашего кода привязки внутри вашего BindingHelper на:
// bind ListViewItem.IsEnabled to ListViewItem(self) -> Content(=ViewModel) -> propertyPath(=IsSelectable)
BindingOperations.SetBinding(
obj, bindingProperty,
new Binding {
RelativeSource = new RelativeSource() { Mode=RelativeSourceMode.Self },
Path = new PropertyPath("Content." + propertyPath) });
Спасибо, это работает. Спасибо также за объяснение того, как работает внутреннее устройство и разницу между ComboBox и Listiew, очень полезно знать.
Вы хотите отключить
ListViewItemпосле его выбора?