使用MVVM在Silverlight 3 DataGrid中编辑新添加的行

时间:2009-12-17 01:51:25

标签: c# silverlight silverlight-3.0 datagrid mvvm

我正在尝试将Silverlight 3.0 DataGrid与MVVM设计模式一起使用。我的页面有一个DataGrid和一个按钮,它使用命令(来自Composite Application Library)将项目添加到VM中的集合。这样可以正常工作,并显示和选择新项目。

我无法解决的问题是如何开始编辑行。我希望当用户单击“添加”按钮时,可以立即编辑新行,即焦点设置为DataGrid,而新行则处于编辑模式。

这是视图中的XAML:

<Grid x:Name="LayoutRoot">
    <StackPanel>
        <data:DataGrid ItemsSource="{Binding DataView}"/>
        <Button cmd:Click.Command="{Binding AddItemCommand}" Content="Add" />
    </StackPanel>
</Grid>

后面的代码有一行代码,用于创建VM的实例并设置视图的DataContext。

VM代码是:

public class VM 
{
    public List<TestData> UnderlyingData { get; set; }
    public PagedCollectionView DataView { get; set; }
    public ICommand AddItemCommand { get; set; }

    public VM()
    {
        AddItemCommand = new DelegateCommand<object>(o =>
            {
                DataView.AddNew();
            });

        UnderlyingData = new List<TestData>();
        UnderlyingData.Add(new TestData() { Value = "Test" });

        DataView = new PagedCollectionView(UnderlyingData);
    }
}

public class TestData
{
    public string Value { get; set; }

    public TestData()
    {
        Value = "<new>";
    }

    public override string ToString()
    {
        return Value.ToString();
    }
}

使用MVVM设计模式解决此问题的最佳方法是什么?

2 个答案:

答案 0 :(得分:1)

我遇到了同样的问题。我已经介绍了接口ISupportEditingState:

public interface ISupportEditingState
{
    EditingState EditingState { get; set; }
}

我的虚拟机实现了它。然后我写了这个行为来同步DataGrid和我的VM的编辑状态:

    public class SynchroniseDataGridEditingStateBehaviour : Behavior<DataGrid>
{
    public static readonly DependencyProperty EditingStateBindingProperty =
        DependencyProperty.Register("EditingStateBinding", typeof(ISupportEditingState),
        typeof(SynchroniseDataGridEditingStateBehaviour), new PropertyMetadata(OnEditingStateBindingPropertyChange));

    private bool _attached;
    private bool _changingEditingState;

    public ISupportEditingState EditingStateBinding
    {
        get { return (ISupportEditingState)GetValue(EditingStateBindingProperty); }
        set { SetValue(EditingStateBindingProperty, value); }
    }

    private static void OnEditingStateBindingPropertyChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var b = d as SynchroniseDataGridEditingStateBehaviour;
        if (b == null)
            return;

        var oldNotifyChanged = e.OldValue as INotifyPropertyChanged;
        if (oldNotifyChanged != null)
            oldNotifyChanged.PropertyChanged -= b.OnEditingStatePropertyChanged;

        var newNotifyChanged = e.NewValue as INotifyPropertyChanged;
        if (newNotifyChanged != null)
            newNotifyChanged.PropertyChanged += b.OnEditingStatePropertyChanged;

        var newEditingStateSource = e.NewValue as ISupportEditingState;
        if (newEditingStateSource.EditingState == EditingState.Editing)
        {
            // todo: mh: decide on this behaviour once again.
            // maybe it's better to start editing if selected item is already bound in the DataGrid
            newEditingStateSource.EditingState = EditingState.LastCancelled;
        }
    }

    private static readonly string EditingStatePropertyName = 
        CodeUtils.GetPropertyNameByLambda<ISupportEditingState>(ses => ses.EditingState);

    private void OnEditingStatePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (_changingEditingState || !_attached || e.PropertyName != EditingStatePropertyName)
            return;

        _changingEditingState = true;

        var editingStateSource = sender as ISupportEditingState;
        if (editingStateSource == null)
            return;

        var grid = AssociatedObject;
        var editingState = editingStateSource.EditingState;
        switch (editingState)
        {
            case EditingState.Editing:
                grid.BeginEdit();
                break;
            case EditingState.LastCancelled:
                grid.CancelEdit();
                break;
            case EditingState.LastCommitted:
                grid.CommitEdit();
                break;
            default:
                throw new InvalidOperationException("Provided EditingState is not supported by the behaviour.");
        }

        _changingEditingState = false;
    }

    protected override void OnAttached()
    {
        var grid = AssociatedObject;
        grid.BeginningEdit += OnBeginningEdit;
        grid.RowEditEnded += OnEditEnded;
        _attached = true;
    }

    protected override void OnDetaching()
    {
        var grid = AssociatedObject;
        grid.BeginningEdit -= OnBeginningEdit;
        grid.RowEditEnded -= OnEditEnded;
        _attached = false;
    }

    void OnEditEnded(object sender, DataGridRowEditEndedEventArgs e)
    {
        if (_changingEditingState)
            return;

        EditingState editingState;
        if (e.EditAction == DataGridEditAction.Commit)
            editingState = EditingState.LastCommitted;
        else if (e.EditAction == DataGridEditAction.Cancel)
            editingState = EditingState.LastCancelled;
        else
            return; // if DataGridEditAction will ever be extended, this part must be changed
        EditingStateBinding.EditingState = editingState;
    }

    void OnBeginningEdit(object sender, DataGridBeginningEditEventArgs e)
    {
        if (_changingEditingState)
            return;
        EditingStateBinding.EditingState = EditingState.Editing;
    }
}

对我来说没问题,希望有所帮助。

答案 1 :(得分:0)

每当你谈到直接访问ui组件时,你都会忽略mvvm的观点。 ui绑定到viewmodel,因此找到一种改变viewmodel的方法。