从 Silverlight ItemsControl 删除项目的模式

发布于 2024-12-12 02:19:41 字数 610 浏览 1 评论 0原文

我有一个包含 ItemsControl 的 Silverlight 页面。它看起来像这样

-- Name             Description          [Add]
-- Thing1           The first thing      [Edit] [Delete]
-- Thing2           The second thing     [Edit] [Delete]

,其中 [Edit][Delete][Add] 是按钮。

目前,我正在将控件绑定到 Thing 集合,并使用模板来显示属性,并绑定到 ViewModel 中的 Edit 命令。

对于 ThingViewModel 来说,拥有导致其删除自身的 Delete 命令是没有意义的(对我来说);

  • 看起来不干净
  • 该物体不知道它在集合中,因此无法将其自身从集合中删除

那么连接[Delete]按钮的最佳模式是什么?

I have a Silverlight page that contains an ItemsControl. It looks something like this

-- Name             Description          [Add]
-- Thing1           The first thing      [Edit] [Delete]
-- Thing2           The second thing     [Edit] [Delete]

where [Edit], [Delete], and [Add] are buttons.

Currently I'm binding the control to a collection of Thing and using a template to display the properties, and bind to an Edit Command in my ViewModel.

It doesn't make sense (to me) for the ThingViewModel to have a Delete Command which causes it to delete itself;

  • doesn't seem clean
  • the Thing doesn't know that it is in a collection so can't remove itself from the collection

So what's the best pattern to wire up the [Delete] button?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

南烟 2024-12-19 02:19:41

“删除”代码不会在集合中单个项目的 ViewModel 上运行,而是会(以某种方式)冒泡到包含集合的 ViewModel,并在那里进行处理。

伪代码:

public class ItemContainerViewModel
{
    List<ItemClass> Items { get; set; }

    public void DeleteItem(ItemClass item)
    {
        this.Items.Remove(item);
        NotifyOfPropertyChange(() => this.Items); // Causes controls bound to this list to refresh their contents
    }
}

冒泡事件的一种方法是让 ItemViewModel 知道它的父 ViewModel

public class ItemViewModel
{
    public ItemsCollectionViewModel ParentViewModel { get; private set; }
    public ItemViewModel(ItemsCollectionViewModel parentViewModel)
    {
        this.ParentView = parentViewModel;
    }

    public void Delete()
    {
        this.ParentViewModel.Delete(this);
    }
}

使用 MVVM 框架(如 Caliburn.Micro 或 MVVM-Lite)有更好的方法,但这至少让您开始了解如何考虑这些类型ViewModel 中的操作。

本质上 - 您的 ViewModel 应该能够执行所有用户操作,而不需要任何类型的视图。 (您应该能够运行一些测试代码,并且让您的虚拟机按预期工作,而无需绑定视图)

The "Delete" code will not be run on the ViewModel for the individual item, in the collection, rather it would be bubbled up (somehow) to the ViewModel that contains the collection, and processed there.

Pseudocode:

public class ItemContainerViewModel
{
    List<ItemClass> Items { get; set; }

    public void DeleteItem(ItemClass item)
    {
        this.Items.Remove(item);
        NotifyOfPropertyChange(() => this.Items); // Causes controls bound to this list to refresh their contents
    }
}

One way to bubble up the event is to have the ItemViewModel aware of it's parent ViewModel

public class ItemViewModel
{
    public ItemsCollectionViewModel ParentViewModel { get; private set; }
    public ItemViewModel(ItemsCollectionViewModel parentViewModel)
    {
        this.ParentView = parentViewModel;
    }

    public void Delete()
    {
        this.ParentViewModel.Delete(this);
    }
}

There are better ways with a MVVM framework like Caliburn.Micro or MVVM-Lite, but this at least gets you started on how to think about these kinds of operations in your ViewModel.

Essentialy - your ViewModel should be able to do all of your user operations without requiring a View of any kind. (You should be able to run some test code, and have your VM work as intended without a bound View)

夏见 2024-12-19 02:19:41

这是我想出的——以一种非常简化的形式。当 RelativeSource 不可用时,这是一个显而易见的选择。

  • 命名一个绑定到父/集合的控件
  • 在子/项目的绑定上指定 ElementName 属性

这适用于 ItemsControl 但我无法获得此模式还适用于 DataGrid。

<ItemsControl Name="MyParentControl"
              ItemsSource="{Binding Things}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button Content="Delete" 
                    Command="{Binding ElementName=MyParentControl, 
                        Path=DataContext.DeleteCommand}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Here's what I've come up with - in a very simplified form. It's an obvious choice when RelativeSource is not available.

  • Name a control which binds to the parent / collection
  • Specify the ElementName property on the binding for the child / item

This works for an ItemsControl but I haven't been able to get this pattern to work for a DataGrid yet.

<ItemsControl Name="MyParentControl"
              ItemsSource="{Binding Things}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Button Content="Delete" 
                    Command="{Binding ElementName=MyParentControl, 
                        Path=DataContext.DeleteCommand}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
滥情稳全场 2024-12-19 02:19:41

如果您使用列表框,以便用户可以选择一行,然后将命令放入包含该集合的虚拟机中,您将获得更清晰的实现。

这是带有删除按钮的行的 xaml。诀窍是使用“RelativeSource”

    <ListBox ItemsSource="{Binding MyItems, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding MySelectedItem, UpdateSourceTrigger=PropertyChanged}" >
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                            <StackPanel Orientation="Horizontal"   >
                                <Label Content="{Binding}" />
                                <Button Content="Delete" Command="{Binding DataContext.MyDeleteCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" />
                            </StackPanel >
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>

和ViewModel代码

public DelegateCommand<object> MyDeleteCommand { get; set; }

protected void DoDelete(object o)
{
    MyItems.Remove(MySelectedItem);
}

protected bool CanDoDelete(object o)
{
    return MySelectedItem != null;
}

string _mySelectedItem;
public string MySelectedItem
{
    get { return _mySelectedItem; }
    set
    {
        _mySelectedItem = value;
        OnPropertyChanged("MySelectedItem");
        MyDeleteCommand.RaiseCanExecuteChanged();
    }
}

ObservableCollection<string> _myItems;
public ObservableCollection<string> MyItems
{
    get { return _myItems; }
    set 
    { 
        _myItems = value;
        OnPropertyChanged("MyItems");
    }
}

将按钮绑定到父项DeleteCommand ,哦,我刚刚看到上面关于silverlight和relativeSource的评论。我用 WPF 做了这个并测试了它,它有效,它可能不适用于 silverlight

If you use a listbox so that the user can select a row and then put your commands in the VM that contains the collection, you get a cleaner implementation.

here is the xaml for a row with a delete button. The trick is to bind the button to the parent items DeleteCommand by using "RelativeSource"

    <ListBox ItemsSource="{Binding MyItems, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding MySelectedItem, UpdateSourceTrigger=PropertyChanged}" >
        <ListBox.ItemContainerStyle>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                            <StackPanel Orientation="Horizontal"   >
                                <Label Content="{Binding}" />
                                <Button Content="Delete" Command="{Binding DataContext.MyDeleteCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" />
                            </StackPanel >
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ListBox.ItemContainerStyle>
    </ListBox>

And the ViewModel code

public DelegateCommand<object> MyDeleteCommand { get; set; }

protected void DoDelete(object o)
{
    MyItems.Remove(MySelectedItem);
}

protected bool CanDoDelete(object o)
{
    return MySelectedItem != null;
}

string _mySelectedItem;
public string MySelectedItem
{
    get { return _mySelectedItem; }
    set
    {
        _mySelectedItem = value;
        OnPropertyChanged("MySelectedItem");
        MyDeleteCommand.RaiseCanExecuteChanged();
    }
}

ObservableCollection<string> _myItems;
public ObservableCollection<string> MyItems
{
    get { return _myItems; }
    set 
    { 
        _myItems = value;
        OnPropertyChanged("MyItems");
    }
}

oh, i just saw the above comment about silverlight and relativeSource. I did this with WPF and tested it out, which worked, it may not work for silverlight

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文