0

首次尝试在业务线项目上实现 MVVM 模式。我遇到了一些问题,我认为像这样的问题有更简单的答案:

原型窗口是项目列表的基本主从视图。(一个 Person 对象列表)。该视图包含主列表的 Infragistics xamDataGrid。当项目在网格中被选中时,您可以在下面的详细信息面板中编辑详细信息,并且当您在详细信息面板中的字段上按标签时,更新会在网格数据中“实时”显示。唯一的问题是我不想要“presto”,我想要“等到我按下‘应用更改’按钮”。

我希望避免创建一个单独的列表实例,以将主列表与我在详细信息面板中添加/删除/修改的工作组分开。

我走过的路:

我覆盖了网格字段中的 CellValuePresenter 样式,因此我可以将绑定设置为“OneWay”。这会阻止实时更新。

<ControlTemplate TargetType="{x:Type igDP:CellValuePresenter}">
  <ControlTemplate.Resources>
    <Style TargetType="TextBlock">
      <Setter Property="Background" Value="{Binding Path=DataItem.NameUIProperty.IsDirty, Converter={StaticResource BooleanBrushConverter}}" />
      <Setter Property="IsEnabled" Value="{Binding Path=DataItem.NameUIProperty.IsEditable}" />
    </Style>
  </ControlTemplate.Resources>
  <ContentControl>
    <TextBlock Text="{Binding Path=DataItem.Name, Mode=OneTime}" />
  </ContentControl>                                                     
</ControlTemplate>      

然后我将“ApplyUpdates”命令 (RelayCommand) 添加到我的 PersonListViewModel。这会引发“PERSON _ITEM_UPDATED”消息。我正在使用 MVVM Foundation Messenger 和 RelayCommand 类的 VB 端口。

#Region "ApplyUpdates Command"

Private mApplyUpdatesCommand As New RelayCommand(AddressOf ApplyUpdates)
Public ReadOnly Property ApplyUpdatesCommand() As ICommand
    Get
        Return mApplyUpdatesCommand
    End Get
End Property

Private Sub ApplyUpdates()
    'the changes are already in the object in the list so we don't have to do anything here except fire off the Applied message
    Messages.AppMessenger.NotifyColleagues(Messages.PERSON_ITEM_UPDATED)
End Sub

#End Region

PersonView 注册 PERSON _ITEM_UPDATED 消息并在收到消息时重新绑定网格。

'In Loaded Event

'register for window messages we care about
Messages.AppMessenger.Register(Messages.PERSON_ITEM_UPDATED, AddressOf OnPersonItemUpdated)

'EventHandler
Private Sub OnPersonItemUpdated()
  PersonGrid.DataSource = Nothing
  PersonGrid.DataSource = mViewModel.List
End Sub

所以,这行得通,但它闻起来不对。视图中似乎有太多的逻辑,而 ViewModel 并没有规定 UI 的状态,而是视图。

我错过了什么?您将使用什么方法让 ViewModel 延迟将更改发布到视图?

更新:我现在正在为网格创建一个自定义 ViewModel(只读,没有 Propertychanged 通知)和一个用于详细信息区域的可编辑 ViewModel。两个 VM 将包装相同的业务对象,但 ReadOnly 版本不会发布更改。这将使 VM 控制视图何时更新。

4

2 回答 2

0

声明基础数据网格的字段布局时,可以使用 UnboundField 代替 Field。此类公开底层绑定的 BindingPath 和 BindingMode 属性。使用这种技术,您可以摆脱实时更新,并且不需要自定义控件模板。

我对将逻辑移至 VM 的想法:

创建网格的 DataSource 和 nViewModel.List 的单向绑定。然后 ApplyChanges 可以调用:BindingOperations.GetBindingExpressionBase(dependencyObject, dependencyProperty).UpdateTarget();来强制刷新目标属性 DataSource。不幸的是,这会将您的 VM 绑定到绑定,但会导致您的视图中没有代码。

这里的一个大问题是,如果您有这种延迟绑定场景,ApplyChanges 确实需要一些 IoC 到 View 中,因为只有 View 知道如何真正进行更新(无论是使用 Bindings 还是其他)。最后,链上的东西将管理列表的两个实例:视图中的实例和 VM 中的实际实例。在这种特殊情况下,延迟更新似乎是 View 的一种行为。但是,VM 上的 UpdateChanges 命令实际上将该行为耦合到 VM,在这种情况下,我认为将两个列表实例存储在 VM 中是有意义的。

希望这可以帮助。

于 2009-11-30T18:31:01.810 回答
0

我在 MVVM 中实现选项对话框时遇到了类似的问题。您希望能够让用户编辑您的 ViewModel 的属性,但仅在他们点击应用时提交更改。我找到了一个合理的解决方案。这是最简单的选项板的代码,只有一个布尔属性“声音”:

class PinBallOptionsPad : AbstractOptionsPad
{
    public PinBallOptionsPad()
    {
        Name = "PinBallOptionsPad";
    }

    public override void Commit()
    {
        base.Commit();
        Properties.Settings.Default.Save();
    }

    #region "Sound"

    public bool SoundEdit
    {
        get
        {
            return m_SoundEdit;
        }
        set
        {
            if (m_SoundEdit != value)
            {
                m_SoundEdit = value;
                CommitActions.Add(
                    () => Properties.Settings.Default.Sound = m_SoundEdit);
                CancelActions.Add(
                    () =>
                    {
                        m_SoundEdit = Properties.Settings.Default.Sound;
                        NotifyPropertyChanged(m_SoundEditArgs);
                        NotifyPropertyChanged(m_SoundArgs);
                    });
                NotifyOptionChanged();
                NotifyPropertyChanged(m_SoundEditArgs);
            }
        }
    }
    private bool m_SoundEdit = Properties.Settings.Default.Sound;
    static readonly PropertyChangedEventArgs m_SoundEditArgs =
        NotifyPropertyChangedHelper.CreateArgs<PinBallOptionsPad>(o => o.SoundEdit);

    public bool Sound
    {
        get
        {
            return Properties.Settings.Default.Sound;
        }
    }
    static readonly PropertyChangedEventArgs m_SoundArgs =
        NotifyPropertyChangedHelper.CreateArgs<PinBallOptionsPad>(o => o.Sound);
    #endregion

}

一个属性看起来很多,但好处是整个属性的所有内容都包含在 Sound 区域中。因此,如果您复制并粘贴它,并进行搜索和替换,您可以相对快速地创建新属性。为了了解 CommitActions 和 CancelActions 的工作原理,您还需要AbstractOptionsPad类:

public abstract class AbstractOptionsPad : AbstractPad, IOptionsPad
{
    #region " Commit "

    /// <summary>
    /// If overriding this method, make sure to call base.Commit first.
    /// </summary>
    public virtual void Commit()
    {
        foreach (var commitAction in CommitActions)
        {
            commitAction();
        }
        CommitActions.Clear();
        CancelActions.Clear();
    }

    protected IList<Action> CommitActions
    {
        get
        {
            return m_commitActions;
        }
    }
    private readonly IList<Action> m_commitActions = new List<Action>();

    #endregion

    #region " Cancel "

    /// <summary>
    /// If overriding this method, make sure to call base.Cancel first.
    /// </summary>
    public virtual void Cancel()
    {
        foreach (var cancelAction in CancelActions)
        {
            cancelAction();
        }
        CancelActions.Clear();
        CommitActions.Clear();
    }

    protected IList<Action> CancelActions
    {
        get
        {
            return m_cancelActions;
        }
    }
    private readonly IList<Action> m_cancelActions = new List<Action>();

    #endregion

    public event EventHandler OptionChanged;

    protected void NotifyOptionChanged()
    {
        var evt = OptionChanged;
        if (evt != null)
        {
            evt(this, new EventArgs());
        }
    }
}

这是此垫的视图:

<DataTemplate DataType="{x:Type local:PinBallOptionsPad}">
    <CheckBox IsChecked="{Binding SoundEdit}">
        <TextBlock Text="Sound"/>
    </CheckBox> 
</DataTemplate>

所以它在选项中绑定到 SoundEdit,但应用程序的其余部分可以绑定到 Sound 属性,并根据 NotifyPropertyChanged 事件进行更新。

于 2009-12-05T02:27:09.263 回答