I have a custom user control that has a checkbox and a button with backing dependency properties.
I also have a window with an ItemsControl and I bind that control's "ItemsSource" to an ObservableCollection containing said user controls.
My goal is to be able to access the dependency properties in the window's view model, so I can check whether the checkbox of each member of the collection is checked or not and when the user clicks the button - remove the user control containing the button from the collection.
User control XAML:
<CheckBox IsChecked="{Binding cbChecked,
Mode=OneWayToSource,
UpdateSourceTrigger=PropertyChanged,
RelativeSource={RelativeSource FindAncestor,
AncestorType=UserControl}}"/>
<Button Content="X"
Command="{Binding RemoveCommand,
RelativeSource={RelativeSource FindAncestor,
AncestorType=UserControl}}">
(note I am not sure if I need the "UpdateSourceTrigger")
User control codebehind:
public ICommand RemoveCommand
{
get { return (ICommand)GetValue(RemoveCommandProperty); }
set { SetValue(RemoveCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for RemoveCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty RemoveCommandProperty =
DependencyProperty.Register("RemoveCommand", typeof(ICommand), typeof(CustomUserControl), new PropertyMetadata(OnAnyPropertyChanged));
public bool cbChecked
{
get { return (bool)GetValue(cbCheckedProperty); }
set { SetValue(cbCheckedProperty, value); }
}
// Using a DependencyProperty as the backing store for cbChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty cbCheckedProperty =
DependencyProperty.Register("cbChecked", typeof(bool), typeof(CustomUserControl), new PropertyMetadata(OnAnyPropertyChanged));
static void OnAnyPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
=> (obj as CustomUserControl).OnAnyPropertyChanged(args);
void OnAnyPropertyChanged(DependencyPropertyChangedEventArgs args)
=> AnyPropertyChanged?.Invoke(this, args);
Window XAML:
<ScrollViewer VerticalScrollBarVisibility="Visible" Grid.Row="0">
<ItemsControl ItemsSource="{Binding ControlsCollection}" HorizontalAlignment="Stretch" VerticalAlignment="Top">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin" Value="5,20,0,0"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</ScrollViewer>
First, you should not hold an element of view in view model. The item of ObservableCollection should be item's view model rather than UserControl.
Second, you don't need to use a UserControl in such case. A DataTemplate which has the same elements will suffice.
For example, a minimal item's view model would be as follows.
using CommunityToolkit.Mvvm.ComponentModel;
public partial class ItemViewModel : ObservableObject
{
[ObservableProperty]
public bool _checked;
}
Then, window's view model.
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
public class MainWindowViewModel : ObservableObject
{
public ObservableCollection<ItemViewModel> ItemsCollection { get; } = new();
public MainWindowViewModel()
{
ItemsCollection.Add(new ItemViewModel());
ItemsCollection.Add(new ItemViewModel());
ItemsCollection.Add(new ItemViewModel());
}
public ICommand RemoveCommand => _removeCommand ??= new RelayCommand<ItemViewModel>(item => Remove(item));
private RelayCommand<ItemViewModel>? _removeCommand;
private void Remove(ItemViewModel? item)
{
if (item is not null)
{
Debug.WriteLine($"Checked:{item.Checked}");
ItemsCollection.Remove(item);
}
}
}
The window's view. The elements of DataTemplate of ItemsControl.ItemTemplate are the same as your UserControl but bindings are modified.
<Window x:Class="WpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp"
Title="MainWindow"
Width="600" Height="300">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<ScrollViewer VerticalScrollBarVisibility="Visible">
<ItemsControl ItemsSource="{Binding ItemsCollection}"
HorizontalAlignment="Stretch" VerticalAlignment="Top">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="FrameworkElement.Margin" Value="5,20,0,0"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox IsChecked="{Binding Checked, Mode=OneWayToSource}"/>
<Button Content="X"
Command="{Binding DataContext.RemoveCommand, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}}"
CommandParameter="{Binding DataContext, RelativeSource={RelativeSource Self}}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Window>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.