【问题标题】:How to update the View from a ViewModel如何从 ViewModel 更新视图
【发布时间】:2016-01-14 21:20:57
【问题描述】:

我有一个视图模型ProductsViewModel,其中一种方法将一个新产品添加到它存储的ProductList。我目前有一个ListBox 绑定到ProductList。我通过将一个按钮绑定到一个简单的Command 添加一个新产品,该按钮调用视图模型上的相关方法。

当视图模型无法与视图“对话”时,如何修改视图以选择已添加到 ListBox 的新产品并向下滚动到新项目?

编辑

注意,我不希望每次将新项目添加到列表框时都自动选择最后一个项目,因为当我将项目导入列表框时会选择最后一个项目,我想避免。

【问题讨论】:

  • 你是否引发了 ViewModel 的 PropertyChanged 事件?
  • @AlyEl-Haddad 是的,虽然对于 hte 产品列表,我使用的是 ObservableCollection
  • mvvm how to make a list view auto scroll to a new Item in a list view 的可能重复项。正确的 MVVM 方法是通过该问题的已接受答案中所示的行为。
  • @MarkFeldman 不是重复的,因为它引用了自动滚动,我希望能够仅在某些点触发它。
  • @user3791372 好的,这与您提出的问题非常不同,请参阅下面的答案。

标签: c# xaml mvvm listbox


【解决方案1】:

在您的 ViewModel 'SelectedProduct' 中创建一个属性(显然它需要引发属性更改。将新产品添加到 ProductList 后,还要使用此新产品更新 SelectedProduct。 在 View 中,将 ListBox 的 SelectedItem 绑定到 CurrentProduct。

【讨论】:

  • 那么,viewmodel应该负责在listbox上设置当前选中的item?
  • 我想是的。据我所知,每个人都是这样做的。
  • 在 mvvm 方面,viewmodel 不应该知道 view。
  • 嗯,我不认为我们正在破坏 MVVM。让我们说这是一个糟糕的命名约定,而不是 SelectedProduct - CurrentProduct 怎么样。假设 VM 需要知道当前产品是什么,那么视图如何设置它是无关的(或未知的)VM 和视图之间的明显分离。这是在 VM 中设置 CurrentItem 以便对其进行编辑、将其保存到数据库等的常见做法。也许 VM 设置了 CurrentProduct,并且视图可以通过绑定对其做出反应。
【解决方案2】:

一般来说,实现这一目标的最佳方法是使用行为。实现可能取决于您的具体要求,但我将在此处提供一个通用示例,展示如何使视图模型触发 ListBox 滚动到您选择的特定项目。

首先,您需要一种将其从视图模型传递到视图的方法,您不能直接绑定到 XAML 中的事件,但您可以将事件封装在包装器中并绑定到它:

public class ListBoxScrollHandler
{
    public event Action<object> ScrollEvent;

    public void ScrollTo(object item)
    {
        if (this.ScrollEvent != null)
            this.ScrollEvent(item);
    }
}

该类包含我们的行为可以绑定到的事件和我们的视图模型可以调用的ScrollTo 方法。对于视图,让我们创建一个简单的列表框,我们将用数字(实际上是字符串)和一个按钮来填充它,该按钮将迫使我们滚动到内容为“500”的元素:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition />
    </Grid.RowDefinitions>

    <Button Content="Scroll to 500" HorizontalAlignment="Left" VerticalAlignment="Top" Command="{Binding ScrollCommand}" CommandParameter="500" />
    <ListBox Grid.Row="1" ItemsSource="{Binding MyItems}" SelectedItem="{Binding CurrentItem}" ScrollViewer.VerticalScrollBarVisibility="Visible">
        <i:Interaction.Behaviors>
            <behaviors:ListBoxScrollBehavior ScrollHandler="{Binding ScrollHandler}" />
        </i:Interaction.Behaviors>
    </ListBox>
</Grid>

如您所见,我已经使用 Blend 行为实现了这一点,如果您愿意,当然可以使用常规附加行为来实现它,但我在这里保持简单:

public class ListBoxScrollBehavior : Behavior<ListBox>
{
    public ListBoxScrollHandler ScrollHandler
    {
        get { return (ListBoxScrollHandler)GetValue(ScrollHandlerProperty); }
        set { SetValue(ScrollHandlerProperty, value); }
    }

    public static readonly DependencyProperty ScrollHandlerProperty =
        DependencyProperty.Register("ScrollHandler", typeof(ListBoxScrollHandler),
        typeof(ListBoxScrollBehavior), new PropertyMetadata(null, OnScrollHandlerChanged));

    protected override void OnAttached()
    {
        base.OnAttached();
    }

    protected override void OnDetaching()
    {
        base.OnDetaching();
    }

    private static void OnScrollHandlerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var behavior = d as ListBoxScrollBehavior;
        if (behavior == null)
            return;

        var oldHandler = e.OldValue as ListBoxScrollHandler;
        if (oldHandler != null)
            oldHandler.ScrollEvent -= behavior.ScrollTo;

        var newHandler = e.NewValue as ListBoxScrollHandler;
        if (newHandler != null)
            newHandler.ScrollEvent += behavior.ScrollTo;
    }

    public void ScrollTo(object item)
    {
        this.AssociatedObject.ScrollIntoView(item);
    }

}

所以我们的行为包含一个“ScrollHandler”依赖属性,我们可以绑定到我们的视图模型并通过调用列表框的 ScrollIntoView 方法来响应。之后,只需创建一个提供此属性的视图模型以及用于初始化列表项的代码和一个响应按钮按下并调用它的滚动处理程序的 ScrollTo 方法的命令处理程序:

public class MainViewModel : ViewModelBase
{
    private ObservableCollection<string> _MyItems = new ObservableCollection<string>();
    public ObservableCollection<string> MyItems
    {
        get { return this._MyItems; }
        set { this._MyItems = value; RaisePropertyChanged(); }
    }

    private string _SelectedItem;
    public string SelectedItem
    {
        get { return this._SelectedItem; }
        set { this._SelectedItem = value; RaisePropertyChanged(); }
    }

    public ICommand ScrollCommand { get { return new RelayCommand<string>(OnScroll); } }
    private void OnScroll(string item)
    {
        this.ScrollHandler.ScrollTo(item);
    }

    private ListBoxScrollHandler _ScrollHandler = new ListBoxScrollHandler();
    public ListBoxScrollHandler ScrollHandler
    {
        get { return this._ScrollHandler;}
        set { this._ScrollHandler = value; RaisePropertyChanged(); }
    }

    public MainViewModel()
    {
        for (int i = 0; i < 1000; i++)
            this.MyItems.Add(i.ToString());
    }
}

运行代码,单击按钮,列表框将向下滚动到包含“500”内容的元素。显然,如果您只需要此行为的一个子集(例如,滚动到当前选定的项目),那么您可以相应地修改此行为。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-28
    • 1970-01-01
    • 1970-01-01
    • 2011-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多