c# - 通过 CommandParameters 返回 ViewModels 的实例

标签 c# wpf mvvm ioc-container

我有一个父 ViewModel,绑定(bind)到父窗口。在内部,有一个 ItemsControl,它绑定(bind)到子 ViewModel 的集合。
ItemsControl 创建将其 DataContext 作为 ChildViewModel 的用户控件( subview )(根据 ItemsControl 的工作方式自动)。

值得注意的是,我使用的是温莎城堡作为 IoC。
我还尝试通过(当前工作的)抽象工厂解决子 ViewModels,但它没有帮助。我什至尝试将子 ViewModel 注册为单例,但仍然 - 在集合中找不到。

所以我将问题简化为简单的类实例化。

现在,我想要:

每个 Reminder (UserControl) 都有一个 Button,代表关闭。
此按钮绑定(bind)到父 ViewModel,如下所示:

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

在哪里 level1ListenerItemsControl 的名称.

到目前为止一切正常,UserControls 被创建并且所有绑定(bind)工作。但:

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?



这怎么可能,或者我完全错过了什么?

这是我的 PhoneWindow View :
<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>

PhoneWindow (View) 后面的代码:
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,它目前是空的,除了继承自(目前也是空的)接口(interface) IReminderViewModel。

最佳答案

您的问题似乎是您正在设置 CommandParameter DP 绑定(bind)到当前 ReminderBoxViewModel ,您没有在 ReminderBox 的任何地方使用它。

在提醒 XAML 中,您正确绑定(bind)了 Command按钮的 CloseCommand你的提醒框,你没有设置按钮的CommandParameterCommandParameter您的提醒框。

ReminderBox的XAML相关代码:

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

关于c# - 通过 CommandParameters 返回 ViewModels 的实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43586767/

相关文章:

c# - WPF 列表框绑定(bind)更新

C# CommandManager 结合撤消/重做模式

WPF 网格 : Rectangle as transparent overlay

c# - 依赖属性绑定(bind)和更新到自定义控件——它更新,但显然不是两种方式?

c# - asp.net文件上传控件如何动态设置值?

c# - Nhibernate n+1 具有三元关系。想要三元中的中间实体

c# - 数据未加载到网格中(此错误的发生不一致)

c# - 寻找类似于WPF轮播的图像旋转器

wpf - 如何在 MVVM View 模型中指定资源?

c# - 如何为每个客户管理不同的设置?