通过 CommandParameters 返回 ViewModels 的实例

Returning ViewModels' instance through CommandParameters

我有一个父 ViewModel,绑定到父 Window。在内部,有一个 ItemsControl,它绑定到子 ViewModel 的集合。 ItemsControl 创建 UserControl(子视图),将其 DataContext 作为 ChildViewModel(自动,根据 ItemsControl 的工作方式)。

可能值得注意的是,我使用 Castle Windsor 作为 IoC。 我也尝试通过(当前工作的)抽象工厂解决子 ViewModels,但它没有帮助。我什至尝试将子 ViewModel 注册为单例,但仍然无法在集合中找到。

所以我把问题简化为简单的 class 实例化。

现在,我想要的是:

每个Reminder(UserControl)都有一个Button,代表关闭。 这个按钮像这样绑定到父视图模型:

CloseCommand="{Binding ElementName=level1Listener, Path=DataContext.ReminderBoxCloseCommand}"

其中 level1ListenerItemsControl 的名称。

到目前为止一切正常,创建了 UserControls 并且所有绑定都正常工作。但是:

When I click the Close button, the ViewModel isn't removed from the collection - even though the bound command executes. It seems, that the instances of ReminderBoxViewModel returned by ANY of the UserControls, do not exist in the collection - but the UserControls even showing up depend directly on their existence.

When i simply remove an element at a certain index - all works fine.

How could the UC return something different as its' Data Context?

这怎么可能,还是我完全遗漏了什么?

这是我的手机Window查看:

<Window x:Class="NoContact.Views.PhoneWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:NoContact.Views"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:avalon="http://icsharpcode.net/sharpdevelop/avalonedit"
    xmlns:usercontrols="clr-namespace:NoContact.UserControls"
    xmlns:viewModels="clr-namespace:NoContact.ViewModels"
    mc:Ignorable="d"
    Title="PhoneWindow" Height="600" Width="980"
    Background="#22282a">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="600" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
    <Border Grid.Column="0" Margin="10, 10" Style="{StaticResource phoneWindowBorderStyle}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="80"/>
                <RowDefinition Height="180" />
                <RowDefinition Height="*" />
                <RowDefinition Height="100"/>
            </Grid.RowDefinitions>
            <Border Grid.Row="0" Margin="-1,-1" Style="{StaticResource phoneWindowBorderStyle}">
                <StackPanel Orientation="Horizontal">
                    <Image Margin="15">
                        <Image.Source>
                            <DrawingImage>
                                <DrawingImage.Drawing>
                                    <DrawingGroup>
                                        <GeometryDrawing Brush="#ffcd22"
                                             Geometry="{StaticResource phoneIconGeometry}" />
                                    </DrawingGroup>
                                </DrawingImage.Drawing>
                            </DrawingImage>
                        </Image.Source>
                    </Image>
                    <TextBlock VerticalAlignment="Center" FontSize="30" Style="{StaticResource phoneWindowTextBlockStyle}">Telefon</TextBlock>
                </StackPanel> 
            </Border>

            <Grid Grid.Row="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Grid Grid.Column="0">
                    <DockPanel LastChildFill="False">
                        <DockPanel LastChildFill="True" Margin="10,20,0,0" DockPanel.Dock="Top">
                            <TextBlock Style="{StaticResource phoneWindowTextBlockStyle}">Imię:</TextBlock>
                            <TextBox Height="30" Margin="46,0,10,0" Style="{StaticResource phoneWindowTextBoxStyle}"></TextBox>
                        </DockPanel>
                        <DockPanel LastChildFill="True" Margin="10,20,0,0" DockPanel.Dock="Top">
                            <TextBlock Style="{StaticResource phoneWindowTextBlockStyle}">Nazwisko:</TextBlock>
                            <TextBox Height="30" Margin="10,0,10,0" Style="{StaticResource phoneWindowTextBoxStyle}"></TextBox>
                        </DockPanel>
                        <DockPanel LastChildFill="True" Margin="10,20,0,0" DockPanel.Dock="top">
                            <TextBlock Style="{StaticResource phoneWindowTextBlockStyle}">Data:</TextBlock>
                            <usercontrols:DateTimePicker Height="30" Margin="40,0,10,0" BorderBrush="#ffcd22" BorderThickness="1" Background="#15151a" Foreground="#ffcd22"/>
                        </DockPanel>
                    </DockPanel>
                </Grid>
                <Grid Grid.Column="1">
                    <DockPanel LastChildFill="False">
                        <DockPanel LastChildFill="True" Margin="10,20,0,0" DockPanel.Dock="Top">
                            <TextBlock Style="{StaticResource phoneWindowTextBlockStyle}">Email:</TextBlock>
                            <TextBox Height="30" Margin="26,0,10,0" Style="{StaticResource phoneWindowTextBoxStyle}"></TextBox>
                        </DockPanel>
                        <DockPanel LastChildFill="True" Margin="10,20,0,0" DockPanel.Dock="Top">
                            <TextBlock Style="{StaticResource phoneWindowTextBlockStyle}">Telefon:</TextBlock>
                            <TextBox Height="30" Margin="10,0,10,0" Style="{StaticResource phoneWindowTextBoxStyle}"></TextBox>
                        </DockPanel>
                        <DockPanel LastChildFill="True" Margin="10,20,0,0" DockPanel.Dock="top">
                            <TextBlock Style="{StaticResource phoneWindowTextBlockStyle}">Nr. Zamówienia:</TextBlock>
                            <TextBox Height="30" Margin="10,0,10,0" Style="{StaticResource phoneWindowTextBoxStyle}"></TextBox>
                        </DockPanel>
                    </DockPanel>
                </Grid>
            </Grid>
            <Grid Grid.Row="2">
                <avalon:TextEditor Margin="10,10" Style="{StaticResource phoneWindowAvalonEditStyle}" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" WordWrap="True">

                </avalon:TextEditor>
            </Grid>
            <Border Grid.Row="3" Margin="-1,0,-1,-1" Style="{StaticResource phoneWindowBorderStyle}">
                <Grid>
                    <Button Width="170" HorizontalAlignment="Right" Margin="10" Style="{StaticResource flatButtonStyle}">Gotowe</Button>
                    <Button Width="170" HorizontalAlignment="Left" Margin="10" Style="{StaticResource flatButtonStyle}">Anuluj</Button>
                </Grid>
            </Border>
        </Grid>
    </Border>

    <Border Grid.Column="1" Margin="10,10" Style="{StaticResource phoneWindowBorderStyle}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="80" />
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <Border Grid.Row="0" Margin="-1,-1,-1,-1" Style="{StaticResource phoneWindowBorderStyle}">
                <usercontrols:ImageButton x:Name="addButton" Style="{StaticResource addImageButtonUCStyle}" Text="Dodaj Termin" ClickCommand="{Binding Path=AddReminderCommand}">

                </usercontrols:ImageButton>
            </Border>
            <Border Grid.Row="1" Margin="-1,0,-1,-1" Style="{StaticResource phoneWindowBorderStyle}">
                <ScrollViewer VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden" Background="#15151a">
                    <ItemsControl x:Name="level1Listener" ItemsSource="{Binding Path=Reminders, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <StackPanel />
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                        <ItemsControl.ItemTemplate>
                            <DataTemplate DataType="{x:Type viewModels:ReminderBoxViewModel}">
                                <StackPanel>
                                    <usercontrols:ReminderBox VerticalAlignment="Top" Background="#22282a" Foreground="#ffcd22" Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, 
                                                              AncestorType={x:Type ScrollViewer}}, Path=ActualWidth}" Height="140" CloseCommand="{Binding ElementName=level1Listener, Path=DataContext.ReminderBoxCloseCommand}"
                                                              CommandParameter="{Binding}"/>
                                    <Rectangle Height="1" Margin="10,0" Fill="#ffcd22" />
                                </StackPanel>
                            </DataTemplate>
                        </ItemsControl.ItemTemplate>
                    </ItemsControl>
                </ScrollViewer>
            </Border>
        </Grid>
    </Border>
</Grid>

电话Window(查看)后面的代码:

namespace NoContact.Views
{
/// <summary>
/// Interaction logic for phoneWindow.xaml
/// </summary>
public partial class PhoneWindow : Window, IView
{
    public IViewModel ViewModel { get; private set; }
    private IWindowManager _windowManager;

    public PhoneWindow(IViewFactory context, IWindowManager windowManager)
    {
        InitializeComponent();

        _windowManager = windowManager;

        var obj = context.Create<IPhoneWindowViewModel>();
        ViewModel = obj;
        this.DataContext = obj;
        context.Release(obj);
    }

    protected override void OnClosed(EventArgs e)
    {
        _windowManager.RemoveFromWindowList(this);
        base.OnClosed(e);
    }
}
}

这是 MainViewModel:

public class PhoneWindowViewModel : BindableBase, IPhoneWindowViewModel
{
    private IWindowManager _windowManager;

    private ObservableCollection<IReminderBoxViewModel> _reminders;

    public PhoneWindowViewModel(IWindowManager windowManager)
    {
        _windowManager = windowManager;

        _reminders = new ObservableCollection<IReminderBoxViewModel>();
    }

    public ICommand ReminderBoxCloseCommand { get { return new RelayCommand<ReminderBoxViewModel>(CloseReminderBox); } }
    public ICommand AddReminderCommand { get { return new RelayCommand(AddReminder, () => true); } }

    public ObservableCollection<IReminderBoxViewModel> Reminders
    {
        get { return _reminders; }
        set
        {
            _reminders = value;
            OnPropertyChanged();
        }
    }

    /// <summary>
    /// Delegates closing the window, associated with 
    /// this ViewModel instance.
    /// </summary>
    private void CloseWindow()
    {
        _windowManager.CloseWindow(this);
    }

    /// <summary>
    /// "Closes" the Reminder Box by deleting it from the Collection 
    /// </summary>
    private void CloseReminderBox(ReminderBoxViewModel viewModel)
    {
        Reminders.Remove(viewModel);
        var index = Reminders.IndexOf(viewModel);
        System.Windows.MessageBox.Show(index.ToString());

    }

    /// <summary>
    /// Adds the reminder to the Collection
    /// </summary>
    private void AddReminder()
    {
        Reminders.Add(new ReminderViewModel());
    }

最后,我的用户控制代码:

namespace NoContact.UserControls
{
/// <summary>
/// Interaction logic for ReminderBox.xaml
/// </summary>
public partial class ReminderBox : UserControl
{
    #region Dependency Properties

    public Brush CloseButtonBrush
    {
        get { return (Brush)GetValue(CloseButtonBrushProperty); }
        set { SetValue(CloseButtonBrushProperty, value); }
    }

    public new Brush Background
    {
        get { return (Brush)GetValue(BackgroundProperty); }
        set { SetValue(BackgroundProperty, value); }
    }

    public new Brush Foreground
    {
        get { return (Brush)GetValue(ForegroundProperty); }
        set { SetValue(ForegroundProperty, value); }
    }

    public ICommand CloseCommand
    {
        get { return (ICommand)GetValue(CloseCommandProperty); }
        set { SetValue(CloseCommandProperty, value); }
    }

    public object CommandParameter
    {
        get { return (object)GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }

    public static readonly DependencyProperty CloseButtonBrushProperty =
        DependencyProperty.Register("CloseButtonBrush", typeof(Brush), typeof(ReminderBox), new UIPropertyMetadata(null));

    public static readonly new DependencyProperty BackgroundProperty =
       DependencyProperty.Register("Background", typeof(Brush), typeof(ReminderBox), new UIPropertyMetadata(null));

    public static readonly new DependencyProperty ForegroundProperty =
      DependencyProperty.Register("Foreground", typeof(Brush), typeof(ReminderBox), new UIPropertyMetadata(null));

    public static readonly DependencyProperty CloseCommandProperty =
        DependencyProperty.Register("CloseCommand", typeof(ICommand), typeof(ReminderBox), new UIPropertyMetadata(null));

    public static readonly DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter", typeof(object), typeof(ReminderBox), new UIPropertyMetadata(null));

    #endregion



    public ReminderBox()
    {
        InitializeComponent();


    }
}
}

提醒XAML:

<UserControl x:Class="NoContact.UserControls.ReminderBox"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:local="clr-namespace:NoContact.UserControls"
         xmlns:usercontrols="clr-namespace:NoContact.UserControls"
         xmlns:helpers="clr-namespace:NoContact.Helpers"
         mc:Ignorable="d" 
         x:Name="ReminderBoxUC"
         d:DesignHeight="130" d:DesignWidth="400">
<Grid>
    <Border Background="{Binding ElementName=ReminderBoxUC, Path=Background}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="0.5*" />
                <RowDefinition Height="0.5*" />
                <RowDefinition Height="1*" />
            </Grid.RowDefinitions>

            <Button Grid.Row="0" Width="20" Height="20" Style="{StaticResource flatButtonStyle}" Background="Transparent" Foreground="Transparent"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Top"
                    Margin="0,10,10,0"
                    Command="{Binding ElementName=ReminderBoxUC, Path=CloseCommand}">
                <Border Name="closeButtonBackgroundBorder" CornerRadius="5" Style="{StaticResource CloseButtonReminderBox_BorderUCStyle}">
                    <Image Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=ActualWidth}"
                       Height="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Button}}, Path=ActualHeight}"
                       HorizontalAlignment="Center" VerticalAlignment="Center">
                        <Image.Source>
                            <DrawingImage>
                                <DrawingImage.Drawing>
                                    <DrawingGroup>
                                        <GeometryDrawing Brush="{Binding ElementName=ReminderBoxUC, Path=CloseButtonBrush}" Geometry="{StaticResource closeIconGeometry}" />
                                    </DrawingGroup>
                                </DrawingImage.Drawing>
                            </DrawingImage>
                        </Image.Source>
                    </Image>
                </Border>
            </Button>

            <StackPanel Grid.Row="1" Orientation="Horizontal" Margin="10,0">
                <TextBlock Foreground="{Binding ElementName=ReminderBoxUC, Path=Foreground}"
                           VerticalAlignment="Center" HorizontalAlignment="Left">Przypomnij:
                </TextBlock>
                <RadioButton Name="RemindOnceRadioButton" IsChecked="True"
                                 Foreground="{Binding ElementName=ReminderBoxUC, Path=Foreground}"
                                 Style="{StaticResource RadioButtonStyle}"
                                 VerticalAlignment="Center" Margin="25,0,0,0">1 Raz</RadioButton>
                <RadioButton Name="RemindMultipleRadioButton"   
                             Foreground="{Binding ElementName=ReminderBoxUC, Path=Foreground}"
                                 Style="{StaticResource RadioButtonStyle}"
                                 VerticalAlignment="Center" Margin="25,0,0,0">Wielokrotnie</RadioButton>
            </StackPanel>

            <DockPanel Grid.Row="2"
                            Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Grid}}, Path=ActualWidth}">
                <usercontrols:DateTimePicker Height="25" Margin="10,0" BorderBrush="#ffcd22" BorderThickness="1" Foreground="#ffcd22" Background="#15151a" Width="150" />
                <ComboBox Width="150" Height="25" HorizontalAlignment="Right" Margin="10,0" Style="{StaticResource comboBoxStyle}" ItemsSource="{Binding Source={helpers:EnumBindingSource {x:Type local:TimePeriods}}}"
                          SelectedIndex="0" />
            </DockPanel>
        </Grid>
    </Border>        
</Grid>
<UserControl.Resources>
    <Style TargetType="local:ReminderBox">
        <Setter Property="CloseButtonBrush" Value="#ffcd22" />
        <Style.Triggers>
            <DataTrigger Binding="{Binding ElementName=closeButtonBackgroundBorder, Path=IsMouseOver}" Value="True">
                <Setter Property="CloseButtonBrush" Value="#22282a" />
            </DataTrigger>
        </Style.Triggers>
    </Style>
</UserControl.Resources>

至于 ReminderViewModel,它目前是空的,除了继承自(目前也是空的)接口 IReminderViewModel。

您的问题似乎是尽管您将 CommandParameter DP 设置为绑定到当前 ReminderBoxViewModel,但您并未在 ReminderBox 中的任何地方使用它。

在提醒 XAML 中,您正确地将按钮的 Command 绑定到 ReminderBox 的 CloseCommand,您没有将按钮的 CommandParameter 设置为CommandParameter 你的 ReminderBox。

ReminderBox的相关代码XAML:

<Button ...
        Command="{Binding ElementName=ReminderBoxUC, Path=CloseCommand}"
        CommandParameter"{Binding ElementName=ReminderBoxUC, Path=CommandParameter}">
    ...
</Button>